From: <cr...@us...> - 2008-03-16 13:55:06
|
Revision: 3844 http://jnode.svn.sourceforge.net/jnode/?rev=3844&view=rev Author: crawley Date: 2008-03-16 06:55:01 -0700 (Sun, 16 Mar 2008) Log Message: ----------- Changes to add / integrate the new-style command syntax system. Modified Paths: -------------- trunk/all/conf/default-plugin-list.xml trunk/all/conf/shell-plugin-list.xml trunk/shell/.classpath trunk/shell/descriptors/org.jnode.shell.command.xml trunk/shell/descriptors/org.jnode.shell.help.xml trunk/shell/descriptors/org.jnode.shell.xml trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/Command.java trunk/shell/src/shell/org/jnode/shell/CommandInfo.java trunk/shell/src/shell/org/jnode/shell/CommandInvoker.java trunk/shell/src/shell/org/jnode/shell/CommandLine.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/DefaultInterpreter.java trunk/shell/src/shell/org/jnode/shell/NoTokensAvailableException.java trunk/shell/src/shell/org/jnode/shell/ProcletCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java trunk/shell/src/shell/org/jnode/shell/Shell.java trunk/shell/src/shell/org/jnode/shell/ShellUtils.java trunk/shell/src/shell/org/jnode/shell/ThreadCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneToken.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java trunk/shell/src/shell/org/jnode/shell/command/HelpCommand.java trunk/shell/src/shell/org/jnode/shell/command/SetCommand.java trunk/shell/src/shell/org/jnode/shell/def/ShellPlugin.java trunk/shell/src/shell/org/jnode/shell/help/Argument.java trunk/shell/src/shell/org/jnode/shell/help/Help.java trunk/shell/src/shell/org/jnode/shell/help/def/DefaultHelp.java Added Paths: ----------- trunk/shell/descriptors/org.jnode.shell.syntax.xml trunk/shell/src/shell/org/jnode/shell/SymbolSource.java trunk/shell/src/shell/org/jnode/shell/syntax/ trunk/shell/src/shell/org/jnode/shell/syntax/AliasArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/AlternativesSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/Argument.java trunk/shell/src/shell/org/jnode/shell/syntax/ArgumentBundle.java trunk/shell/src/shell/org/jnode/shell/syntax/ArgumentSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/CommandSyntaxException.java trunk/shell/src/shell/org/jnode/shell/syntax/DefaultSyntaxManager.java trunk/shell/src/shell/org/jnode/shell/syntax/EnumArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/FileArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/FlagArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/GroupSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/IntegerArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/MuAlternation.java trunk/shell/src/shell/org/jnode/shell/syntax/MuArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/MuBackReference.java trunk/shell/src/shell/org/jnode/shell/syntax/MuParser.java trunk/shell/src/shell/org/jnode/shell/syntax/MuPreset.java trunk/shell/src/shell/org/jnode/shell/syntax/MuSequence.java trunk/shell/src/shell/org/jnode/shell/syntax/MuSymbol.java trunk/shell/src/shell/org/jnode/shell/syntax/MuSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/OptionSetSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/OptionSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/PowersetSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/PropertyNameArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/SequenceSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/SharedStack.java trunk/shell/src/shell/org/jnode/shell/syntax/StringArgument.java trunk/shell/src/shell/org/jnode/shell/syntax/Syntax.java trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxArgumentMissingException.java trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxFailureException.java trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxManager.java trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxMultiplicityException.java trunk/shell/src/shell/org/jnode/shell/syntax/SystemSyntaxPlugin.java trunk/shell/src/shell/org/jnode/shell/syntax/TokenSyntax.java trunk/shell/src/test/ trunk/shell/src/test/org/ trunk/shell/src/test/org/jnode/ trunk/shell/src/test/org/jnode/test/ trunk/shell/src/test/org/jnode/test/shell/ trunk/shell/src/test/org/jnode/test/shell/syntax/ trunk/shell/src/test/org/jnode/test/shell/syntax/AllTests.java trunk/shell/src/test/org/jnode/test/shell/syntax/AlternativesSyntaxTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/ArgumentBundleTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/ArgumentMultiplicityTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/ArgumentTypesTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/CommandLineTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/DefaultTokenizerTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/MuParserTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/MuParserTest2.java trunk/shell/src/test/org/jnode/test/shell/syntax/MuSyntaxTest.java trunk/shell/src/test/org/jnode/test/shell/syntax/OptionSetSyntaxTest.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 trunk/shell/src/test/org/jnode/test/shell/syntax/TestAliasManager.java trunk/shell/src/test/org/jnode/test/shell/syntax/TestShell.java trunk/shell/src/test/org/jnode/test/shell/syntax/TestSyntaxManager.java Modified: trunk/all/conf/default-plugin-list.xml =================================================================== --- trunk/all/conf/default-plugin-list.xml 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/all/conf/default-plugin-list.xml 2008-03-16 13:55:01 UTC (rev 3844) @@ -154,6 +154,7 @@ <plugin id="org.jnode.shell"/> <plugin id="org.jnode.shell.help"/> + <plugin id="org.jnode.shell.syntax"/> <plugin id="org.jnode.shell.command"/> <plugin id="org.jnode.shell.command.bsh"/> <plugin id="org.jnode.shell.command.debug"/> Modified: trunk/all/conf/shell-plugin-list.xml =================================================================== --- trunk/all/conf/shell-plugin-list.xml 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/all/conf/shell-plugin-list.xml 2008-03-16 13:55:01 UTC (rev 3844) @@ -28,4 +28,5 @@ <plugin id="org.jnode.shell"/> <plugin id="org.jnode.shell.command"/> <plugin id="org.jnode.shell.help"/> + <plugin id="org.jnode.shell.syntax"/> </plugin-list> Modified: trunk/shell/.classpath =================================================================== --- trunk/shell/.classpath 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/.classpath 2008-03-16 13:55:01 UTC (rev 3844) @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src/shell"/> + <classpathentry kind="src" path="src/test"/> <classpathentry kind="src" path="/JNode-Core"/> <classpathentry kind="lib" path="/JNode-Core/lib/log4j-1.2.8.jar"/> <classpathentry kind="lib" path="/JNode-Core/lib/junit.jar"/> Modified: trunk/shell/descriptors/org.jnode.shell.command.xml =================================================================== --- trunk/shell/descriptors/org.jnode.shell.command.xml 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/descriptors/org.jnode.shell.command.xml 2008-03-16 13:55:01 UTC (rev 3844) @@ -46,6 +46,15 @@ <alias name="onheap" class="org.jnode.shell.command.OnHeapCommand"/> <alias name="grep" class="org.jnode.shell.command.GrepCommand"/> </extension> + + <extension point="org.jnode.shell.syntaxes"> + <syntax alias="set"> + <sequence> + <argument argLabel="key"/> + <argument argLabel="value"/> + </sequence> + </syntax> + </extension> <extension point="org.jnode.security.permissions"> <permission class="java.util.PropertyPermission" name="*" actions="read,write"/> Modified: trunk/shell/descriptors/org.jnode.shell.help.xml =================================================================== --- trunk/shell/descriptors/org.jnode.shell.help.xml 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/descriptors/org.jnode.shell.help.xml 2008-03-16 13:55:01 UTC (rev 3844) @@ -10,6 +10,7 @@ <requires> <import plugin="org.jnode.shell"/> + <import plugin="org.jnode.shell.syntax"/> </requires> <extension point="org.jnode.security.permissions"> Added: trunk/shell/descriptors/org.jnode.shell.syntax.xml =================================================================== --- trunk/shell/descriptors/org.jnode.shell.syntax.xml (rev 0) +++ trunk/shell/descriptors/org.jnode.shell.syntax.xml 2008-03-16 13:55:01 UTC (rev 3844) @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plugin SYSTEM "jnode.dtd"> + +<plugin id="org.jnode.shell.syntax" + name="JNode Command Syntax System" + version="@VERSION@" + provider-name="JNode.org" + license-name="lgpl" + class="org.jnode.shell.syntax.SystemSyntaxPlugin"> + + <requires> + <import plugin="org.jnode.shell"/> + </requires> + + <extension point="org.jnode.security.permissions"> + <permission class="java.io.FilePermission" name="<<ALL FILES>>" actions="read"/> + </extension> + +</plugin> Modified: trunk/shell/descriptors/org.jnode.shell.xml =================================================================== --- trunk/shell/descriptors/org.jnode.shell.xml 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/descriptors/org.jnode.shell.xml 2008-03-16 13:55:01 UTC (rev 3844) @@ -24,10 +24,13 @@ <export name="org.jnode.shell.help.argument.*"/> <export name="org.jnode.shell.help.def.*"/> <export name="org.jnode.shell.proclet.*"/> + <export name="org.jnode.shell.syntax.*"/> </library> </runtime> <extension-point id="aliases" name="System shell aliases"/> + + <extension-point id="syntaxes" name="System command syntaxes"/> <extension point="org.jnode.security.permissions"> <permission class="java.io.FilePermission" name="<<ALL FILES>>" actions="read,write"/> Modified: trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -1,5 +1,28 @@ +/* + * $Id: Command.java 3772 2008-02-10 15:02:53Z lsantha $ + * + * JNode.org + * Copyright (C) 2007-2008 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; +import org.jnode.shell.syntax.Argument; +import org.jnode.shell.syntax.ArgumentBundle; import org.jnode.vm.VmExit; /** @@ -9,7 +32,17 @@ * */ public abstract class AbstractCommand implements Command { + + private ArgumentBundle bundle; + + public AbstractCommand() { + this.bundle = null; + } + public AbstractCommand(String description) { + this.bundle = new ArgumentBundle(description); + } + @SuppressWarnings("deprecation") public final void execute(String[] args) throws Exception { execute(new CommandLine(args), System.in, System.out, System.err); @@ -23,4 +56,17 @@ protected void exit(int rc) { throw new VmExit(rc); } + + public final ArgumentBundle getArgumentBundle() { + return bundle; + } + + protected final void registerArguments(Argument<?> ... args) { + if (bundle == null) { + bundle = new ArgumentBundle(); + } + for (Argument<?> arg : args) { + bundle.addArgument(arg); + } + } } Modified: trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -77,22 +77,26 @@ // ctrl-c } - public int invoke(CommandLine cmdLine) throws ShellException { + public int invoke(CommandLine cmdLine, Command command) throws ShellException { + // FIXME -- we already did this ... CommandInfo cmdInfo = lookupCommand(cmdLine); if (cmdInfo == null) { return 0; } - CommandRunner cr = setup(cmdLine, cmdInfo); + CommandRunner cr = setup(cmdLine, command, cmdInfo); return runIt(cmdLine, cmdInfo, cr); } - public CommandThread invokeAsynchronous(CommandLine cmdLine) + public CommandThread invokeAsynchronous(CommandLine cmdLine, Command command) throws ShellException { - CommandInfo cmdInfo = lookupCommand(cmdLine); - if (cmdInfo == null) { - return null; + CommandInfo cmdInfo = null; + if (command == null) { + cmdInfo = lookupCommand(cmdLine); + if (cmdInfo == null) { + return null; + } } - CommandRunner cr = setup(cmdLine, cmdInfo); + CommandRunner cr = setup(cmdLine, command, cmdInfo); return forkIt(cmdLine, cmdInfo, cr); } @@ -110,7 +114,7 @@ } } - private CommandRunner setup(CommandLine cmdLine, CommandInfo cmdInfo) + private CommandRunner setup(CommandLine cmdLine, Command command, CommandInfo cmdInfo) throws ShellException { Method method; CommandRunner cr = null; @@ -125,17 +129,10 @@ } catch (ClassCastException ex) { throw new ShellFailureException("streams array broken", ex); } - try { - method = cmdInfo.getCommandClass().getMethod(EXECUTE_METHOD, - EXECUTE_ARG_TYPES); - if ((method.getModifiers() & Modifier.STATIC) == 0) { - cr = createRunner(cmdInfo.getCommandClass(), method, - new Object[] { cmdLine, in, out, err }, in, out, err); - } - } catch (NoSuchMethodException e) { - // continue; + if (command != null) { + cr = createRunner(command, cmdLine, in, out, err); } - if (cr == null) { + else { try { method = cmdInfo.getCommandClass().getMethod(MAIN_METHOD, MAIN_ARG_TYPES); @@ -149,19 +146,20 @@ + " does not allow redirection or pipelining"); } cr = createRunner(cmdInfo.getCommandClass(), method, - new Object[] { cmdLine.getArguments() }, in, out, - err); + new Object[] { cmdLine.getArguments() }, + in, out, err); } } catch (NoSuchMethodException e) { // continue; } + if (cr == null) { + throw new ShellInvocationException( + "No suitable entry point method for " + + cmdInfo.getCommandClass()); + } } - if (cr == null) { - throw new ShellInvocationException( - "No suitable entry point method for " - + cmdInfo.getCommandClass()); - } - // THese are now the real streams ... + + // These are now the real streams ... cmdLine.setStreams(new Closeable[] { in, out, err }); return cr; } @@ -178,11 +176,13 @@ throw new ShellInvocationException( "Exception while creating command thread", ex); } - + // FIXME this method for waiting for the command to finish is + // really lame ... and theoretically incorrect. We should + // wait / notify. this.blocking = true; this.blockingThread = Thread.currentThread(); this.cmdName = cmdLine.getCommandName(); - + threadProcess.start(); while (this.blocking) { @@ -274,8 +274,10 @@ public void keyReleased(KeyboardEvent event) { } + + abstract CommandRunner createRunner(Class<?> cx, Method method, Object[] args, + InputStream in, PrintStream out, PrintStream err); - abstract CommandRunner createRunner(Class<?> cx, Method method, - Object[] args, InputStream commandIn, PrintStream commandOut, - PrintStream commandErr); + abstract CommandRunner createRunner(Command command, CommandLine cmdLine, + InputStream in, PrintStream out, PrintStream err); } Modified: trunk/shell/src/shell/org/jnode/shell/Command.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/Command.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/Command.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -24,8 +24,11 @@ import java.io.InputStream; import java.io.PrintStream; +import org.jnode.shell.syntax.ArgumentBundle; + /** * @author Martin Husted Hartvig (ha...@jn...) + * @author cr...@jn... */ public interface Command { @@ -43,4 +46,6 @@ */ public void execute(CommandLine commandLine, InputStream in, PrintStream out, PrintStream err) throws Exception; + + public ArgumentBundle getArgumentBundle(); } Modified: trunk/shell/src/shell/org/jnode/shell/CommandInfo.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandInfo.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/CommandInfo.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -42,4 +42,13 @@ public final boolean isInternal() { return internal; } + + public final Command createCommandInstance() throws InstantiationException, IllegalAccessException { + if (Command.class.isAssignableFrom(clazz)) { + return (Command) (clazz.newInstance()); + } + else { + return null; + } + } } Modified: trunk/shell/src/shell/org/jnode/shell/CommandInvoker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandInvoker.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/CommandInvoker.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -45,11 +45,13 @@ * * @param commandLine this provides the command name (alias), the command * arguments and (where relevant) the command's i/o stream context. + * @param command the command instance to which arguments have previously + * been bound, or <code>null</null> * @return an integer return code, with zero indicating command success, * non-zero indicating command failure. * @throws ShellException if there was some problem launching the command. */ - int invoke(CommandLine commandLine) throws ShellException; + int invoke(CommandLine commandLine, Command command) throws ShellException; /** * Create a thread for running a command asynchronously. This can be used @@ -57,12 +59,14 @@ * * @param commandLine this provides the command name (alias), the command * arguments and (where relevant) the command's i/o stream context. + * @param command the command object that we bound arguments to, or + * <code>null</code> * @return the thread for the command. Calling * {@link java.lang.Thread.start()} will cause the command to * execute. * @throws ShellException if there was some problem launching the command. */ - CommandThread invokeAsynchronous(CommandLine commandLine) + CommandThread invokeAsynchronous(CommandLine commandLine, Command command) throws ShellException; /** Modified: trunk/shell/src/shell/org/jnode/shell/CommandLine.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandLine.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/CommandLine.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -22,24 +22,32 @@ package org.jnode.shell; import java.io.Closeable; -import java.util.Iterator; +import java.util.Collections; +import java.util.List; import java.util.NoSuchElementException; +import org.apache.log4j.Logger; import org.jnode.driver.console.CompletionInfo; -import org.jnode.shell.help.Argument; import org.jnode.shell.help.CompletionException; import org.jnode.shell.help.Help; import org.jnode.shell.help.HelpException; import org.jnode.shell.help.Parameter; -import org.jnode.shell.help.argument.AliasArgument; -import org.jnode.shell.help.argument.FileArgument; +import org.jnode.shell.syntax.CommandSyntaxException; +import org.jnode.shell.syntax.AliasArgument; +import org.jnode.shell.syntax.FileArgument; +import org.jnode.shell.syntax.ArgumentBundle; +import org.jnode.shell.syntax.ArgumentSyntax; +import org.jnode.shell.syntax.RepeatSyntax; +import org.jnode.shell.syntax.Syntax; + /** * This class represents the command line as command name and a sequence of * argument strings. It also can carry the i/o stream environment for launching * the command. * - * TODO This class needs to be "syntax agnostic". + * TODO This class needs to be fully "shell and command syntax agnostic". + * TODO Get rid of API methods using a String argument representation. * * @author cr...@jn... */ @@ -93,17 +101,22 @@ private static final Token[] NO_TOKENS = new Token[0]; private final Help.Info defaultParameter = new Help.Info("file", - "default parameter for command line completion", new Parameter( - new FileArgument("file", "a file", Argument.MULTI), - Parameter.OPTIONAL)); + "default parameter for command line completion", + new Parameter( + new org.jnode.shell.help.argument.FileArgument( + "file", "a file", org.jnode.shell.help.Argument.MULTI), + org.jnode.shell.help.Parameter.OPTIONAL)); - private final Argument defaultArg = new AliasArgument("command", + private final org.jnode.shell.help.Argument defaultArg = + new org.jnode.shell.help.argument.AliasArgument("command", "the command to be called"); + + private final Syntax defaultSyntax = new RepeatSyntax(new ArgumentSyntax("argument")); + private final ArgumentBundle defaultArguments = new ArgumentBundle( + new FileArgument("argument", org.jnode.shell.syntax.Argument.MULTIPLE)); - private final String commandName; private final Token commandToken; - private final String[] arguments; private final Token[] argumentTokens; private Closeable[] streams; @@ -120,10 +133,8 @@ public CommandLine(Token commandToken, Token[] argumentTokens, Closeable[] streams) { this.commandToken = commandToken; - this.commandName = (commandToken == null) ? null : commandToken.token; this.argumentTokens = (argumentTokens == null || argumentTokens.length == 0) ? NO_TOKENS : argumentTokens.clone(); - this.arguments = prepareArguments(this.argumentTokens); this.streams = setupStreams(streams); } @@ -140,11 +151,17 @@ */ public CommandLine(String commandName, String[] arguments, Closeable[] streams) { - this.commandName = commandName; - this.arguments = (arguments == null || arguments.length == 0) ? NO_ARGS - : arguments.clone(); - this.commandToken = null; - this.argumentTokens = null; + this.commandToken = commandName == null ? null : new Token(commandName); + if (arguments == null || arguments.length == 0) { + this.argumentTokens = NO_TOKENS; + } + else { + int len = arguments.length; + argumentTokens = new Token[len]; + for (int i = 0; i < len; i++) { + this.argumentTokens[i] = new Token(arguments[i]); + } + } this.streams = setupStreams(streams); } @@ -166,7 +183,7 @@ * @deprecated It is a bad idea to leave out the command name. */ public CommandLine(String[] arguments) { - this(null, arguments, null); + this(null, arguments, null /* FIXME */); } private Closeable[] setupStreams(Closeable[] streams) { @@ -180,48 +197,77 @@ } } - private String[] prepareArguments(Token[] argumentTokens) { - String[] arguments = new String[argumentTokens.length]; - for (int i = 0; i < arguments.length; i++) { - arguments[i] = argumentTokens[i].token; - } - return arguments; - } - /** * This method returns an Iterator for the arguments represented as Strings. + * @deprecated */ - public Iterator<String> iterator() { - return new Iterator<String>() { + public SymbolSource<String> iterator() { + final boolean whitespaceAfterLast = this.argumentAnticipated; + + return new SymbolSource<String>() { private int pos = 0; public boolean hasNext() { - return pos < arguments.length; + return pos < argumentTokens.length; } public String next() throws NoSuchElementException { if (!hasNext()) { throw new NoSuchElementException(); } - return arguments[pos++]; + return argumentTokens[pos++].token; } + public String peek() throws NoSuchElementException { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return argumentTokens[pos].token; + } + + public String last() throws NoSuchElementException { + if (pos <= 0) { + throw new NoSuchElementException(); + } + return argumentTokens[pos - 1].token; + } + public void remove() { throw new UnsupportedOperationException(); } + public void seek(int pos) throws NoSuchElementException { + if (pos >= 0 && pos <= argumentTokens.length) { + this.pos = pos; + } + else { + throw new NoSuchElementException("pos out of range"); + } + } + + public int tell() { + return pos; + } + + public boolean whitespaceAfterLast() { + return whitespaceAfterLast; + } + }; } /** * This method returns an Iterator for the arguments represented as Tokens */ - public Iterator<Token> tokenIterator() throws NoTokensAvailableException { + public SymbolSource<Token> tokenIterator() throws NoTokensAvailableException { if (argumentTokens == null) { throw new NoTokensAvailableException( "No tokens available in the CommandLine"); } - return new Iterator<Token>() { + + final boolean whitespaceAfterLast = this.argumentAnticipated; + + return new SymbolSource<Token>() { private int pos = 0; public boolean hasNext() { @@ -235,10 +281,41 @@ return argumentTokens[pos++]; } - public void remove() { - throw new UnsupportedOperationException(); + public Token peek() throws NoSuchElementException { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return argumentTokens[pos]; } + + public Token last() throws NoSuchElementException { + if (pos <= 0) { + throw new NoSuchElementException(); + } + return argumentTokens[pos - 1]; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void seek(int pos) throws NoSuchElementException { + if (pos >= 0 && pos <= argumentTokens.length) { + this.pos = pos; + } + else { + throw new NoSuchElementException("pos out of range"); + } + } + public int tell() { + return pos; + } + + public boolean whitespaceAfterLast() { + return whitespaceAfterLast; + } + }; } @@ -248,7 +325,7 @@ * @return the command name */ public String getCommandName() { - return commandName; + return commandToken == null ? null : commandToken.token; } /** @@ -266,7 +343,15 @@ * @return the arguments as String[] */ public String[] getArguments() { - return arguments.clone(); + int len = argumentTokens.length; + if (len == 0) { + return NO_ARGS; + } + String[] arguments = new String[len]; + for (int i = 0; i < len; i++) { + arguments[i] = argumentTokens[i].token; + } + return arguments; } /** @@ -276,7 +361,7 @@ * @deprecated this method name is wrong. */ public String[] toStringArray() { - return arguments.clone(); + return getArguments(); } /** @@ -285,10 +370,10 @@ * @return the entire command line */ public String toString() { - StringBuilder sb = new StringBuilder(escape(commandName)); - for (String argument : arguments) { + StringBuilder sb = new StringBuilder(escape(commandToken.token)); + for (Token arg : argumentTokens) { sb.append(' '); - sb.append(escape(argument)); + sb.append(escape(arg.token)); } return sb.toString(); } @@ -299,7 +384,7 @@ * @return the remaining number of parts */ public int getLength() { - return arguments.length; + return argumentTokens.length; } public boolean isArgumentAnticipated() { @@ -342,13 +427,60 @@ * interpreter. */ public final int end; + + /** + * This field is <code>true</code> if the token is the target for completion. + */ + public final boolean completionTarget; - public Token(String token, int type, int start, int end) { - this.token = token; + public Token(String value, int type, int start, int end, boolean completionTarget) { + this.token = value; this.tokenType = type; this.start = start; this.end = end; + this.completionTarget = completionTarget; } + + public Token(String token) { + this(token, 0, 0, 0, false); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (completionTarget ? 1231 : 1237); + result = prime * result + end; + result = prime * result + start; + result = prime * result + ((token == null) ? 0 : token.hashCode()); + result = prime * result + tokenType; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Token other = (Token) obj; + if (completionTarget != other.completionTarget) + return false; + if (end != other.end) + return false; + if (start != other.start) + return false; + if (token == null) { + if (other.token != null) + return false; + } else if (!token.equals(other.token)) + return false; + if (tokenType != other.tokenType) + return false; + return true; + } } // escape and unescape methods @@ -378,8 +510,8 @@ * @param forceQuote if <code>true</code>, forces the argument to be * returned in quotes even if not necessary * @return the escaped argument - * @deprecated This method does not belong here. Escaping is an interpretter - * concern, and this class needs to be interpretter specific. + * @deprecated This method does not belong here. Escaping is an interpreter + * concern, and this class needs to be interpreter specific. */ public static String doEscape(String arg, boolean forceQuote) { int length = arg.length(); @@ -443,11 +575,57 @@ this.streams = streams.clone(); } + /** + * Perform command line argument parsing in preparation to invoking a command. + * This locates the command's class and a suitable command line syntax, then + * parses against the Syntax, binding the command arguments to Argument objects + * in an ArgumentBundle object obtained from the Command object. + * + * @param shell the context for resolving command aliases and locating syntaxes + * @return the command instance to which the arguments have been bound + * @throws CommandSyntaxException if the chosen syntax doesn't match the command + * line arguments. + */ + public Command parseCommandLine(CommandShell shell) + throws ShellException { + String cmd = (commandToken == null) ? "" : commandToken.token.trim(); + if (cmd.equals("")) { + throw new ShellFailureException("no command name"); + } + try { + // Get command's argument bundle and syntax + CommandInfo cmdClass = shell.getCommandClass(cmd); + Command command = cmdClass.createCommandInstance(); + + // Get the command's argument bundle, or the default one. + ArgumentBundle bundle = (command == null) ? null : + command.getArgumentBundle(); + bundle = (bundle == null) ? defaultArguments : bundle; + + // Get a syntax for the alias, or a default one. + Syntax syntax = shell.getSyntaxManager().getSyntax(cmd); + syntax = syntax == null ? defaultSyntax : syntax; + + // Do a full parse to bind the command line argument tokens to corresponding + // command arguments + bundle.parse(this, syntax); + return command; + } catch (ClassNotFoundException ex) { + throw new ShellException("Command class not found", ex); + } catch (InstantiationException ex) { + throw new ShellException("Command class cannot be instantiated", ex); + } catch (IllegalAccessException ex) { + throw new ShellException("Command class cannot be instantiated", ex); + } + } + public void complete(CompletionInfo completion, CommandShell shell) throws CompletionException { - String cmd = (commandName == null) ? "" : commandName.trim(); + Logger log = Logger.getLogger(CommandLine.class); + String cmd = (commandToken == null) ? "" : commandToken.token.trim(); String result = null; - if (!cmd.equals("") && (arguments.length > 0 || argumentAnticipated)) { + if (!cmd.equals("") && (argumentTokens.length > 0 || argumentAnticipated)) { + log.debug("doing argument completion"); try { // get command's help info CommandInfo cmdClass = shell.getCommandClass(cmd); @@ -470,6 +648,7 @@ } } else { // do completion on the command name + log.debug("doing command name completion"); result = defaultArg.complete(cmd); } completion.setCompleted(result); Modified: trunk/shell/src/shell/org/jnode/shell/CommandShell.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -36,7 +36,10 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.List; import java.util.StringTokenizer; import javax.naming.NameNotFoundException; @@ -53,13 +56,15 @@ import org.jnode.shell.alias.AliasManager; import org.jnode.shell.alias.NoSuchAliasException; import org.jnode.shell.help.CompletionException; +import org.jnode.shell.syntax.ArgumentBundle; +import org.jnode.shell.syntax.SyntaxManager; import org.jnode.util.SystemInputStream; import org.jnode.vm.VmSystem; /** * @author epr * @author Fabien DUMINY - * @authod crawley + * @author cr...@jn... */ public class CommandShell implements Runnable, Shell, ConsoleListener { @@ -97,6 +102,8 @@ private AliasManager aliasMgr; + private SyntaxManager syntaxMgr; + /** * Keeps a reference to the console this CommandShell is using * */ @@ -176,8 +183,8 @@ cons.setCompleter(this); console.addConsoleListener(this); - aliasMgr = ((AliasManager) InitialNaming.lookup(AliasManager.NAME)) - .createAliasManager(); + aliasMgr = ShellUtils.getAliasManager().createAliasManager(); + syntaxMgr = ShellUtils.getSyntaxManager().createSyntaxManager(); System.setProperty(PROMPT_PROPERTY_NAME, DEFAULT_PROMPT); } catch (NameNotFoundException ex) { throw new ShellException("Cannot find required resource", ex); @@ -206,6 +213,17 @@ ex.printStackTrace(); } } + + /** + * This constructor builds a partial command shell for test purposes only. + * + * @param aliasMgr test framework supplies an alias manager + * @param syntaxMgr test framework supplies a syntax manager + */ + protected CommandShell(AliasManager aliasMgr, SyntaxManager syntaxMgr) { + this.aliasMgr = aliasMgr; + this.syntaxMgr = syntaxMgr; + } /** * Run this shell until exit. @@ -429,8 +447,8 @@ * @return the command's return code * @throws ShellException */ - public int invoke(CommandLine cmdLine) throws ShellException { - return this.invoker.invoke(cmdLine); + public int invoke(CommandLine cmdLine, Command command) throws ShellException { + return this.invoker.invoke(cmdLine, command); } /** @@ -442,9 +460,9 @@ * @return the command's return code * @throws ShellException */ - public CommandThread invokeAsynchronous(CommandLine cmdLine) + public CommandThread invokeAsynchronous(CommandLine cmdLine, Command command) throws ShellException { - return this.invoker.invokeAsynchronous(cmdLine); + return this.invoker.invokeAsynchronous(cmdLine, command); } protected CommandInfo getCommandClass(String cmd) @@ -458,6 +476,19 @@ return new CommandInfo(cl.loadClass(cmd), false); } } + + protected ArgumentBundle getCommandArgumentBundle(CommandInfo commandInfo) { + if (Command.class.isAssignableFrom(commandInfo.getCommandClass())) { + try { + Command cmd = (Command) (commandInfo.getCommandClass().newInstance()); + return cmd.getArgumentBundle(); + } + catch (Exception ex) { + // drop through + } + } + return null; + } boolean isDebugEnabled() { return debugEnabled; @@ -540,7 +571,11 @@ throws ShellSyntaxException { return interpreter.parsePartial(this, cmdLineStr); } - + + /** + * This method is called by the console input driver to perform command line + * completion in response to a TAB character. + */ public CompletionInfo complete(String partial) { if (!readingCommand) { // dummy completion behavior for application input. @@ -551,6 +586,7 @@ } // workaround to set the currentShell to this shell + // FIXME is this needed? try { ShellUtils.getShellManager().registerShell(this); } catch (NameNotFoundException ex) { @@ -572,16 +608,11 @@ } } catch (ShellSyntaxException ex) { out.println(); // next line - err.println("Cannot parse: " + ex.getMessage()); // print the - // error - // (optional) + err.println("Cannot parse: " + ex.getMessage()); } catch (CompletionException ex) { out.println(); // next line - err.println("Problem in completer: " + ex.getMessage()); // print - // the - // error - // (optional) + err.println("Problem in completer: " + ex.getMessage()); } if (!success) { @@ -641,7 +672,7 @@ } /** - * This class subtypes FilterInputStream to capture console input to an + * This subtype of FilterInputStream captures the console input for an * application in the application input history. */ private class HistoryInputStream extends FilterInputStream { @@ -809,4 +840,8 @@ return new PrintStream((OutputStream) tmp); } } + + public SyntaxManager getSyntaxManager() { + return syntaxMgr; + } } Modified: trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -33,6 +33,7 @@ import org.jnode.shell.help.Help; import org.jnode.shell.help.SyntaxErrorException; +import org.jnode.vm.VmExit; /* * User: Sam Reid Date: Dec 20, 2003 Time: 1:20:33 AM Copyright (c) Dec 20, 2003 @@ -52,7 +53,7 @@ private final PrintStream err; private final CommandShell commandShell; - private static final Class<?>[] MAIN_ARG_TYPES = new Class[] { String[].class }; + private static final Class<?>[] MAIN_ARG_TYPES = new Class[] { String[].class}; static final Factory FACTORY = new Factory() { public CommandInvoker create(CommandShell shell) { @@ -73,7 +74,10 @@ return "default"; } - public int invoke(CommandLine cmdLine) { + /** + * Invoke the command. The Command argument is not used by this method. + */ + public int invoke(CommandLine cmdLine, Command command) { String cmdName = cmdLine.getCommandName(); if (cmdName == null) { return 0; @@ -109,15 +113,11 @@ } catch (PrivilegedActionException ex) { throw ex.getException(); } - } catch (InvocationTargetException ex) { - Throwable tex = ex.getTargetException(); - if (tex instanceof SyntaxErrorException) { - Help.getInfo(cmdInfo.getCommandClass()).usage(); - err.println(tex.getMessage()); - } else { - err.println("Exception in command"); - stackTrace(tex); - } + } catch (SyntaxErrorException ex) { + Help.getInfo(cmdInfo.getCommandClass()).usage(); + err.println(ex.getMessage()); + } catch (VmExit ex) { + return ex.getStatus(); } catch (Exception ex) { err.println("Exception in command"); stackTrace(ex); @@ -138,7 +138,7 @@ return 1; } - public CommandThread invokeAsynchronous(CommandLine commandLine) { + public CommandThread invokeAsynchronous(CommandLine commandLine, Command command) { throw new UnsupportedOperationException(); } Modified: trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2008-03-16 05:47:04 UTC (rev 3843) +++ trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2008-03-16 13:55:01 UTC (rev 3844) @@ -21,10 +21,13 @@ package org.jnode.shell; -import java.util.Iterator; +import java.util.ArrayList; import java.util.LinkedList; import java.util.NoSuchElementException; +import org.jnode.shell.CommandLine.Token; +import org.jnode.shell.syntax.CommandSyntaxException; + /** * This interpreter simply parses the command line into a command name and * arguments, with simple quoting and escaping. @@ -78,7 +81,8 @@ } public int interpret(CommandShell shell, String line) throws ShellException { - LinkedList<CommandLine.Token> tokens = new LinkedList<CommandLine.Token>(); + LinkedList<CommandLine.Token> tokens = + new LinkedList<CommandLine.Token>(); Tokenizer tokenizer = new Tokenizer(line); while (tokenizer.hasNext()) { tokens.add(tokenizer.next()); @@ -87,17 +91,23 @@ if (nosTokens == 0) { return 0; } - CommandLine commandLine; + CommandLine cmd; if (nosTokens == 1) { - commandLine = new CommandLine(tokens.get(0), null, null); + cmd = new CommandLine(tokens.get(0), null, null); } else { CommandLine.Token commandToken = tokens.removeFirst(); - CommandLine.Token[] argTokens = new CommandLine.Token[nosTokens - 1]; - commandLine = new CommandLine(commandToken, tokens - .toArray(argTokens), null); + CommandLine.Token[] argTokens = + new CommandLine.Token[nosTokens - 1]; + cmd = new CommandLine( + commandToken, tokens.toArray(argTokens),null); } shell.addCommandToHistory(line); - return shell.invoke(commandLine); + try { + Command command = cmd.parseCommandLine(shell); + return shell.invoke(cmd, command); + } catch (CommandSyntaxException ex) { + throw new ShellException("Command arguments don't match syntax", ex); + } } public Completable parsePartial(CommandShell shell, String line) @@ -107,14 +117,15 @@ return new CommandLine("", null); } CommandLine.Token commandToken = tokenizer.next(); - LinkedList<CommandLine.Token> tokenList = new LinkedList<CommandLine.Token>(); + LinkedList<CommandLine.Token> tokenList = + new LinkedList<CommandLine.Token>(); while (tokenizer.hasNext()) { tokenList.add(tokenizer.next()); } - CommandLine.Token[] argTokens = tokenList - .toArray(new CommandLine.Token[tokenList.size()]); + CommandLine.Token[] argTokens = + tokenList.toArray(new CommandLine.Token[tokenList.size()]); CommandLine res = new CommandLine(commandToken, argTokens, null); - res.setArgumentAnticipated(tokenizer.whitespaceAfter(tokenizer.last())); + res.setArgumentAnticipated(tokenizer.whitespaceAfterLast()); return res; } @@ -123,26 +134,20 @@ * understands quoting, some '\' escapes, and (depending on constructor * flags) certain "special" symbols. */ - static class Tokenizer implements Iterator<CommandLine.Token> { - private final String s; - private final int flags; - + protected static class Tokenizer implements SymbolSource<CommandLine.Token> { private int pos = 0; - private boolean inFullEscape = false; - private boolean inQuote = false; + private final ArrayList<CommandLine.Token> tokens = + new ArrayList<Token>(8); + private boolean whiteSpaceAfterLast; - private CommandLine.Token lastToken; - /** - * Instantiate a commandline tokenizer for a given input String. + * Instantiate a command line tokenizer for a given input String. * * @param line the input String. * @param flags flags controlling the tokenization. */ public Tokenizer(String line, int flags) { - pos = 0; - s = line; - this.flags = flags; + tokenize(line, flags); } public Tokenizer(String line) { @@ -156,10 +161,7 @@ * <code>false</code> otherwise */ public boolean hasNext() { - while (pos < s.length() && s.charAt(pos) == SPACE_CHAR) { - pos++; - } - return pos < s.length() && s.charAt(pos) != COMMENT_CHAR; + return pos < tokens.size(); } /** @@ -171,110 +173,128 @@ if (!hasNext()) { throw new NoSuchElementException(); } + return tokens.get(pos++); + } - int type = LITERAL; - int start = pos; + private void tokenize(String s, int flags) + throws IllegalArgumentException { + int pos = 0; - StringBuilder token = new StringBuilder(5); - char currentChar; + while (true) { + // Skip spaces before start of token + whiteSpaceAfterLast = false; + while (pos < s.length() && s.charAt(pos) == SPACE_CHAR) { + pos++; + whiteSpaceAfterLast = true; + } + if (pos >= s.length()) { + break; + } - boolean finished = false; + // Parse a token + boolean inFullEscape = false; + boolean inQuote = false; + int type = LITERAL; + int start = pos; + StringBuilder token = new StringBuilder(5); + char currentChar; + boolean finished = false; - while (!finished && pos < s.length()) { - currentChar = s.charAt(pos++); + while (!finished && pos < s.length()) { + currentChar = s.charAt(pos++); - switch (currentChar) { - case ESCAPE_CHAR: - if (pos >= s.length()) { - throw new IllegalArgumentException( - "escape char ('\\') not followed by a character"); - } - char ch; - switch (ch = s.charAt(pos++)) { - case N: - token.append(ESCAPE_N); + switch (currentChar) { + case ESCAPE_CHAR: + if (pos >= s.length()) { + throw new IllegalArgumentException( + "escape char ('\\') not followed by a character"); + } + char ch; + switch (ch = s.charAt(pos++)) { + case N: + token.append(ESCAPE_N); + break; + case B: + token.append(ESCAPE_B); + break; + case R: + token.append(ESCAPE_R); + break; + case T: + token.append(ESCAPE_T); + break; + default: + token.append(ch); + } break; - case B: - token.append(ESCAPE_B); + + case FULL_ESCAPE_CHAR: + if (inQuote) { + token.append(currentChar); + } else { + inFullEscape = !inFullEscape; // just a toggle + type = STRING; + if (!inFullEscape) { + type |= CLOSED; + } + } break; - case R: - token.append(ESCAPE_R); + case QUOTE_CHAR: + if (inFullEscape) { + token.append(currentChar); + } else { + inQuote = !inQuote; + type = STRING; + if (!inQuote) { + type |= CLOSED; + } + } break; - case T: - token.append(ESCAPE_T); + case SPACE_CHAR: + if (inFullEscape || inQuote) { + token.append(currentChar); + } else { + if (token.length() != 0) { // don't return an empty + // token + finished = true; + pos--; // to return trailing space as empty + // last + // token + } + } break; - default: - token.append(ch); - } - break; - - case FULL_ESCAPE_CHAR: - if (inQuote) { - token.append(currentChar); - } else { - inFullEscape = !inFullEscape; // just a toggle - type = STRING; - if (!inFullEscape) { - type |= CLOSED; - } - } - break; - case QUOTE_CHAR: - if (inFullEscape) { - token.append(currentChar); - } else { - inQuote = !inQuote; - type = STRING; - if (!inQuote) { - type |= CLOSED; - } - } - break; - case SPACE_CHAR: - if (inFullEscape || inQuote) { - token.append(currentChar); - } else { - if (token.length() != 0) { // don't return an empty - // token + case COMMENT_CHAR: + if (inFullEscape || inQuote) { + token.append(currentChar); + } else { finished = true; - pos--; // to return trailing space as empty last - // token + pos = s.length(); // ignore EVERYTHING } - } - break; - case COMMENT_CHAR: - if (inFullEscape || inQuote) { - token.append(currentChar); - } else { - finished = true; - pos = s.length(); // ignore EVERYTHING - } - break; - case GET_INPUT_FROM_CHAR: - case SEND_OUTPUT_TO_CHAR: - case PIPE_CHAR: - if (inFullEscape || inQuote - || (flags & REDIRECTS_FLAG) == 0) { - token.append(currentChar); - } else { - finished = true; - if (token.length() == 0) { + break; + case GET_INPUT_FROM_CHAR: + case SEND_OUTPUT_TO_CHAR: + case PIPE_CHAR: + if (inFullEscape || inQuote || + (flags & REDIRECTS_FLAG) == 0) { token.append(currentChar); - type = SPECIAL; } else { - pos--; // the special character terminates the - // literal. + finished = true; + if (token.length() == 0) { + token.append(currentChar); + type = SPECIAL; + } else { + pos--; // the special character terminates the + // literal. + } } + break; + default: + token.append(currentChar); } - break; - default: - token.append(currentChar); } + tokens.add(new CommandLine.Token(token.toString(), type, start, + pos, pos < s.length())); } - - lastToken = new CommandLine.Token(token.toString(), type, start, - pos); - return lastToken; } /** @@ -290,15 +310,29 @@ * @return the last token. */ public CommandLine.Token last() { - return lastToken; + return tokens.get(pos - 1); } - /** - * Test if there is a whitespace character after a token. This only - * works if the token was returned by this Tokenizer. - */ - public boolean whitespaceAfter(CommandLine.Token token) { - return token.end < s.length() && s.charAt(token.end) == SPACE_CHAR; + public Token peek() throws NoSuchElementException { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return tokens.get(pos + 1); } + + public void seek(int pos) throws NoSuchElementException { + if (pos < 0 || pos > tokens.siz... [truncated message content] |