All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.deephacks.tools4j.cli.CliMain Maven / Gradle / Ivy

/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.deephacks.tools4j.cli;

import static org.deephacks.tools4j.cli.CliEvents.CLI001_SUCCESS;
import static org.deephacks.tools4j.cli.CliEvents.CLI002_HELP_COMMAND;
import static org.deephacks.tools4j.cli.CliEvents.CLI003_HELP_COMMANDS;
import static org.deephacks.tools4j.cli.CliEvents.CLI108_DUPLICATE_COMMANDS;
import static org.deephacks.tools4j.cli.CliEvents.CLI109_COMMAND_DOES_NOT_EXIST;
import static org.deephacks.tools4j.cli.CliEvents.CLI110_CONF_DOES_NOT_EXIST;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.deephacks.tools4j.log.LogConfiguration;
import org.deephacks.tools4j.support.event.AbortRuntimeException;
import org.deephacks.tools4j.support.event.Event;
import org.deephacks.tools4j.support.lookup.Lookup;
import org.deephacks.tools4j.support.reflections.BeanAnnotatedField;
import org.deephacks.tools4j.support.reflections.BeanInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;

/**
 * Main class that is called by the bootstrap when classpath have been initalized and it is time to start 
 * executing.  
 * 
 * @author Kristoffer Sjogren 
 */
class CliMain {
    static final String CONF_DIR = "conf";
    static final String CONF_FILE = "conf.properties";
    private Collection> availableCliCommands = new ArrayList>();
    private BeanInstance cliCommand;
    private String terminalArgs[];
    private Logger logger = null;
    private AbortRuntimeException constructorEx;

    public CliMain(String terminalArgs[]) {
        if (terminalArgs == null) {
            terminalArgs = new String[0];
        }
        try {
            LogConfiguration.init(getConfDir());
        } catch (AbortRuntimeException e) {
            // cannot throw yet, constructor cannot return the event.
            constructorEx = e;
        }

        this.terminalArgs = terminalArgs;

        if (GNUishParser.parseReservedOnly(terminalArgs).debug()) {
            LogConfiguration.setDebug();
        }

        this.logger = LoggerFactory.getLogger(CliMain.class);
        availableCliCommands = getLookups();

    }

    public Event run() {
        if (terminalArgs == null || terminalArgs.length == 0) {
            // no command provided, show what commands are available.
            printAvailableCommandsHelp();
            return CLI003_HELP_COMMANDS();
        }
        String command = terminalArgs[0];
        // strip command from args after parsing
        String[] remainingArgs = stripFirst(terminalArgs);
        if (command == null || "".equals(command)) {
            printAvailableCommandsHelp();
            return CLI003_HELP_COMMANDS();
        }
        try {
            if (constructorEx != null) {
                throw constructorEx;
            }
            validateUniqueness();
            cliCommand = getCommand(command);
            List nonArgumentedOptions = getNonArgumentedOptions(cliCommand);
            GNUishParser p = GNUishParser.parse(remainingArgs, nonArgumentedOptions);
            if (p.help() && cliCommand != null) {
                printCommandHelp();
                return CLI002_HELP_COMMAND(command);
            }
            CliExecutionContext ctx = new CliExecutionContext(p, cliCommand);
            ctx.execute();
        } catch (AbortRuntimeException e) {
            System.out.println(e.getMessage());
            logger.debug(e.getMessage(), e);
            return e.getEvent();
        } catch (Throwable e) {
            Event code = CliEvents.CLI004_UNEXPECTED_EXCEPTION(e);
            System.out.println(code.getMessage());
            logger.debug(e.getMessage(), e);
            return code;
        }
        return CLI001_SUCCESS();
    }

    BeanInstance getCommand(String command) {
        for (BeanInstance aCommand : availableCliCommands) {
            CliExtension extension = aCommand.getClassAnnotation(CliExtension.class);
            if (command.equals(extension.keyword())) {
                cliCommand = aCommand;
            }
        }
        if (cliCommand == null) {
            throw CLI109_COMMAND_DOES_NOT_EXIST(command);
        }
        return cliCommand;
    }

    private Collection> getLookups() {
        Collection lookups = Lookup.get().lookupAll(CliCommand.class);
        logger.debug("CliCommands are {}", lookups);
        return BeanInstance.of(lookups);

    }

    private void validateUniqueness() {
        Map> availableCommands = Maps.newHashMap();
        for (BeanInstance command : availableCliCommands) {
            CliExtension ext = command.getClassAnnotation(CliExtension.class);
            if (availableCommands.get(ext.keyword()) != null) {
                throw CLI108_DUPLICATE_COMMANDS(ext.keyword());
            }
            availableCommands.put(ext.keyword(), command);
        }
    }

    /**
     * We need to know which options are argumented and which ones are not.
     * The only field considered non-argumented is java.lang.Boolean.
     * 
     * --verbose, -v, --debug and -d, --help, -h are reserved non-argumented options.
     * 
     * @return
     */
    public List getNonArgumentedOptions(BeanInstance target) {
        List nonArgumentedOptions = new ArrayList();
        for (BeanAnnotatedField opt : target.findFieldsAnnotatedWith(CliOption.class)
                .values()) {
            if ("java.lang.Boolean".equals(opt.getTypeString())) {
                nonArgumentedOptions.add(opt.getAnnotation().shortName());
                nonArgumentedOptions.add(opt.getAnnotation().name());
            }
        }
        nonArgumentedOptions.addAll(GNUishParser.getReservedNonArgumentOptions());
        return nonArgumentedOptions;
    }

    public final static File getConfDir() {
        File file = new File(Bootstrap.getHomeDir(), CONF_DIR);
        try {
            if (!file.exists()) {
                throw new FileNotFoundException(file.getAbsolutePath());
            }
            return file.getCanonicalFile();
        } catch (FileNotFoundException e) {
            throw CLI110_CONF_DOES_NOT_EXIST(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String[] stripFirst(String[] args) {
        String[] argz = new String[args.length - 1];
        System.arraycopy(args, 1, argz, 0, args.length - 1);
        return argz;
    }

    private void printAvailableCommandsHelp() {
        StringBuffer sb = new StringBuffer();
        sb.append("Available commands are: ");
        for (BeanInstance cliCommand : availableCliCommands) {
            CliExtension ext = cliCommand.getClassAnnotation(CliExtension.class);
            sb.append("  " + ext.keyword() + " - " + ext.usage());
        }
        System.out.println(sb.toString());
    }

    private void printCommandHelp() {
        CliExtension cliExtension = cliCommand.getClassAnnotation(CliExtension.class);
        StringBuffer sb = new StringBuffer();
        Map> arguments = cliCommand
                .findFieldsAnnotatedWith(CliArgument.class);

        Map> sortedArguments = new HashMap>();
        for (BeanAnnotatedField argument : arguments.values()) {
            sortedArguments.put(argument.getAnnotation().position(), argument);
        }

        StringBuffer args = new StringBuffer();
        for (int i = 0; i < sortedArguments.size(); i++) {
            BeanAnnotatedField a = sortedArguments.get(i);

            args.append(a.getAnnotation().name()).append(" ");
        }

        sb.append("usage: ").append(cliExtension.keyword())
                .append(" [OPTIONS...] " + args + "\n\n");
        sb.append("  ").append(cliExtension.usage()).append("\n\n");

        sb.append("ARGUMENTS\n");
        sb.append("\n");
        for (int i = 0; i < sortedArguments.size(); i++) {
            CliArgument arg = sortedArguments.get(i).getAnnotation();
            sb.append(" ").append(arg.name());
            sb.append(" : ").append(arg.desc());
            sb.append("\n");
        }
        sb.append("\n");
        sb.append("OPTIONS\n");
        Map> options = cliCommand
                .findFieldsAnnotatedWith(CliOption.class);
        for (BeanAnnotatedField option : options.values()) {
            CliOption opt = option.getAnnotation();
            sb.append(" ");
            sb.append("-").append(opt.shortName()).append(",");
            sb.append("--").append(opt.name()).append(" ");
            sb.append("<").append(CliExtensionValidator.getInputFormatString(option.getType()))
                    .append(">");
            sb.append(" : ").append(opt.desc());
            sb.append("\n");
        }

        System.out.println(sb.toString());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy