From: <cr...@us...> - 2008-11-16 02:53:12
|
Revision: 4703 http://jnode.svn.sourceforge.net/jnode/?rev=4703&view=rev Author: crawley Date: 2008-11-16 02:53:07 +0000 (Sun, 16 Nov 2008) Log Message: ----------- Overhaul the help subsystem to make it easier to hook in ESC help, and support anticipated future developments. This also addresses a previously unnoticed problem where HelpCommand triggered class loading for all known aliases, and improves diagnostics for class load failure. This checkin will probably break 'old' syntax command completion (see issue #2802) but AFAIK, there are no extant commands using the 'old' syntax mechanism. Modified Paths: -------------- trunk/distr/src/emu/org/jnode/emu/Emu.java trunk/shell/src/shell/org/jnode/shell/CommandLine.java trunk/shell/src/shell/org/jnode/shell/CommandRunner.java trunk/shell/src/shell/org/jnode/shell/CommandShell.java trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/command/HelpCommand.java trunk/shell/src/shell/org/jnode/shell/help/Argument.java trunk/shell/src/shell/org/jnode/shell/help/CommandLineElement.java trunk/shell/src/shell/org/jnode/shell/help/Help.java trunk/shell/src/shell/org/jnode/shell/help/Parameter.java trunk/shell/src/shell/org/jnode/shell/help/argument/EnumOptionArgument.java trunk/shell/src/shell/org/jnode/shell/help/argument/OptionArgument.java trunk/shell/src/shell/org/jnode/shell/help/def/SystemHelpPlugin.java trunk/shell/src/test/org/jnode/test/shell/Cassowary.java trunk/shell/src/test/org/jnode/test/shell/help/DefaultHelpTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/AlternativesSyntaxTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/OptionSyntaxTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/PowersetSyntaxTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/RepeatedSyntaxTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/SequenceSyntaxTest.java Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/help/EnhancedHelp.java trunk/shell/src/shell/org/jnode/shell/help/HelpFactory.java trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelpFactory.java trunk/shell/src/shell/org/jnode/shell/help/def/NewSyntaxHelp.java trunk/shell/src/shell/org/jnode/shell/help/def/OldSyntaxHelp.java trunk/shell/src/shell/org/jnode/shell/help/def/TextHelpBase.java Removed Paths: ------------- trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelp.java Modified: trunk/distr/src/emu/org/jnode/emu/Emu.java =================================================================== --- trunk/distr/src/emu/org/jnode/emu/Emu.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/distr/src/emu/org/jnode/emu/Emu.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -19,8 +19,8 @@ import org.jnode.shell.alias.AliasManager; import org.jnode.shell.alias.def.DefaultAliasManager; import org.jnode.shell.def.DefaultShellManager; -import org.jnode.shell.help.Help; -import org.jnode.shell.help.def.DefaultHelp; +import org.jnode.shell.help.HelpFactory; +import org.jnode.shell.help.def.DefaultHelpFactory; import org.jnode.shell.syntax.DefaultSyntaxManager; import org.jnode.shell.syntax.SyntaxBundle; import org.jnode.shell.syntax.SyntaxManager; @@ -93,7 +93,7 @@ InitialNaming.bind(AliasManager.NAME, aliasMgr); InitialNaming.bind(ShellManager.NAME, new DefaultShellManager()); InitialNaming.bind(SyntaxManager.NAME, syntaxMgr); - InitialNaming.bind(Help.NAME, new DefaultHelp()); + InitialNaming.bind(HelpFactory.NAME, new DefaultHelpFactory()); } catch (NamingException ex) { throw new EmuException("Problem setting up InitialNaming bindings", ex); } Modified: trunk/shell/src/shell/org/jnode/shell/CommandLine.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandLine.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/CommandLine.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -27,6 +27,7 @@ import org.jnode.shell.help.CompletionException; import org.jnode.shell.help.Help; import org.jnode.shell.help.HelpException; +import org.jnode.shell.help.HelpFactory; import org.jnode.shell.help.Parameter; import org.jnode.shell.io.CommandIO; import org.jnode.shell.io.CommandIOMarker; @@ -643,7 +644,7 @@ } else { // Otherwise, try old-style completion using the command's INFO try { - Help.Info info = Help.getInfo(cmdClass.getCommandClass()); + Help.Info info = HelpFactory.getInfo(cmdClass.getCommandClass()); info.complete(completion, this, shell.getOut()); } catch (HelpException ex) { // And fall back to old-style completion with an 'info' that Modified: trunk/shell/src/shell/org/jnode/shell/CommandRunner.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandRunner.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/CommandRunner.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -30,7 +30,7 @@ import java.security.AccessController; import java.security.PrivilegedActionException; -import org.jnode.shell.help.Help; +import org.jnode.shell.help.HelpFactory; import org.jnode.shell.help.HelpException; import org.jnode.shell.help.SyntaxErrorException; import org.jnode.shell.io.CommandIO; @@ -144,7 +144,7 @@ } } catch (SyntaxErrorException ex) { try { - Help.getInfo(cmdInfo.getCommandClass()).usage(shellErr); + HelpFactory.getInfo(cmdInfo.getCommandClass()).usage(shellErr); shellErr.println(ex.getMessage()); } catch (HelpException e) { shellErr.println("Exception while trying to get the command usage"); Modified: trunk/shell/src/shell/org/jnode/shell/CommandShell.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -55,7 +55,6 @@ import org.jnode.naming.InitialNaming; import org.jnode.shell.alias.AliasManager; import org.jnode.shell.alias.NoSuchAliasException; -import org.jnode.shell.help.CompletionException; import org.jnode.shell.io.CommandIO; import org.jnode.shell.io.CommandInput; import org.jnode.shell.io.CommandInputOutput; Modified: trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -30,7 +30,7 @@ import java.security.PrivilegedAction; import java.security.PrivilegedActionException; -import org.jnode.shell.help.Help; +import org.jnode.shell.help.HelpFactory; import org.jnode.shell.help.SyntaxErrorException; import org.jnode.shell.io.CommandIO; import org.jnode.vm.VmExit; @@ -117,7 +117,7 @@ } } } catch (SyntaxErrorException ex) { - Help.getInfo(cmdInfo.getCommandClass()).usage(err); + HelpFactory.getInfo(cmdInfo.getCommandClass()).usage(err); err.println(ex.getMessage()); } catch (VmExit ex) { return ex.getStatus(); Modified: trunk/shell/src/shell/org/jnode/shell/command/HelpCommand.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/command/HelpCommand.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/command/HelpCommand.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -26,18 +26,17 @@ import javax.naming.NameNotFoundException; import org.jnode.shell.AbstractCommand; +import org.jnode.shell.CommandInfo; import org.jnode.shell.CommandLine; -import org.jnode.shell.Shell; +import org.jnode.shell.CommandShell; import org.jnode.shell.ShellUtils; import org.jnode.shell.alias.AliasManager; import org.jnode.shell.alias.NoSuchAliasException; import org.jnode.shell.help.Help; +import org.jnode.shell.help.HelpFactory; import org.jnode.shell.help.HelpException; import org.jnode.shell.syntax.AliasArgument; import org.jnode.shell.syntax.Argument; -import org.jnode.shell.syntax.ArgumentBundle; -import org.jnode.shell.syntax.SyntaxBundle; -import org.jnode.shell.syntax.SyntaxManager; /** * @author qades @@ -59,7 +58,9 @@ } @Override - public void execute() throws Exception { + public void execute() throws NameNotFoundException, ClassNotFoundException, NoSuchAliasException { + // The above exceptions are either bugs or configuration errors and should be allowed + // to propagate so that the shell can diagnose them appropriately. String alias; CommandLine commandLine = getCommandLine(); PrintWriter out = getOutput().getPrintWriter(); @@ -71,108 +72,53 @@ } else { alias = "help"; } - - Help.Info info = null; - SyntaxBundle syntaxes = null; - ArgumentBundle bundle = null; - String otherAliases = null; + CommandShell shell = null; try { - final Shell shell = ShellUtils.getShellManager().getCurrentShell(); - final AliasManager aliasManager = shell.getAliasManager(); - final SyntaxManager syntaxManager = shell.getSyntaxManager(); - Class<?> clazz = getCommandClass(aliasManager, alias); - bundle = getBundle(clazz, err); - if (bundle != null) { - syntaxes = syntaxManager.getSyntaxBundle(alias); - if (syntaxes == null) { - syntaxes = new SyntaxBundle(alias, bundle.createDefaultSyntax()); - } - } else { - info = Help.getInfo(clazz); - } - if (info != null || syntaxes != null) { - otherAliases = getOtherAliases(aliasManager, alias, clazz); - } - } catch (ClassNotFoundException ex) { - err.println("Alias not found: " + alias); - exit(1); - } catch (SecurityException ex) { - err.println("Access to class prevented by security manager"); - exit(2); - } catch (NameNotFoundException ex) { - err.println("Can't find the shell manager"); - exit(2); + shell = (CommandShell) ShellUtils.getShellManager().getCurrentShell(); + CommandInfo cmdInfo = shell.getCommandInfo(alias); + Help cmdHelp = HelpFactory.getHelpFactory().getHelp(alias, shell.getCommandInfo(alias)); + cmdHelp.help(out); + otherAliases(shell.getAliasManager(), alias, cmdInfo.getCommandClass().getName(), out); } catch (HelpException ex) { - err.println("No help information available for alias " + alias); + err.println("No help information is available for alias / class '" + alias + "'"); exit(1); + } catch (ClassNotFoundException ex) { + try { + String className = shell.getAliasManager().getAliasClassName(alias); + err.println("Cannot load class '" + className + "' for alias '" + alias + "'"); + } catch (NoSuchAliasException ex2) { + err.println("'" + alias + "' is neither an alias or a loadable class name"); + } + throw ex; + } catch (SecurityException ex) { + err.println("Security exception while loading the class associated with alias '" + alias + "'"); + err.println("Reported reason: " + ex.getLocalizedMessage()); + throw ex; } - - if (syntaxes != null) { - Help.getHelp().help(syntaxes, bundle, out); - } else if (info != null) { - Help.getHelp().help(info, alias, out); - } else { - out.println("No help information available: " + alias); - } - if (otherAliases != null) { - out.println(otherAliases); - } } - private ArgumentBundle getBundle(Class<?> clazz, PrintWriter err) { - try { - AbstractCommand command = (AbstractCommand) clazz.newInstance(); - return command.getArgumentBundle(); - } catch (ClassCastException e) { - // The target class cannot - } catch (InstantiationException e) { - err.println("Problem during instantiation of " + clazz.getName()); - exit(2); - } catch (IllegalAccessException e) { - err.println("Constructor for " + clazz.getName() + " is not accessible"); - exit(2); - } - return null; - } + private void otherAliases(AliasManager aliasManager, String thisAlias, + String aliasClass, PrintWriter out) throws NoSuchAliasException { + // NoSuchAliasException indicates a bug, and should be allowed to propagate. + StringBuilder sb = new StringBuilder(); - private Class<?> getCommandClass(AliasManager aliasManager, String commandName) - throws ClassNotFoundException { - try { - return aliasManager.getAliasClass(commandName); - } catch (NoSuchAliasException ex) { - // Not an alias -> assuming it's a class name - return Class.forName(commandName); - } - } - - private String getOtherAliases(AliasManager aliasManager, String alias, Class<?> aliasClass) { - boolean hasOtherAlias = false; - StringBuilder sb = new StringBuilder("Other aliases: "); - boolean first = true; - for (String otherAlias : aliasManager.aliases()) { - // exclude alias from the returned list - if (!otherAlias.equals(alias)) { - try { - Class<?> otherAliasClass = aliasManager.getAliasClass(otherAlias); - - if (aliasClass.equals(otherAliasClass)) { - // we have found another alias for the same command - hasOtherAlias = true; - if (!first) { - sb.append(","); - } - sb.append(otherAlias); - first = false; + // exclude thisAlias from the output + if (!otherAlias.equals(thisAlias)) { + String otherAliasClass = aliasManager.getAliasClassName(otherAlias); + // System.err.println("comparing '" + aliasClass + "' with '" + otherAliasClass + "'"); + if (aliasClass.equals(otherAliasClass)) { + // we have found another alias for the same command + if (sb.length() > 0) { + sb.append(", "); } - } catch (NoSuchAliasException nsae) { - // should never happen since we iterate on known aliases - } catch (ClassNotFoundException e) { - // should never happen since we iterate on known aliases + sb.append(otherAlias); } } } - return hasOtherAlias ? sb.toString() : null; + if (sb.length() > 0) { + out.println("Other aliases: " + sb); + } } } Modified: trunk/shell/src/shell/org/jnode/shell/help/Argument.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/Argument.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/help/Argument.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -55,7 +55,7 @@ return "<" + getName() + ">" + (isMulti() ? " ..." : ""); } - public void describe(Help help, PrintWriter out) { + public void describe(HelpFactory help, PrintWriter out) { help.describeArgument(this, out); } Modified: trunk/shell/src/shell/org/jnode/shell/help/CommandLineElement.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/CommandLineElement.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/help/CommandLineElement.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -46,7 +46,7 @@ } public abstract String format(); - public abstract void describe(Help help, PrintWriter out); + public abstract void describe(HelpFactory help, PrintWriter out); public abstract void complete(CompletionInfo completion, String partial); /** Added: trunk/shell/src/shell/org/jnode/shell/help/EnhancedHelp.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/EnhancedHelp.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/help/EnhancedHelp.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -0,0 +1,59 @@ +/* + * $Id: CommandLineElement.java 4556 2008-09-13 08:02:20Z crawley $ + * + * JNode.org + * Copyright (C) 2003-2006 JNode.org + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; If not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + package org.jnode.shell.help; + +import java.io.PrintWriter; + +/** + * This enhanced help interface provides extra fine-grained help + * methods. + * + * @author cr...@jn... + */ +public interface EnhancedHelp extends Help { + /** + * Output the description (summary) text for the command. + * + * @param pw the help information is written here + */ + void description(PrintWriter pw); + + /** + * Output the argument descriptions for the command. + * + * @param pw the help information is written here + */ + public void arguments(PrintWriter pw); + + /** + * Output the option descriptions for the command. + * + * @param pw the help information is written here + */ + public void options(PrintWriter pw); + + /** + * Output the detailed help text for the command. + * + * @param pw the help information is written here + */ + public void details(PrintWriter pw); +} Modified: trunk/shell/src/shell/org/jnode/shell/help/Help.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/Help.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/help/Help.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -18,118 +18,41 @@ * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - package org.jnode.shell.help; import java.io.PrintWriter; -import java.lang.reflect.Field; -import java.util.TreeSet; -import javax.naming.NamingException; - import org.jnode.driver.console.CompletionInfo; -import org.jnode.naming.InitialNaming; -import org.jnode.plugin.PluginUtils; import org.jnode.shell.CommandLine; -import org.jnode.shell.syntax.ArgumentBundle; -import org.jnode.shell.syntax.FlagArgument; -import org.jnode.shell.syntax.SyntaxBundle; /** - * @author qades - * @author Fabien DUMINY (fd...@jn...) + * This is the interface for an object that outputs command help. Different + * implementations support different command syntax mechanisms, and (in the + * future) will provide help in different output formats; e.g. plain text, + * HTML and so on. + * + * @author cr...@jn... */ -public abstract class Help { - public static final String BUNDLE_NAME = "messages"; // must be in our package - - public static final Class<Help> NAME = Help.class; - - public static final String INFO_FIELD_NAME = "HELP_INFO"; - - public static Help getHelp() throws HelpException { - try { - return InitialNaming.lookup(NAME); - } catch (NamingException ex) { - throw new HelpException("Help application not found"); - } - } - - public static String getLocalizedHelp(String messageKey) { - return PluginUtils.getLocalizedMessage(Help.class, - BUNDLE_NAME, messageKey); - } - - public static Info getInfo(Class<?> clazz) throws HelpException { - try { - Field helpInfo = clazz.getField(INFO_FIELD_NAME); - return (Help.Info) helpInfo.get(null); // static access - } catch (NoSuchFieldException ex) { - throw new HelpException("Command information not found"); - } catch (IllegalAccessException ex) { - throw new HelpException("Command information not accessible"); - } - } - +public interface Help { + /** - * Shows the help page for a command + * Output complete help for the command. * - * @param info the command info - * @param command a command name or alias which appears in the help - * @param out the destination for help output. + * @param pw the help information is written here */ - public abstract void help(Info info, String command, PrintWriter out); - + public void help(PrintWriter pw); + /** - * Shows the help page for a command + * Output the usage message(s) for the command. * - * @param syntaxes the command's syntax bundle - * @param bundle the command's argument bundle - * @param out the destination for help output. + * @param pw the help information is written here */ - public abstract void help(SyntaxBundle syntaxes, ArgumentBundle bundle, PrintWriter out); - + public void usage(PrintWriter pw); + /** - * Shows the usage line for a command - * - * @param info the command information - * @param out the destination for help output. + * This class is here for historical reasons. It is a key API class in the + * 'old' JNode syntax mechanism. */ - public abstract void usage(Info info, PrintWriter out); - - /** - * Shows the usage line for a command - * - * @param syntaxes the command's syntax bundle - * @param bundle the command's argument bundle - * @param out the destination for help output. - */ - public abstract void usage(SyntaxBundle syntaxes, ArgumentBundle bundle, PrintWriter out); - - /** - * Shows the description of a single argument. Used as a callback in - * {@link Argument#describe(Help)}. - */ - public abstract void describeArgument(Argument arg, PrintWriter out); - - /** - * Shows the description of a single argument. Used as a callback in - * {@link Argument#describe(Help)}. - */ - public abstract void describeArgument(org.jnode.shell.syntax.Argument<?> arg, PrintWriter out); - - /** - * Shows the description of a single FlagArgument. Used as a callback in - * {@link Argument#describe(Help)}. - */ - public abstract void describeOption(FlagArgument arg, - TreeSet<String> flagTokens, PrintWriter out); - - /** - * Shows the description of a single parameter. Used as a callback in - * {@link Parameter#describe(Help)}. - */ - public abstract void describeParameter(Parameter param, PrintWriter out); - public static class Info { private final String name; @@ -161,7 +84,7 @@ public void usage(PrintWriter out) { try { - Help.getHelp().usage(this, out); + HelpFactory.getHelpFactory().usage(this, out); } catch (HelpException ex) { ex.printStackTrace(); } @@ -173,7 +96,7 @@ * @throws HelpException */ public void help(String command, PrintWriter out) throws HelpException { - Help.getHelp().help(this, command, out); + HelpFactory.getHelpFactory().help(this, command, out); } public String complete(CompletionInfo completion, CommandLine partial, Added: trunk/shell/src/shell/org/jnode/shell/help/HelpFactory.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/HelpFactory.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/help/HelpFactory.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -0,0 +1,152 @@ +/* + * $Id: Help.java 4556 2008-09-13 08:02:20Z crawley $ + * + * JNode.org + * Copyright (C) 2003-2006 JNode.org + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; If not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.jnode.shell.help; + +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.util.TreeSet; + +import javax.naming.NamingException; + +import org.jnode.naming.InitialNaming; +import org.jnode.plugin.PluginUtils; +import org.jnode.shell.CommandInfo; +import org.jnode.shell.help.Help.Info; +import org.jnode.shell.syntax.ArgumentBundle; +import org.jnode.shell.syntax.FlagArgument; +import org.jnode.shell.syntax.SyntaxBundle; + +/** + * The HelpFactory class is the base class for the Help factory classes, and provides + * a static method for getting the default factory. Other methods in this class are + * here for historical reasons, and should be avoided where possible. + * + * @author qades + * @author Fabien DUMINY (fd...@jn...) + * @author cr...@jn... + */ +public abstract class HelpFactory { + public static final String BUNDLE_NAME = "messages"; // must be in our package + + public static final Class<HelpFactory> NAME = HelpFactory.class; + + public static final String INFO_FIELD_NAME = "HELP_INFO"; + + public static HelpFactory getHelpFactory() throws HelpException { + try { + return InitialNaming.lookup(NAME); + } catch (NamingException ex) { + throw new HelpException("Help factory not found"); + } + } + + public static String getLocalizedHelp(String messageKey) { + return PluginUtils.getLocalizedMessage(HelpFactory.class, + BUNDLE_NAME, messageKey); + } + + public static Info getInfo(Class<?> clazz) throws HelpException { + try { + Field helpInfo = clazz.getField(INFO_FIELD_NAME); + return (Help.Info) helpInfo.get(null); // static access + } catch (NoSuchFieldException ex) { + throw new HelpException("Command information not found"); + } catch (IllegalAccessException ex) { + throw new HelpException("Command information not accessible"); + } + } + + /** + * Obtain a CommanHelp object for a given command alias and its resolved CommandInfo. + * + * @param alias + * @param cmdInfo + * @return + * @throws HelpException + */ + public abstract Help getHelp( + String alias, CommandInfo cmdInfo) throws HelpException; + + // FIXME ... the remaining API methods are historical, and should not be used outside of + // the help package and its implementation packages. + + /** + * Shows the help page for a command + * + * @param info the command info + * @param command a command name or alias which appears in the help + * @param out the destination for help output. + */ + protected abstract void help(Info info, String command, PrintWriter out); + + /** + * Shows the help page for a command + * + * @param syntaxes the command's syntax bundle + * @param bundle the command's argument bundle + * @param out the destination for help output. + */ + protected abstract void help(SyntaxBundle syntaxes, ArgumentBundle bundle, PrintWriter out); + + /** + * Shows the usage line for a command + * + * @param info the command information + * @param out the destination for help output. + */ + protected abstract void usage(Info info, PrintWriter out); + + /** + * Shows the usage line for a command + * + * @param syntaxes the command's syntax bundle + * @param bundle the command's argument bundle + * @param out the destination for help output. + */ + protected abstract void usage(SyntaxBundle syntaxes, ArgumentBundle bundle, PrintWriter out); + + /** + * Shows the description of a single argument. Used as a callback in + * {@link Argument#describe(HelpFactory)}. + */ + protected abstract void describeArgument(Argument arg, PrintWriter out); + + /** + * Shows the description of a single argument. Used as a callback in + * {@link Argument#describe(HelpFactory)}. + */ + protected abstract void describeArgument(org.jnode.shell.syntax.Argument<?> arg, PrintWriter out); + + /** + * Shows the description of a single FlagArgument. Used as a callback in + * {@link Argument#describe(HelpFactory)}. + */ + protected abstract void describeOption(FlagArgument arg, + TreeSet<String> flagTokens, PrintWriter out); + + /** + * Shows the description of a single parameter. Used as a callback in + * {@link Parameter#describe(HelpFactory)}. + */ + protected abstract void describeParameter(Parameter param, PrintWriter out); + +} Modified: trunk/shell/src/shell/org/jnode/shell/help/Parameter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/Parameter.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/help/Parameter.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -105,7 +105,7 @@ return (optional ? "[" + result + "]" : result); } - public void describe(Help help, PrintWriter out) { + public void describe(HelpFactory help, PrintWriter out) { if (!isAnonymous()) { help.describeParameter(this, out); } Modified: trunk/shell/src/shell/org/jnode/shell/help/argument/EnumOptionArgument.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/argument/EnumOptionArgument.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/help/argument/EnumOptionArgument.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -26,7 +26,7 @@ import org.jnode.driver.console.CompletionInfo; import org.jnode.shell.help.Argument; -import org.jnode.shell.help.Help; +import org.jnode.shell.help.HelpFactory; import org.jnode.shell.help.Parameter; import org.jnode.shell.help.ParsedArguments; @@ -55,7 +55,7 @@ return result.substring(1); } - public void describe(Help help, PrintWriter out) { + public void describe(HelpFactory help, PrintWriter out) { for (EnumOption<T> option : options) option.describe(help, out); } Modified: trunk/shell/src/shell/org/jnode/shell/help/argument/OptionArgument.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/argument/OptionArgument.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/help/argument/OptionArgument.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -25,7 +25,7 @@ import org.jnode.driver.console.CompletionInfo; import org.jnode.shell.help.Argument; -import org.jnode.shell.help.Help; +import org.jnode.shell.help.HelpFactory; import org.jnode.shell.help.Parameter; /** @@ -54,7 +54,7 @@ } @Override - public void describe(Help help, PrintWriter out) { + public void describe(HelpFactory help, PrintWriter out) { for (Option option : options) option.describe(help, out); } Deleted: trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelp.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelp.java 2008-11-12 13:47:18 UTC (rev 4702) +++ trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelp.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -1,347 +0,0 @@ -/* - * $Id$ - * - * JNode.org - * Copyright (C) 2003-2006 JNode.org - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; If not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -package org.jnode.shell.help.def; - -import java.io.PrintWriter; -import java.util.HashMap; -import java.util.Map; -import java.util.TreeSet; - -import org.jnode.shell.help.Argument; -import org.jnode.shell.help.Help; -import org.jnode.shell.help.Parameter; -import org.jnode.shell.help.Syntax; -import org.jnode.shell.syntax.ArgumentBundle; -import org.jnode.shell.syntax.FlagArgument; -import org.jnode.shell.syntax.OptionSyntax; -import org.jnode.shell.syntax.SyntaxBundle; - -/** - * @author qades - * @author Fabien DUMINY (fd...@jn...) - * @author cr...@jn... - */ -public class DefaultHelp extends Help { - public static final String RESOURCE_NAME = "messages.properties"; - private static final int NOMINAL_WIDTH = 75; - /* start with 80 spaces ... */ - private static String spaces = - " "; - - /** - * Create a new instance - */ - public DefaultHelp() { - } - - /** - * Shows the complete help for a command. - * - * @see Help#help(org.jnode.shell.help.Help.Info, String) - */ - public void help(Info info, String command, PrintWriter out) { - final Syntax[] syntaxes = info.getSyntaxes(); - final String name = command == null ? info.getName() : command; - for (int i = 0; i < syntaxes.length; i++) { - help(name, syntaxes[i], out); - if (i < syntaxes.length) { - out.println(); - } - } - } - - @Override - public void help(SyntaxBundle syntaxes, ArgumentBundle bundle, PrintWriter out) { - usage(syntaxes, bundle, out); - if (bundle.getDescription() != null) { - out.println("\n" + Help.getLocalizedHelp("help.description") + ":"); - format(out, new Cell[]{new Cell(4, NOMINAL_WIDTH - 4)}, - new String[]{bundle.getDescription()}); - } - Map<String, TreeSet<String>> flagMap = buildFlagMap(syntaxes); - boolean first = true; - for (org.jnode.shell.syntax.Argument<?> arg : bundle) { - if (arg instanceof FlagArgument) { - if (first) { - out.println("\n" + Help.getLocalizedHelp("help.options") + ":"); - first = false; - } - describeOption((FlagArgument) arg, flagMap.get(arg.getLabel()), out); - } - } - first = true; - for (org.jnode.shell.syntax.Argument<?> arg : bundle) { - if (!(arg instanceof FlagArgument)) { - if (first) { - out.println("\n" + Help.getLocalizedHelp("help.parameters") + ":"); - first = false; - } - describeArgument(arg, out); - } - } - } - - private Map<String, TreeSet<String>> buildFlagMap(SyntaxBundle syntaxes) { - HashMap<String, TreeSet<String>> res = new HashMap<String, TreeSet<String>>(); - for (org.jnode.shell.syntax.Syntax syntax : syntaxes.getSyntaxes()) { - buildFlagMap(syntax, res); - } - return res; - } - - private void buildFlagMap(org.jnode.shell.syntax.Syntax syntax, - HashMap<String, TreeSet<String>> res) { - if (syntax instanceof OptionSyntax) { - OptionSyntax os = (OptionSyntax) syntax; - String key = os.getArgName(); - TreeSet<String> options = res.get(key); - if (options == null) { - options = new TreeSet<String>(); - res.put(key, options); - } - String shortOptName = os.getShortOptName(); - if (shortOptName != null) { - options.add(shortOptName); - } - String longOptName = os.getLongOptName(); - if (longOptName != null) { - options.add(longOptName); - } - } else { - for (org.jnode.shell.syntax.Syntax child : syntax.getChildren()) { - buildFlagMap(child, res); - } - } - } - - /** - * Shows the help for a command syntax. - */ - public void help(String name, Syntax syntax, PrintWriter out) { - usage(name, syntax, out); - if (syntax.getDescription() != null) { - out.println("\n" + Help.getLocalizedHelp("help.description") + ":"); - format(out, new Cell[]{new Cell(4, NOMINAL_WIDTH - 4)}, - new String[]{syntax.getDescription()}); - } - final Parameter[] params = syntax.getParams(); - if (params.length != 0) { - out.println("\n" + Help.getLocalizedHelp("help.parameters") + ":"); - for (int i = 0; i < params.length; i++) { - params[i].describe(this, out); - } - } - } - - /** - * Shows the usage information of a command. - */ - public void usage(Info info, PrintWriter out) { - final Syntax[] syntaxes = info.getSyntaxes(); - for (int i = 0; i < syntaxes.length; i++) { - usage(info.getName(), syntaxes[i], out); - } - } - - /** - * Shows the usage information of a command. - */ - public void usage(String name, Syntax syntax, PrintWriter out) { - StringBuilder line = new StringBuilder(name); - final Parameter[] params = syntax.getParams(); - for (int i = 0; i < params.length; i++) { - line.append(' ').append(params[i].format()); - } - out.println(Help.getLocalizedHelp("help.usage") + ": " + line); - } - - @Override - public void usage(SyntaxBundle syntaxBundle, ArgumentBundle bundle, PrintWriter out) { - String command = syntaxBundle.getAlias(); - String usageText = Help.getLocalizedHelp("help.usage") + ":"; - int usageLength = usageText.length(); - int commandLength = command.length(); - Cell[] cells = - new Cell[]{new Cell(0, usageLength), new Cell(1, commandLength), - new Cell(1, NOMINAL_WIDTH - 2 - usageLength - commandLength)}; - String[] texts = new String[]{usageText, command, null}; - String[] texts2 = new String[]{"", "", null}; - org.jnode.shell.syntax.Syntax[] syntaxes = syntaxBundle.getSyntaxes(); - if (syntaxes.length > 0) { - for (int i = 0; i < syntaxes.length; i++) { - if (i == 1) { - texts[0] = getSpaces(usageLength); - } - texts[2] = syntaxes[i].format(bundle); - format(out, cells, texts); - texts2[2] = syntaxes[i].getDescription(); - format(out, cells, texts2); - } - } else { - texts[2] = ""; - format(out, cells, texts); - } - } - - public void describeParameter(Parameter param, PrintWriter out) { - format(out, new Cell[]{new Cell(2, 18), new Cell(2, NOMINAL_WIDTH - 22)}, - new String[]{param.getName(), param.getDescription()}); - } - - public void describeArgument(Argument arg, PrintWriter out) { - format(out, new Cell[]{new Cell(4, 16), new Cell(2, NOMINAL_WIDTH - 22)}, - new String[]{arg.getName(), arg.getDescription()}); - } - - @Override - public void describeArgument(org.jnode.shell.syntax.Argument<?> arg, PrintWriter out) { - String description = "(" + arg.getTypeDescription() + ") " + arg.getDescription(); - format(out, new Cell[]{new Cell(4, 16), new Cell(2, NOMINAL_WIDTH - 22)}, - new String[]{"<" + arg.getLabel() + ">", description}); - } - - @Override - public void describeOption(FlagArgument arg, TreeSet<String> flagTokens, PrintWriter out) { - StringBuffer sb = new StringBuffer(); - for (String flagToken : flagTokens) { - if (sb.length() > 0) { - sb.append(" | "); - } - sb.append(flagToken); - } - format(out, new Cell[]{new Cell(4, 16), new Cell(2, NOMINAL_WIDTH - 22)}, - new String[]{sb.toString(), arg.getDescription()}); - } - - protected void format(PrintWriter out, Cell[] cells, String[] texts) { - if (cells.length != texts.length) { - throw new IllegalArgumentException("Number of cells and texts must match"); - } - // The text remaining to be formatted for each column. - String[] remains = new String[texts.length]; - // The total count of characters remaining - int remainsCount = 0; - // Initialize 'remains' and 'remainsCount' for the first iteration - for (int i = 0; i < texts.length; i++) { - remains[i] = (texts[i] == null) ? "" : texts[i].trim(); - remainsCount += remains[i].length(); - } - - StringBuilder result = new StringBuilder(); - // Repeat while there is still text to output. - while (remainsCount > 0) { - // Each iteration uses 'fit' to get up to 'cell.width' characters from each column - // and then uses 'stamp' to append to them to the buffer with the leading margin - // and trailing padding as required. - remainsCount = 0; - for (int i = 0; i < cells.length; i++) { - String field = cells[i].fit(remains[i]); - remains[i] = remains[i].substring(field.length()); - remainsCount += remains[i].length(); - result.append(cells[i].stamp(field.trim())); - } - result.append('\n'); - } - out.print(result.toString()); - } - - /** - * Get a String consisting of 'count' spaces. - * - * @param count the number of spaces - * @return the string - */ - private static String getSpaces(int count) { - // The following assumes that 1) StringBuilder.append is efficient if you - // preallocate the StringBuilder, 2) StringBuilder.toString() does no character - // copying, and 3) String.substring(...) also does no character copying. - int len = spaces.length(); - if (count > len) { - StringBuilder sb = new StringBuilder(count); - for (int i = 0; i < count; i++) { - sb.append(' '); - } - spaces = sb.toString(); - return spaces; - } else if (count == len) { - return spaces; - } else { - return spaces.substring(0, count); - } - } - - /** - * A Cell is a template for formatting text for help messages. (It is 'protected' so that - * the unit test can declare a subclass ...) - */ - protected static class Cell { - - final String field; - final int margin; - final int width; - - /** - * Construct a Cell with a leading margin and a text width. - * - * @param margin the number of leading spaces for the Cell - * @param width the width of the text part of the Cell - */ - protected Cell(int margin, int width) { - this.margin = margin; - this.width = width; - - // for performance, we pre-build the field mask - this.field = getSpaces(margin + width); - } - - /** - * Heuristically, split of a head substring of 'text' to fit within this Cell's width. We try - * to split at a space character, but if this will make the text too ragged, we simply chop. - */ - protected String fit(String text) { - if (width >= text.length()) { - return text; - } - String hardFit = text.substring(0, width); - if (hardFit.endsWith(" ")) { - return hardFit; - } - int lastSpace = hardFit.lastIndexOf(' '); - if (lastSpace > 3 * width / 4) { - return hardFit.substring(0, lastSpace); - } else { - return hardFit; - } - } - - /** - * Stamp out a line with leading and trailing spaces to fill the Cell. - */ - protected String stamp(String text) { - if (text.length() > field.length()) - throw new IllegalArgumentException("Text length exceeds field width"); - return field.substring(0, margin) + text + field.substring(0, width - text.length()); - } - } - - -} Added: trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelpFactory.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelpFactory.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelpFactory.java 2008-11-16 02:53:07 UTC (rev 4703) @@ -0,0 +1,387 @@ +/* + * $Id: DefaultHelp.java 4556 2008-09-13 08:02:20Z crawley $ + * + * JNode.org + * Copyright (C) 2003-2006 JNode.org + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; If not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +package org.jnode.shell.help.def; + +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; + +import org.jnode.shell.Command; +import org.jnode.shell.CommandInfo; +import org.jnode.shell.Shell; +import org.jnode.shell.ShellUtils; +import org.jnode.shell.help.Argument; +import org.jnode.shell.help.Help; +import org.jnode.shell.help.HelpException; +import org.jnode.shell.help.HelpFactory; +import org.jnode.shell.help.Parameter; +import org.jnode.shell.help.Syntax; +import org.jnode.shell.syntax.ArgumentBundle; +import org.jnode.shell.syntax.FlagArgument; +import org.jnode.shell.syntax.OptionSyntax; +import org.jnode.shell.syntax.SyntaxBundle; +import org.jnode.shell.syntax.SyntaxManager; + +/** + * @author qades + * @author Fabien DUMINY (fd...@jn...) + * @author cr...@jn... + */ +public class DefaultHelpFactory extends HelpFactory { + public static final String RESOURCE_NAME = "messages.properties"; + private static final int NOMINAL_WIDTH = 75; + /* start with 80 spaces ... */ + private static String spaces = + " "; + + /** + * Create a new instance + */ + public DefaultHelpFactory() { + } + + @Override + public Help getHelp(String alias, CommandInfo cmdInfo) throws HelpException { + Help.Info info = null; + SyntaxBundle syntaxes = null; + ArgumentBundle bundle = null; + try { + final Shell shell = ShellUtils.getShellManager().getCurrentShell(); + final SyntaxManager syntaxManager = shell.getSyntaxManager(); + Class<?> clazz = cmdInfo.getCommandClass(); + Command cmd = cmdInfo.createCommandInstance(); + if (cmd != null) { + bundle = cmd.getArgumentBundle(); + syntaxes = syntaxManager.getSyntaxBundle(alias); + if (syntaxes == null) { + syntaxes = new SyntaxBundle(alias, bundle.createDefaultSyntax()); + } + } else { + info = HelpFactory.getInfo(clazz); + } + } catch (HelpException ex) { + throw ex; + } catch (Exception ex) { + throw new HelpException(ex.getMessage(), ex); + } + + if (info != null && alias != null) { + return new OldSyntaxHelp(info, alias); + } else if (syntaxes != null && bundle != null) { + return new NewSyntaxHelp(syntaxes, bundle); + } else { + return null; + } + } + + /** + * Shows the complete help for a command. + * + * @see HelpFactory#help(org.jnode.shell.help.HelpFactory.Info, String) + */ + public void help(Help.Info info, String command, PrintWriter out) { + final Syntax[] syntaxes = info.getSyntaxes(); + final String name = command == null ? info.getName() : command; + for (int i = 0; i < syntaxes.length; i++) { + help(name, syntaxes[i], out); + if (i < syntaxes.length) { + out.println(); + } + } + } + + @Override + public void help(SyntaxBundle syntaxes, ArgumentBundle bundle, PrintWriter out) { + usage(syntaxes, bundle, out); + if (bundle.getDescription() != null) { + out.println("\n" + HelpFactory.getLocalizedHelp("help.description") + ":"); + format(out, new Cell[]{new Cell(4, NOMINAL_WIDTH - 4)}, + new String[]{bundle.getDescription()}); + } + Map<String, TreeSet<String>> flagMap = buildFlagMap(syntaxes); + boolean first = true; + for (org.jnode.shell.syntax.Argument<?> arg : bundle) { + if (arg instanceof FlagArgument) { + if (first) { + out.println("\n" + HelpFactory.getLocalizedHelp("help.options") + ":"); + first = false; + } + describeOption((FlagArgument) arg, flagMap.get(arg.getLabel()), out); + } + } + first = true; + for (org.jnode.shell.syntax.Argument<?> arg : bundle) { + if (!(arg instanceof FlagArgument)) { + if (first) { + out.println("\n" + HelpFactory.getLocalizedHelp("help.parameters") + ":"); + first = false; + } + describeArgument(arg, out); + } + } + } + + private Map<String, TreeSet<String>> buildFlagMap(SyntaxBundle syntaxes) { + HashMap<String, TreeSet<String>> res = new HashMap<String, TreeSet<String>>(); + for (org.jnode.shell.syntax.Syntax syntax : syntaxes.getSyntaxes()) { + buildFlagMap(syntax, res); + } + return res; + } + + private void buildFlagMap(org.jnode.shell.syntax.Syntax syntax, + HashMap<String, TreeSet<String>> res) { + if (syntax instanceof OptionSyntax) { + OptionSyntax os = (OptionSyntax) syntax; + String key = os.getArgName(); + TreeSet<String> options = res.get(key); + if (options == null) { + options = new TreeSet<String>(); + res.put(key, options); + } + String shortOptName = os.getShortOptName(); + if (shortOptName != null) { + options.add(shortOptName); + } + String longOptName = os.getLongOptName(); + if (longOptName != null) { + options.add(longOptName); + } + } else { + for (org.jnode.shell.syntax.Syntax child : syntax.getChildren()) { + buildFlagMap(child, res); + } + } + } + + /** + * Shows the help for a command syntax. + */ + public void help(String name, Syntax syntax, PrintWriter out) { + usage(name, syntax, out); + if (syntax.getDescription() != null) { + out.println("\n" + HelpFactory.getLocalizedHelp("help.description") + ":"); + format(out, new Cell[]{new Cell(4, NOMINAL_WIDTH - 4)}, + new String[]{syntax.getDescription()}); + } + final Parameter[] params = syntax.getParams(); + if (params.length != 0) { + out.println("\n" + HelpFactory.getLocalizedHelp("help.parameters") + ":"); + for (int i = 0; i < params.length; i++) { + params[i].describe(this, out); + } + } + } + + /** + * Shows the usage information of a command. + */ + public void usage(Help.Info info, PrintWriter out) { + final Syntax[] syntaxes = info.getSyntaxes(); + for (int i = 0; i < syntaxes.length; i++) { + usage(info.getName(), syntaxes[i], out); + } + } + + /** + * Shows the usage information of a command. + */ + public void usage(String name, Syntax syntax, PrintWriter out) { + StringBuilder line = new StringBuilder(name); + final Parameter[] params = syntax.getParams(); + for (int i = 0; i < params.length; i++) { + line.append(' ').append(params[i].format()); + } + out.println(HelpFactory.getLocalizedHelp("help.usage") + ": " + line); + } + + @Override + public void usage(SyntaxBundle syntaxBundle, ArgumentBundle bundle, PrintWriter out) { + String command = syntaxBundle.getAlias(); + String usageText = HelpFactory.getLocalizedHelp("help.usage") + ":"; + int usageLength = usageText.length(); + int commandLength = command.length(); + Cell[] cells = + new Cell[]{new Cell(0, usageLength), new Cell(1, commandLength), + new Cell(1, NOMINAL_WIDTH - 2 - usageLength - commandLength)}; + String[] texts = new String[]{usageText, command, null}; + String[] texts2 = new String[]{"", "", null}; + org.jnode.shell.syntax.Syntax[] syntaxes = syntaxBundle.getSyntaxes(); + if (syntaxes.length > 0) { + for (int i = 0; i < syntaxes.length; i++) { + if (i == 1) { + texts[0] = getSpaces(usageLength); + } + texts[2] = syntaxes[i].format(bundle); + format(out, cells, texts); + texts2[2] = syntaxes[i].getDescription(); + format(out, cells, texts2); + } + } else { + texts[2] = ""; + format(out, cells, texts); + } + } + + public void describeParameter(Parameter param, PrintWriter out) { + format(out, new Cell[]{new Cell(2, 18), new Cell(2, NOMINAL_WIDTH - 22)}, + new String[]{param.getName(), param.getDescription()}); + } + + public void describeArgument(Argument arg, PrintWriter out) { + format(out, new Cell[]{new Cell(4, 16), new Cell(2, NOMINAL_WIDTH - 22)}, + new String[]{arg.getName(), arg.getDescription()}); + } + + @Override + public void describeArgument(org.jnode.shell.syntax.Argument<?> arg, PrintWriter out) { + String description = "(" + arg.getTypeDescription() + ") " + arg.getDescription(); + format(out, new Cell[]{new Cell(4, 16), new Cell(2, NOMINAL_WIDTH - 22)}, + new String[]{"<" + arg.getLabel() + ">", description}); + } + + @Override + public void describeOption(FlagArgument arg, TreeSet<String> flagTokens, PrintWriter out) { + StringBuffer sb = new StringBuffer(); + for (String flagToken : flagTokens) { + if (sb.length() > 0) { + sb.append(" | "); + } + sb.append(flagToken); + } + format(out, new Cell[]{new Cell(4, 16), new Cell(2, NOMINAL_WIDTH - 22)}, + new String[]{sb.toString(), arg.getDescription()}); + } + + protected void format(PrintWriter out, Cell[] cells, String[] texts) { + if (cells.length != texts.length) { + throw new IllegalArgumentException("Number of cells and texts must match"); + } + // The text remaining to be formatted for each column. + String[] remains = new String[texts.length]; + // The total count of characters remaining + int remainsCount = 0; + // Initialize 'remains' and 'remainsCount' for the first iteration + for (int i = 0; i < texts.length; i++) { + remains[i] = (texts[i] == null) ? "" : texts[i].trim(); + remainsCount += remains[i].length(); + } + + StringBuilder result = new StringBuilder(); + // Repeat while there is still text to output. + while (remainsCount > 0) { + // Each iteration uses 'fit' to get up to 'cell.width' characters from each column + // and then uses 'stamp' to append to them to the buffer with the leading margin + // and trailing padding as required. + remainsCount = 0; + for (int i = 0; i < cells.length; i++) { + String field = cells[i].fit(remains[i]); + remains[i] = remains[i].substring(field.length()); + remainsCount += remains[i].length(); + result.append(cells[i].stamp(field.trim())); + } + result.append('\n'); + } + out.print(result.toString()); + } + + /** + * Get a String consisting of 'count' spaces. + * + * @param count the number of spaces + * @return the string + */ + private static String getSpaces(int count) { + // The following assumes that 1) StringBuilder.append is efficient if you + // preallocate the StringBuilder, 2) StringBuilder.toString() does no character + // copying, and 3) String.substring(...) also does no character copying. + int len = spaces.length(); + if (count > len) { + StringBuilder sb = new StringBuilder(count); + for (int i = 0; i < count; i++) { + sb.append(' '); + } + spaces = sb.toString(); + return spaces; + } else if (count == len) { + return spaces; + } else { + return spaces.substring(0, count); + } + } + + /** + * A Cell is a template for formatting text for help messages. (It is 'protected' so that + * the unit test can declare a subclass ...) + */ + protected static class Cell { + + final Str... [truncated message content] |