nl.toolforge.karma.core.cmd.CommandFactory.java Source code

Java tutorial

Introduction

Here is the source code for nl.toolforge.karma.core.cmd.CommandFactory.java

Source

/*
Karma core - Core of the Karma application
Copyright (C) 2004  Toolforge <www.toolforge.nl>
    
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package nl.toolforge.karma.core.cmd;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import net.sf.sillyexceptions.OutOfTheBlueException;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import nl.toolforge.karma.core.KarmaRuntimeException;

/**
 * This factory is the single resource of Command objects. <code>KarmaRuntimeException</code>s are thrown when
 * something fails.
 *
 * @author W.H. Schraal
 * @version $Id$
 */
public final class CommandFactory {

    // Singleton
    //
    private static CommandFactory factory = null;

    private static final Log logger = LogFactory.getLog(CommandFactory.class);

    // Reference ro all command names
    //
    private static Set commandNames = null;

    private static Map commandsByName = null;
    private static Map commandsByAlias = null;

    /**
     * Only static methods
     */
    private CommandFactory() throws CommandLoadException {
        init();
    }

    private synchronized void init() throws CommandLoadException {

        CommandDescriptorMap descriptors = CommandLoader.getInstance().load();

        commandsByName = new TreeMap();
        commandsByAlias = new TreeMap();
        commandNames = new HashSet();

        // Store all commands by name in a hash
        // Create a set of all command names.
        //
        for (Iterator i = descriptors.values().iterator(); i.hasNext();) {
            CommandDescriptor descriptor = (CommandDescriptor) i.next();

            commandsByName.put(descriptor.getName(), descriptor);
            commandNames.add(descriptor.getName());

            for (Iterator j = descriptor.getAliasList().iterator(); j.hasNext();) {
                String alias = (String) j.next();
                commandsByAlias.put(alias, descriptor);
                commandNames.add(alias);
            }
        }
    }

    /**
     * Gets the singleton <code>CommandFactory</code>.
     *
     * @return The singleton <code>CommandFactory</code> instance.
     */
    public static CommandFactory getInstance() throws CommandLoadException {
        if (factory == null) {
            factory = new CommandFactory();
        }
        return factory;
    }

    /**
     * Retrieves the correct <code>Command</code>-instance, by looking up the implementation class through
     * <code>commandLine</code>.
     *
     * @param commandLineString Command arguments (e.g. <code>select-manifest -m karma-1.0</code>).
     * @return The implementation of a <code>Command</code> object.
     * @throws CommandException When a correct command could not be constructed. See
     *                          {@link CommandException#INVALID_COMMAND}.
     */
    public Command getCommand(String commandLineString) throws CommandException {
        logger.debug("getCommand - commandLineString: " + commandLineString);

        String commandName = null;
        commandLineString = commandLineString.trim();

        if (commandLineString.indexOf(' ') > 0) {
            commandName = commandLineString.substring(0, commandLineString.indexOf(' '));
        } else {
            commandName = commandLineString;
        }

        char[] chars = commandLineString.substring(commandName.length()).toCharArray();

        List commandOptionsList = new ArrayList();

        int j = 0;
        String part = null;

        while (j < chars.length) {

            // Go to the next part while we are a 'space' (chr(32))
            //
            while (chars[j] == ' ') {
                j++;
            }

            part = "";
            if (chars[j] != '"') {
                // Start of options or 'normal' arguments.
                //
                part += chars[j];
                j++;

                while ((j < chars.length) && (chars[j] != ' ') && (chars[j] != '\"')) {
                    // Continue until a space or \" is reached.
                    //
                    part += chars[j];
                    j++;
                }

            } else if (chars[j] == '"') {
                // Begin van een argument dat bestaat uit een block data, gedemarkeerd door dubbele quotes, escaped dubbel
                // quote wordt eruit gevist.
                //
                part += chars[j];
                j++;

                while (j < chars.length) {

                    if (chars[j] == '"') {
                        if (chars[j - 1] != '\\') {
                            // End of demarkated piece of text.
                            //
                            j++;
                            break;
                        }
                    }

                    // doorlopen totdat een '"' is bereikt, behalve \" (escaped).
                    part += chars[j];
                    j++;
                }
                // We have reached a '"', which must be added.
                //
                part += '"';
                j++;
            }

            if (part.startsWith("\"") && part.endsWith("\"")) {
                part = part.substring(1, part.length() - 1).trim();
            }

            commandOptionsList.add(part);
        }
        logger.debug("getCommand - commandOptionsList" + commandOptionsList);

        String[] commandOptions = (String[]) commandOptionsList.toArray(new String[commandOptionsList.size()]);

        return getCommand(commandName, commandOptions);
    }

    private Command getCommand(String commandName, String[] arguments) throws CommandException {

        Command cmd = null;

        if (isCommand(commandName)) {

            CommandDescriptor descriptor = null;
            try {
                descriptor = getCommandDescriptor(commandName);

                // Construct the command implementation, with the default constructor
                //
                Class implementingClass = null;
                try {
                    implementingClass = Class.forName(descriptor.getClassName());
                } catch (ClassNotFoundException c) {
                    throw new CommandException(CommandException.NO_IMPLEMENTING_CLASS,
                            new Object[] { descriptor.getClassName() });
                }

                Constructor defaultConstructor = implementingClass
                        .getConstructor(new Class[] { CommandDescriptor.class });
                cmd = (Command) defaultConstructor.newInstance(new Object[] { descriptor });

                // Parse the command line options.
                //
                CommandLineParser parser = new PosixParser();

                Options parserOptions = descriptor.getOptions();

                //        if (parserOptions.getOptions().size() == 0 && arguments.length > 0) {
                //          // The issue is that this is 1. not an exception and 2. no mechanism to propagate this back in a nice way.
                //          throw new CommandException(CommandException.NO_OPTIONS_REQUIRED);
                //        }

                cmd.setCommandLine(parser.parse(parserOptions, arguments));

            } catch (NoSuchMethodException e) {
                throw new KarmaRuntimeException(e.getLocalizedMessage(), e);
            } catch (SecurityException e) {
                throw new KarmaRuntimeException(e.getLocalizedMessage(), e);
            } catch (InstantiationException e) {
                throw new KarmaRuntimeException(e.getLocalizedMessage(), e);
            } catch (IllegalAccessException e) {
                throw new KarmaRuntimeException(e.getLocalizedMessage(), e);
            } catch (IllegalArgumentException e) {
                throw new KarmaRuntimeException(e.getLocalizedMessage(), e);
            } catch (InvocationTargetException e) {
                throw new KarmaRuntimeException(e.getLocalizedMessage(), e);
            } catch (ParseException e) {
                if (e instanceof MissingOptionException) {
                    throw new CommandException(e, CommandException.MISSING_OPTION, new Object[] { e.getMessage() });
                }
                if (e instanceof UnrecognizedOptionException) {
                    throw new CommandException(e, CommandException.INVALID_OPTION, new Object[] { arguments });
                }
                if (e instanceof MissingArgumentException) {
                    throw new CommandException(e, CommandException.MISSING_ARGUMENT,
                            new Object[] { e.getMessage() });
                }
            }
            return cmd;
        }
        // At this point, we have no command
        //
        throw new CommandException(CommandException.UNKNOWN_COMMAND, new Object[] { commandName });
    }

    /**
     * Checks if some string is a command within this context.
     *
     * @param name
     * @return
     */
    private boolean isCommand(String name) {
        return commandNames.contains(name);
    }

    public Collection getCommands() {
        return commandsByName.values();
    }

    /**
     * Retrieves the correct command descriptor either by name or by alias (whichever is passed as a
     * parameter). Returns <code>null</code> if the descriptor could not be found.
     */
    public CommandDescriptor getCommandDescriptor(String commandId) {

        try {
            init(); //this fixes the bug where the previous value of an option is returned
        } catch (CommandLoadException cle) {
            //this can not happen, since the init has already been called in the constructor
            //and it was successful then.
            throw new OutOfTheBlueException(
                    "Tried to reload the commands, but failed. This is strange because they have been loaded earlier with success",
                    cle);
        }

        if (commandsByName.containsKey(commandId)) {
            return (CommandDescriptor) commandsByName.get(commandId);
        } else {
            if (commandsByAlias.containsKey(commandId)) {
                return (CommandDescriptor) commandsByAlias.get(commandId);
            }
        }
        return null;
    }

}