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

org.aesh.command.impl.internal.ProcessedCommand Maven / Gradle / Ivy

The newest version!
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * 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.aesh.command.impl.internal;

import org.aesh.command.Command;
import org.aesh.command.activator.CommandActivator;
import org.aesh.command.impl.activator.NullCommandActivator;
import org.aesh.command.impl.parser.CompleteStatus;
import org.aesh.command.impl.populator.AeshCommandPopulator;
import org.aesh.command.impl.result.NullResultHandler;
import org.aesh.command.invocation.CommandInvocation;
import org.aesh.command.invocation.InvocationProviders;
import org.aesh.command.parser.CommandLineParserException;
import org.aesh.command.parser.OptionParserException;
import org.aesh.command.populator.CommandPopulator;
import org.aesh.command.result.ResultHandler;
import org.aesh.command.validator.CommandValidator;
import org.aesh.readline.terminal.formatting.TerminalString;
import org.aesh.readline.util.Parser;
import org.aesh.selector.SelectorType;
import org.aesh.terminal.utils.Config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Ståle W. Pedersen
 */
public class ProcessedCommand, CI extends CommandInvocation> {

    private final String name;
    private final String description;
    private final CommandValidator validator;
    private final ResultHandler resultHandler;
    private final CommandPopulator populator;
    private final boolean disableParsing;
    private CommandActivator activator;
    private final boolean generateHelp;
    private String version;

    private List options;
    private ProcessedOption arguments;
    private ProcessedOption argument;
    private final C command;
    private final List aliases;
    private List parserExceptions;
    private CompleteStatus completeStatus;

    public ProcessedCommand(String name, List aliases, C command,
                            String description, CommandValidator validator,
                            ResultHandler resultHandler,
                            boolean generateHelp, boolean disableParsing,
                            String version,
                            ProcessedOption arguments, List options,
                            ProcessedOption argument,
                            CommandPopulator populator, CommandActivator activator) throws OptionParserException {
        this.name = name;
        this.description = description;
        this.aliases = aliases == null ? Collections.emptyList() : aliases;
        this.validator = validator;
        this.generateHelp = generateHelp;
        this.disableParsing = disableParsing;
        if(resultHandler != null)
            this.resultHandler = resultHandler;
        else
            this.resultHandler = new NullResultHandler();
        this.arguments = arguments;
        this.argument = argument;
        if(argument != null && arguments != null)
            throw new OptionParserException("Argument and Arguments cannot be defined in the same Command");
        this.options = new ArrayList<>();
        this.command = command;
        this.activator = activator == null ? new NullCommandActivator() : activator;
        if(populator == null)
            this.populator = new AeshCommandPopulator<>(this.command);
        else
            this.populator = populator;
        setOptions(options);

        if(generateHelp)
            doGenerateHelp();

        if(version != null && version.length() > 0) {
            this.version = version;
            doGenerateVersion();
        }

        parserExceptions = new ArrayList<>();
    }

    public List getOptions() {
        return options;
    }

    public CommandActivator getActivator() {
        return activator;
    }

    public List getAliases() {
        return aliases;
    }

    public void addOption(ProcessedOption opt) throws OptionParserException {
        this.options.add(new ProcessedOption(verifyThatNamesAreUnique(opt.shortName(), opt.name()), opt.name(),
                opt.description(), opt.getArgument(), opt.isRequired(), opt.getValueSeparator(), opt.askIfNotSet(), opt.selectorType(),
                opt.getDefaultValues(), opt.type(), opt.getFieldName(), opt.getOptionType(), opt.converter(),
                opt.completer(), opt.validator(), opt.activator(), opt.getRenderer(), opt.parser(), opt.doOverrideRequired()));

        options.get(options.size()-1).setParent(this);
    }

