Package org.jnode.shell

Source Code of org.jnode.shell.AbstractCommand

/*
* $Id$
*
* Copyright (C) 2003-2014 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 java.io.InputStream;
import java.io.PrintStream;

import javax.naming.NameNotFoundException;

import org.jnode.shell.alias.AliasManager;
import org.jnode.shell.alias.NoSuchAliasException;
import org.jnode.shell.io.CommandIO;
import org.jnode.shell.io.CommandInput;
import org.jnode.shell.io.CommandOutput;
import org.jnode.shell.syntax.Argument;
import org.jnode.shell.syntax.ArgumentBundle;
import org.jnode.vm.VmExit;

/**
* This a base class for JNode native command objects.  It provides default implementations
* of the 'execute' entry points, and other methods defined by the Command API.  It also
* provides 'getter' methods for retrieving the CommandLine and CommandIO stream
* objects, and an 'exit' method.
* <p>
* The class also provides some infrastructure that allows native commands to implement
* a lightweight 'public static void main(String[])' entry point.  This allows them to be
* executed by the minimalist 'DefaultCommandInvoker', and (in the future) will allow them
* to be run on a classic JVM.
*
* @author crawley@jnode.org
*/
public abstract class AbstractCommand implements Command {

    private ArgumentBundle bundle;
    private CommandLine commandLine;
    private CommandIO[] ios;

    /**
     * A child class that uses this constructor won't have an argument
     * bundle (in the first instant); see getArgumentBundle.
     */
    public AbstractCommand() {
        this.bundle = null;
    }

    /**
     * A child class that uses this constructor will have an initially
     * empty argument; see getArgumentBundle.
     */
    public AbstractCommand(String description) {
        this.bundle = new ArgumentBundle(description);
    }

    @SuppressWarnings("deprecation")
    public final void execute(String[] args) throws Exception {
        // The following code is needed to deal with the new-style syntax mechanism.
        // This will have created an instance of the command class and bound the
        // command-line arguments to it.  But the "static void main(...)" method that
        // presumably called us will have created another instance; i.e. this one.
        Command command = retrieveCurrentCommand();
        if (command != null) {
            // We'll ignore the instance created in the static void main method and
            // use the one that the invoker saved for us earlier.
        } else if (bundle != null) {
            // It appears that this class is designed to use the new-style syntax mechanism
            // but we've somehow been called without having a Command instance recorded.
            // We'll do our best to build a Command and parse the arguments based on the
            // information we have to hand.  (We have no way of knowing what alias was
            // supplied by the user.)
            command = prepareCommand(args);
        } else {
            // The command does not use the new syntax mechanism, so we can just run
            // it and let it deal with the arguments itself.
            command = this;
        }
        CommandIO[] myIOs = new CommandIO[] {
            new CommandInput(System.in),
            new CommandOutput(System.out),
            new CommandOutput(System.err)
        };
        command.initialize(new CommandLine(args), myIOs);
        command.execute();
    }

    /**
     * Attempt to create a command instance and parse the command line arguments.
     *
     * @param args the command arguments
     * @return the command with arguments bound by the parser.
     * @throws Exception
     */
    private Command prepareCommand(String[] args) throws Exception {
        String className = this.getClass().getCanonicalName();
        String commandName = getProbableAlias(className);
        if (commandName == null) {
            commandName = className;
        }
        CommandLine cmdLine = new CommandLine(commandName, args);
        // This will be problematic in a classic JVM ...
        CommandInfo ci = cmdLine.parseCommandLine((CommandShell) ShellUtils.getCurrentShell());
        return ci.getCommandInstance();
    }
   
    /**
     * Get our best guess as to what the alias was.
     *
     * @param canonicalName the class name
     * @return the intuited alias name
     * @throws NameNotFoundException
     */
    private String getProbableAlias(String canonicalName) throws NameNotFoundException {
        // This will be problematic in a classic JVM ...
        AliasManager mgr = ShellUtils.getAliasManager();
        for (String alias : mgr.aliases()) {
            try {
                if (mgr.getAliasClassName(alias).equals(canonicalName)) {
                    return alias;
                }
            } catch (NoSuchAliasException e) {
                // This can only occur if an alias is removed while we are working.
                // There's not much we can do about it ... so ignore this.
            }
        }
        return null;
    }

    /**
     * This method causes the command to 'exit' with the supplied return code.
     * This method will never return.  (The current implementation works by throwing
     * a subclass of 'java.lang.Error', so an application command must allow
     * 'Error' or 'Throwable' exceptions to propagate if it wants 'exit' to work.)
     *
     * @param rc the return code.
     */
    protected void exit(int rc) {
        throw new VmExit(rc);
    }

    /**
     * Get the bundle comprising the command's registered Arguments.  If
     * this method returns <code>null</null>, it indicates to the command
     * shell that this command does not use the (new) command syntax parser.
     */
    public final ArgumentBundle getArgumentBundle() {
        return bundle;
    }

    /**
     * A child command class should call this method to register Arguments
     * for use by the Syntax system.
     *
     * @param args Argument objects to be registered for use in command syntax
     * parsing and completion.
     */
    protected final void registerArguments(Argument<?> ... args) {
        if (bundle == null) {
            bundle = new ArgumentBundle();
        }
        for (Argument<?> arg : args) {
            bundle.addArgument(arg);
        }
    }
   
    /**
     * The default implementation of the 'execute()' entry point delegates to
     * the older 'execute(...)' entry point.  A new command class should override
     * this method.
     */
    @Override
    public void execute() throws Exception {
        execute(commandLine,
                ((CommandInput) ios[0]).getInputStream(),
                ((CommandOutput) ios[1]).getPrintStream(),
                ((CommandOutput) ios[2]).getPrintStream());
    }
   
    /**
     * The default implementation of the 'execute(...)' entry point complains that
     * you haven't implemented it.  A command class must override either this method
     * or (ideally) the 'execute()' entry point method.
     */
    public void execute(CommandLine commandLine, InputStream in, PrintStream out, PrintStream err)
        throws Exception {
        throw new UnsupportedOperationException(
                "A JNode command class MUST implement one of the 'execute(...)' methods.");
    }
   
    /**
     * Get the CommandLine object representing the command name and arguments
     * in String form.  This method should not be called from a Command constructor.
     *
     * @return the CommandLine object.
     */
    public final CommandLine getCommandLine() {
        if (commandLine == null) {
            throw new IllegalStateException("this.initialize(...) has not been called yet");
        }
        return commandLine;
    }

    /**
     * Get the CommandIO object representing the 'error' stream; i.e. fd '2'.
     * This method should not be called from a Command constructor.
     *
     * @return the CommandIO for the command's error stream.
     */
    public final CommandOutput getError() {
        if (ios == null) {
            throw new IllegalStateException("this.initialize(...) has not been called yet");
        }
        return (CommandOutput) ios[2];
    }

    /**
     * Get the CommandIO object representing the 'input' stream; i.e. fd '0'.
     * This method should not be called from a Command constructor.
     *
     * @return the CommandIO for the command's input stream.
     */
    public final CommandInput getInput() {
        if (ios == null) {
            throw new IllegalStateException("this.initialize(...) has not been called yet");
        }
        return (CommandInput) ios[0];
    }
   
    /**
     * Get the Command's stream indexed by the 'fd' number.
     * This method should not be called from a Command constructor.
     *
     * @param fd a non-negative 'file descriptor' number.
     * @return the requested CommandIO object.
     */
    public final CommandIO getIO(int fd) {
        if (ios == null) {
            throw new IllegalStateException("this.initialize(...) has not been called yet");
        }
        return ios[fd];
    }
   
    /**
     * Get the CommandIO object representing the 'output' stream; i.e. fd '1'.
     * This method should not be called from a Command constructor.
     *
     * @return the CommandIO for the command's output stream.
     */
    public final CommandOutput getOutput() {
        if (ios == null) {
            throw new IllegalStateException("this.initialize(...) has not been called yet");
        }
        return (CommandOutput) ios[1];
    }

    @Override
    public final void initialize(CommandLine commandLine, CommandIO[] ios) {
        // Check the init parameters ...
        commandLine.getLength();
        if (ios.length < 3) {
            throw new IllegalArgumentException("'ios' must have at least 3 elements");
        }
       
        this.commandLine = commandLine;
        this.ios = ios;
    }


    static ThreadLocal<Command> currentCommand = new ThreadLocal<Command>();

    static void saveCurrentCommand(Command command) {
        currentCommand.set(command);
    }

    static Command retrieveCurrentCommand() {
        Command res = currentCommand.get();
        currentCommand.set(null);
        return res;
    }

}
TOP

Related Classes of org.jnode.shell.AbstractCommand

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.