    private void setOptions(List options) throws OptionParserException {
        for(ProcessedOption opt : options) {
            this.options.add(new ProcessedOption(verifyThatNamesAreUnique(opt.shortName(), opt.name()), opt.name(),
                    opt.description(), opt.getArgument(), opt.isRequired(), opt.getValueSeparator(), opt.askIfNotSet(), opt.selectorType(),
                    opt.getDefaultValues(), opt.type(), opt.getFieldName(), opt.getOptionType(),
                    opt.converter(), opt.completer(), opt.validator(), opt.activator(), opt.getRenderer(),
                    opt.parser(), opt.doOverrideRequired()));

            this.options.get(this.options.size()-1).setParent(this);
        }
    }

    public String name() {
        return name;
    }

    public String description() {
        return description;
    }

    public CommandValidator validator() {
        return validator;
    }

    public ResultHandler resultHandler() {
      return resultHandler;
    }

    public boolean hasArguments() {
        return arguments != null && arguments.hasMultipleValues();
    }

    public ProcessedOption getArguments() {
        return arguments;
    }

    public void setArguments(ProcessedOption arguments) {
        this.arguments = arguments;
        this.arguments.setParent(this);
    }

    public CommandPopulator getCommandPopulator() {
        return populator;
    }

    public C getCommand() {
        return command;
    }

    public boolean generateHelp() {
        return generateHelp;
    }

    public boolean disableParsing() {
        return disableParsing;
    }

    public String version() {
        return version;
    }

    private char verifyThatNamesAreUnique(String name, String longName) throws OptionParserException {
        if(name != null)
            return verifyThatNamesAreUnique(name.charAt(0), longName);
        else
            return verifyThatNamesAreUnique('\u0000', longName);
    }

    private char verifyThatNamesAreUnique(char name, String longName) throws OptionParserException {
        if(longName != null && longName.length() > 0 && findLongOption(longName) != null) {
            throw new OptionParserException("Option --"+longName+" is already added to Param: "+this.toString());
        }
        if(name != '\u0000' && findOption(String.valueOf(name)) != null) {
            throw new OptionParserException("Option -"+name+" is already added to Param: "+this.toString());
        }

        //if name is null, use one based on name
        if(name == '\u0000' && (longName == null || longName.length() == 0))
            throw new OptionParserException("Neither option name and option long name can be both null");

        return name;
    }

    private char findPossibleName(String longName) throws OptionParserException {
        for(int i=0; i < longName.length(); i++) {
            if(findOption(String.valueOf(longName.charAt(i))) == null)
                return longName.charAt(i);
        }
        //all chars are taken
        throw new OptionParserException("All option names are taken, please specify a unique name");
    }

    public ProcessedOption findOption(String name) {
        for (ProcessedOption option : getOptions())
            if(option.shortName() != null &&
                    option.shortName().equals(name) &&
                    option.activator().isActivated(new ParsedCommand(this)))
                return option;

        return null;
    }

    public ProcessedOption findOptionNoActivatorCheck(String name) {
        for (ProcessedOption option : getOptions())
            if(option.shortName() != null &&
                    option.shortName().equals(name))
                return option;

        return null;
    }

    /**
     * Generic search for matching options with input that start with either -- or -
     *
     * @param input input
     * @return matching option
     */
    public ProcessedOption searchAllOptions(String input) {
        if (input.startsWith("--")) {
            ProcessedOption currentOption = findLongOptionNoActivatorCheck(input.substring(2));
            if(currentOption == null && input.contains("="))
                currentOption = startWithLongOptionNoActivatorCheck(input.substring(2));
            if (currentOption != null)
                currentOption.setLongNameUsed(true);
            //need to handle spaces in option names
            else if(Parser.containsNonEscapedSpace(input)) {
                return searchAllOptions(Parser.switchSpacesToEscapedSpacesInWord(input));
            }

            return currentOption;
        }
        else if (input.startsWith("-")) {
            ProcessedOption currentOption = findOption(input.substring(1));
            if(currentOption == null)
                currentOption = startWithOption(input.substring(1));

            if (currentOption != null)
                currentOption.setLongNameUsed(false);

            return currentOption;
        }
        else {
            return null;
        }
    }

    public ProcessedOption findLongOption(String name) {
        for (ProcessedOption option : getOptions())
            if(option.name() != null &&
                    option.name().equals(name) &&
                    option.activator().isActivated(new ParsedCommand(this)))
                return option;

        return null;
    }

    public ProcessedOption findLongOptionNoActivatorCheck(String name) {
        for (ProcessedOption option : getOptions())
            if(option.name() != null && option.name().equals(name))
                return option;

        return null;
    }

    public ProcessedOption startWithOption(String name) {
        for (ProcessedOption option : getOptions())
            if(option.shortName() != null && name.startsWith(option.shortName()) &&
                    option.activator().isActivated(new ParsedCommand(this)))
                return option;

        return null;
    }

    public ProcessedOption startWithLongOption(String name) {
        for (ProcessedOption option : getOptions())
            if(name.startsWith(option.name()) &&
                    option.activator().isActivated(new ParsedCommand(this)))
                return option;

        return null;
    }

    public ProcessedOption startWithLongOptionNoActivatorCheck(String name) {
        ProcessedOption longestMatch = null;
        for (ProcessedOption option : getOptions()) {
            if (name.startsWith(option.name())) {
                if (longestMatch == null) {
                    longestMatch = option;
                } else if (option.name().length() > longestMatch.name().length()) {
                    longestMatch = option;
                }
            }
        }
        return longestMatch;
    }

   public void clear() {
       clearOptions();
       if(arguments != null)
           arguments.clear();
       if(argument != null)
           argument.clear();

       parserExceptions.clear();
       completeStatus = null;
    }

    protected void clearOptions() {
        for (ProcessedOption processedOption : getOptions()) {
            processedOption.clear();
        }
    }

    private void doGenerateHelp() {
        //only generate a help option if there is no other option already called help
        if(findOption("help") == null) {
            try {
                ProcessedOption helpOption = ProcessedOptionBuilder
                        .builder()
                        .name("help")
                        .shortName('h')
                        .description("Displays information of the command and all options")
                        .hasValue(false)
                        .required(false)
                        .optionType(OptionType.BOOLEAN)
                        .type(Boolean.class)
                        .overrideRequired(true)
                        .fieldName("generatedHelp")
                        .build();

                options.add(helpOption);
            }
            catch (OptionParserException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isGenerateHelpOptionSet() {
       ProcessedOption helpOption = findLongOptionNoActivatorCheck("help");
        return helpOption != null && helpOption.getValue() != null;
    }

    private void doGenerateVersion() {
        //only generate a version option if there is no other option already called version
        if(findOption("version") == null) {
            try {
                ProcessedOption versionOption = ProcessedOptionBuilder
                        .builder()
                        .name("version")
                        .shortName('v')
                        .description("Displays version information of the command")
                        .hasValue(false)
                        .required(false)
                        .optionType(OptionType.BOOLEAN)
                        .type(Boolean.class)
                        .overrideRequired(true)
                        .fieldName("generatedVersion")
                        .build();

                options.add(versionOption);
            }
            catch (OptionParserException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isGenerateVersionOptionSet() {
        ProcessedOption versionOption = findLongOptionNoActivatorCheck("version");
        return versionOption != null && versionOption.getValue() != null;
    }

    /**
     * Return all option names that not already have a value
     * and is enabled
     */
    public List getOptionLongNamesWithDash() {
        List opts = getOptions();
        List names = new ArrayList<>(opts.size());
        for (ProcessedOption o : opts) {
            if(o.getValues().size() == 0 &&
                    o.activator().isActivated(new ParsedCommand(this)))
                names.add(o.getRenderedNameWithDashes());
        }

        return names;
    }

    public List findPossibleLongNamesWithDash(String name) {
        List opts = getOptions();
        List names = new ArrayList<>(opts.size());
        for (ProcessedOption o : opts) {
           if(((o.shortName() != null && o.shortName().equals(name) &&
                   !o.isLongNameUsed() && o.getValues().size() == 0) ||
                   (o.name().startsWith(name) && o.getValues().size() == 0)) &&
                   o.activator().isActivated(new ParsedCommand(this)))
               names.add(o.getRenderedNameWithDashes());
        }
        return names;
    }

    public List findPossibleLongNames(String name) {
        if(name.startsWith("--"))
            name = name.substring(2);
        List opts = getOptions();
        List names = new ArrayList<>(opts.size());
        for (ProcessedOption o : opts) {
           if(((o.shortName() != null && o.shortName().equals(name) &&
                   !o.isLongNameUsed() && o.getValues().size() == 0) ||
                   (o.name().startsWith(name) && o.getValues().size() == 0)) &&
                   o.activator().isActivated(new ParsedCommand(this)))
               names.add(o.name());
        }
        return names;
    }

    /**
     * @return returns true if the command has any options with askIfNotSet to true
     * and its value is not set.
     */
    public boolean hasAskIfNotSet() {
        for(ProcessedOption opt : getOptions()) {
            if(opt.askIfNotSet() && opt.hasValue() && opt.getValues().isEmpty() && !opt.hasDefaultValue())
                return true;
        }
        return false;
    }

    public List getAllAskIfNotSet() {
       List  options = new ArrayList<>();
        for(ProcessedOption opt : getOptions()) {
            if(opt.askIfNotSet() && opt.hasValue() && opt.getValues().isEmpty() && !opt.hasDefaultValue())
                options.add(opt);
        }
        if(argument != null && argument.askIfNotSet() && argument.hasValue() && argument.getValues().isEmpty() && !argument.hasDefaultValue())
            options.add(argument);
        if(arguments != null && arguments.askIfNotSet() && arguments.hasValue() && arguments.getValues().isEmpty() && !arguments.hasDefaultValue())
            options.add(arguments);

        return options;
    }

    /**
     * Returns a description String based on the defined command and options.
     * Useful when printing "help" info etc.
     *
     */
    public String printHelp(String commandName) {
        int maxLength = 0;
        int width = 80;
        List opts = getOptions();
        for (ProcessedOption o : opts) {
            if(o.getFormattedLength() > maxLength)
                maxLength = o.getFormattedLength();
        }

        StringBuilder sb = new StringBuilder();
        //first line
        sb.append("Usage: ");
        if(commandName == null || commandName.length() == 0)
            sb.append(name());
        else
            sb.append(commandName);
        if(opts.size() > 0)
            sb.append(" []");

        if(argument != null) {
            if(argument.isTypeAssignableByResourcesOrFile())
                sb.append(" ");
            else
                sb.append(" <").append(argument.getFieldName()).append(">");
        }

        if(arguments != null) {
            if(arguments.isTypeAssignableByResourcesOrFile())
                sb.append(" []");
            else
                sb.append(" [<").append(arguments.getFieldName()).append(">]");
        }
        sb.append(Config.getLineSeparator());
        //second line
        sb.append(description()).append(Config.getLineSeparator());

        //options and arguments
        if (opts.size() > 0)
           sb.append(Config.getLineSeparator()).append("Options:").append(Config.getLineSeparator());
        for (ProcessedOption o : opts)
            sb.append(o.getFormattedOption(2, maxLength+4, width)).append(Config.getLineSeparator());
        if(arguments != null) {
            sb.append(Config.getLineSeparator()).append("Arguments:").append(Config.getLineSeparator());
            sb.append(arguments.getFormattedOption(2, maxLength+4, width)).append(Config.getLineSeparator());
        }
        if(argument != null) {
            sb.append(Config.getLineSeparator()).append("Argument:").append(Config.getLineSeparator());
            sb.append(argument.getFormattedOption(2, maxLength+4, width)).append(Config.getLineSeparator());
        }
        return sb.toString();
    }

    @Override
    public String toString() {
        return "ProcessedCommand{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
 ", options=" + getOptions()
                +                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ProcessedCommand)) return false;

        ProcessedCommand that = (ProcessedCommand) o;

        if (!name.equals(that.name)) return false;
        if (description != null ? !description.equals(that.description) : that.description != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + (description != null ? description.hashCode() : 0);
        return result;
    }

    public boolean anyOptionsSet() {
        for(ProcessedOption o : getOptions()) {
            if (o.hasValue() && o.getValue() != null || !o.hasValue() && o.getValue() != null)
                return true;
        }
        if(hasArgument() && argument.getValue() != null)
            return true;
        if(hasArguments() && arguments.getValue() != null)
            return true;

        return false;
    }

    public boolean hasLongOption(String optionName) {
        for(ProcessedOption o : getOptions()) {
            if (o.name().equals(optionName))
                return true;
        }
        return false;
    }

    public boolean hasOptions() {
        List opts = getOptions();
        return opts != null && opts.size() > 0;
    }

    //will only return true if the optionName equals an option and it does
    //not start with another option name
    public boolean hasUniqueLongOption(String optionName) {
        if(hasLongOption(optionName)) {
            for(ProcessedOption o : getOptions()) {
                if(o.name().startsWith(optionName) && !o.name().equals(optionName))
                    return false;
            }
            return true;
        }
        return false;
    }

    public void updateInvocationProviders(InvocationProviders invocationProviders) {
        updateOptionsInvocationProviders(invocationProviders);
        if (argument != null) {
            argument.updateInvocationProviders(invocationProviders);
        }
        if (arguments != null) {
            arguments.updateInvocationProviders(invocationProviders);
        }
        activator = invocationProviders.getCommandActivatorProvider().enhanceCommandActivator(activator);
    }

    protected void updateOptionsInvocationProviders(InvocationProviders invocationProviders) {
        for (ProcessedOption option : getOptions()) {
            option.updateInvocationProviders(invocationProviders);
        }
    }

    public void addParserException(CommandLineParserException exception) {
        parserExceptions.add(exception);
    }

    public List parserExceptions() {
        return parserExceptions;
    }

    public boolean hasOptionsWithInjectedValues() {
        for(ProcessedOption option : options)
            if(option.getValue() != null)
                return true;
        return false;
    }

    public boolean hasOptionWithOverrideRequired() {
        for(ProcessedOption option : options) {
            if(option.getValue() != null && option.doOverrideRequired())
                return true;
        }
        return false;
    }

    public CompleteStatus completeStatus() {
        return completeStatus;
    }

    public void setCompleteStatus(CompleteStatus completeStatus) {
        this.completeStatus = completeStatus;
    }

    public void setArgument(ProcessedOption arg) {
        this.argument = arg;
        this.argument.setParent(this);
    }

    public ProcessedOption getArgument() {
        return argument;
    }

    public boolean hasArgument() {
        return argument != null;
    }

    public boolean hasArgumentWithNoValue() {
        return argument != null && argument.getValue() == null;
    }

    public boolean hasArgumentsWithNoValue() {
        return arguments != null && arguments.getValue() == null;
    }

    public boolean hasSelector() {
        boolean selector = false;
        for(ProcessedOption opt : getOptions()) {
            // if we have an option that's marked with override required and is set
            // it should override selector
            if(opt.doOverrideRequired() && opt.getValue() != null)
                return false;
            if(opt.selectorType() != SelectorType.NO_OP && opt.hasValue())
                selector = true;
        }
        return selector;
     }

    public List getAllSelectors() {
       List  options = new ArrayList<>();
        for(ProcessedOption opt : getOptions()) {
            if(opt.selectorType() != SelectorType.NO_OP && opt.hasValue() && opt.getValue() == null)
                options.add(opt);
        }
        if(argument != null && argument.selectorType() != SelectorType.NO_OP &&
                   (argument.hasValue() || argument.getOptionType().equals(OptionType.BOOLEAN)))
            options.add(argument);
        if(arguments != null && arguments.selectorType() != SelectorType.NO_OP && arguments.hasValue())
            options.add(arguments);

        return options;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy