org.apache.geode.management.internal.cli.GfshParser.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.geode.management.internal.cli.GfshParser.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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.apache.geode.management.internal.cli;

import org.apache.commons.lang.StringUtils;
import org.apache.geode.management.cli.CommandProcessingException;
import org.apache.geode.management.internal.cli.exceptions.CliCommandMultiModeOptionException;
import org.apache.geode.management.internal.cli.exceptions.CliCommandOptionException;
import org.apache.geode.management.internal.cli.exceptions.CliException;
import org.apache.geode.management.internal.cli.exceptions.ExceptionHandler;
import org.apache.geode.management.internal.cli.help.format.NewHelp;
import org.apache.geode.management.internal.cli.help.utils.HelpUtils;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
import org.apache.geode.management.internal.cli.modes.CommandModes;
import org.apache.geode.management.internal.cli.modes.CommandModes.CommandMode;
import org.apache.geode.management.internal.cli.parser.Argument;
import org.apache.geode.management.internal.cli.parser.AvailabilityTarget;
import org.apache.geode.management.internal.cli.parser.CommandTarget;
import org.apache.geode.management.internal.cli.parser.GfshMethodTarget;
import org.apache.geode.management.internal.cli.parser.MethodParameter;
import org.apache.geode.management.internal.cli.parser.Option;
import org.apache.geode.management.internal.cli.parser.OptionSet;
import org.apache.geode.management.internal.cli.parser.Parameter;
import org.apache.geode.management.internal.cli.parser.ParserUtils;
import org.apache.geode.management.internal.cli.parser.SyntaxConstants;
import org.apache.geode.management.internal.cli.parser.preprocessor.PreprocessorUtils;
import org.apache.geode.management.internal.cli.parser.preprocessor.TrimmedInput;
import org.apache.geode.management.internal.cli.shell.Gfsh;
import org.apache.geode.management.internal.cli.util.CLIConsoleBufferUtil;
import org.springframework.shell.core.AbstractShell;
import org.springframework.shell.core.Completion;
import org.springframework.shell.core.Converter;
import org.springframework.shell.core.MethodTarget;
import org.springframework.shell.core.Parser;
import org.springframework.shell.event.ParseResult;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
 * Implementation of the {@link Parser} interface for GemFire SHell (gfsh) requirements.
 *
 * @since GemFire 7.0
 */
public class GfshParser implements Parser {

    public static final String LINE_SEPARATOR = System.getProperty("line.separator");

    // Constants used while finding command targets for help
    private final static Short EXACT_TARGET = (short) 0;
    private final static Short MATCHING_TARGETS = (short) 1;

    // Make use of LogWrapper
    private static final LogWrapper logWrapper = LogWrapper.getInstance();

    // private CliStringResourceBundle cliStringBundle;
    private CommandManager commandManager;

    /**
     * Used for warning messages
     */
    // TODO Investigating using GemFire logging.
    private final Logger consoleLogger;

    public GfshParser(CommandManager commandManager) {
        // cliStringBundle = new
        // CliStringResourceBundle("org/apache/geode/management/internal/cli/i18n/CliStringResourceBundle");
        this.commandManager = commandManager;
        if (CliUtil.isGfshVM()) {
            consoleLogger = Logger.getLogger(this.getClass().getCanonicalName());
        } else {
            consoleLogger = logWrapper.getLogger();
        }
    }

    // ///////////////// Parser interface Methods Start //////////////////////////
    // ////////////////////// Implemented Methods ////////////////////////////////

    /**
     * Populates a list of completion candidates. See {@link Parser#complete(String, int, List)} for
     * details.
     *
     * @param buffer
     * @param cursor
     * @param completionCandidates
     *
     * @return new cursor position
     */
    public int complete(String buffer, int cursor, List<String> completionCandidates) {
        final List<Completion> candidates = new ArrayList<Completion>();
        final int result = completeAdvanced(buffer, cursor, candidates);
        for (final Completion completion : candidates) {
            completionCandidates.add(completion.getValue());
        }
        return result;
    }

    /**
     * Populates a list of completion candidates.
     *
     * @param buffer
     * @param cursor
     * @param completionCandidates
     *
     * @return new cursor position
     */
    public int completeAdvanced(String buffer, int cursor, List<Completion> completionCandidates) {
        // Currently, support for auto-completion
        // in between is not supported, only if the
        // cursor is at the end

        if (cursor <= buffer.length() - 1 && !PreprocessorUtils.containsOnlyWhiteSpaces(buffer.substring(cursor))
                || (ParserUtils.contains(buffer, SyntaxConstants.COMMAND_DELIMITER))) {
            return cursor;
        }

        int desiredCursorPosition = 0;

        try {
            TrimmedInput simpleTrim = PreprocessorUtils.simpleTrim(buffer);
            desiredCursorPosition += simpleTrim.getNoOfSpacesRemoved();
            List<CommandTarget> targets = locateTargets(simpleTrim.getString());
            if (targets.size() > 1) {
                String padding = desiredCursorPosition != 0 ? ParserUtils.getPadding(desiredCursorPosition) : "";
                // This means that what the user has entered matches
                // the beginning of many commands
                for (CommandTarget commandTarget : targets) {
                    completionCandidates
                            .add(new Completion(padding + commandTarget.getGfshMethodTarget().getKey()));
                }
            } else {
                if (targets.size() == 1) {
                    CommandTarget commandTarget = targets.get(0);
                    // Only one command matches but we still have to check
                    // whether the user has properly entered it or not
                    if (simpleTrim.getString().length() >= commandTarget.getGfshMethodTarget().getKey().length()) {
                        /* int position = */
                        return completeParameters(commandTarget,
                                desiredCursorPosition + commandTarget.getGfshMethodTarget().getKey().length(),
                                commandTarget.getGfshMethodTarget().getRemainingBuffer(), cursor,
                                completionCandidates);
                        /*
                         * updateCompletionCandidates(completionCandidates, buffer, position); return 0;
                         */
                    } else {
                        String padding = desiredCursorPosition != 0 ? ParserUtils.getPadding(desiredCursorPosition)
                                : "";
                        // User has still not entered the command name properly,
                        // we need to populate the completionCandidates list
                        completionCandidates
                                .add(new Completion(padding + commandTarget.getGfshMethodTarget().getKey()));
                    }
                }
            }

        } catch (IllegalArgumentException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (IllegalAccessException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (InvocationTargetException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (RuntimeException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        }
        // Returning 0 for exceptions too. This will break the completors' loop in
        // jline.ConsoleReader.complete() & will return false
        return 0;
    }

    @SuppressWarnings("unused")
    private void updateCompletionCandidates(List<Completion> completionCandidates, String buffer, int position) {
        List<Completion> temp = new ArrayList<Completion>();
        while (completionCandidates.size() > 0) {
            temp.add(completionCandidates.remove(0));
        }
        for (Completion completion : temp) {
            completionCandidates.add(new Completion(buffer.substring(0, position) + completion.getValue(),
                    completion.getFormattedValue(), completion.getHeading(), completion.getOrder()));
        }
    }

    private int completeParameters(CommandTarget commandTarget, int cursorStart, String remainingBuffer, int cursor,
            List<Completion> completionCandidates) {
        int desiredCursorPosition = cursorStart;
        // Factor for remainingBuffer
        boolean sizeReduced = false;
        // We need to work modify the flow according to the CliException
        // generated. For that we will need a reference to the Exception
        // CliException reference
        CliCommandOptionException coe = null;
        OptionSet userOptionSet = null;
        try {
            // We need to remove the space which separates command from the
            // parameters
            if (remainingBuffer.length() > 0) {
                remainingBuffer = remainingBuffer.substring(1);
                sizeReduced = true;
            }

            userOptionSet = commandTarget.getOptionParser().parse(remainingBuffer);
        } catch (CliException ce) {
            if (ce instanceof CliCommandOptionException) {
                coe = (CliCommandOptionException) ce;
                coe.setCommandTarget(commandTarget);
                userOptionSet = coe.getOptionSet();
            }
        }

        // Contains mandatory options which have not been specified
        List<Option> mandatoryOptions = new ArrayList<Option>();
        // Contains non-mandatory options which have not been specified
        List<Option> unspecifiedOptions = new ArrayList<Option>();
        // First we need a list to create a list of all the options specified
        Map<String, Option> optionsPresentMap = new LinkedHashMap<String, Option>();
        if (userOptionSet != null) {

            // Start with the arguments
            String argumentSeparator = " ";
            for (Argument argument : commandTarget.getOptionParser().getArguments()) {
                if (completionCandidates.size() == 0) {
                    boolean warning = false;
                    if (userOptionSet.hasArgument(argument)) {
                        boolean incrementCursor = true;
                        // Here we need to get all the possible values for this
                        // argument
                        if (getAllPossibleValuesForParameter(completionCandidates, argument,
                                userOptionSet.getValue(argument), commandTarget.getGfshMethodTarget())) {
                            // Check whether the list of completionCandidates is
                            // not empty
                            if (completionCandidates.size() > 0) {
                                // First check whether the argument value
                                // matches with any
                                // of the completionCandidates
                                if (perfectMatch(completionCandidates, userOptionSet.getValue(argument))) {
                                    // Remove all the completionCandidates
                                    completionCandidates.clear();
                                } else {
                                    modifyCompletionCandidates(completionCandidates, argumentSeparator,
                                            userOptionSet.getValue(argument));
                                    // For this case also we should not
                                    // increment the
                                    // cursorPosition
                                    if (completionCandidates.size() > 0) {
                                        incrementCursor = false;
                                    }
                                }
                            }
                        } else {
                            // The completion candidates should be cleared if the Converter has
                            // populated it with some values
                            completionCandidates.clear();
                        }
                        if (incrementCursor) {
                            desiredCursorPosition += userOptionSet.getValue(argument).length()
                                    + argumentSeparator.length();
                        }
                    } else {
                        if (argument.isRequired()) {
                            // Here the converter will come in handy
                            // to get suggestion for arguments
                            if (getAllPossibleValuesForParameter(completionCandidates, argument, null,
                                    commandTarget.getGfshMethodTarget())) {
                                if (completionCandidates.size() == 0) {
                                    // Enable warning if nothing is returned
                                    warning = true;
                                }
                            } else {
                                // The completion candidates should be cleared if the Converter has
                                // populated it with some values
                                completionCandidates.clear();
                                warning = true;
                            }
                        } else {
                            boolean checkForPossibleValues = true;
                            // This means that the argument is not mandatory
                            // Now here we need to check whether user wants to
                            // enter an option.
                            if (endsWithOptionSpecifiers(userOptionSet.getUserInput())
                                    || hasOptionSpecified(userOptionSet.getUserInput())) {
                                // This means options have started, and we
                                // cannot have arguments after options
                                // So, we just skip checking for possible
                                // values
                                checkForPossibleValues = false;
                            }
                            // Just try getting the PossibleValues without
                            // aiming
                            if (checkForPossibleValues) {
                                getAllPossibleValuesForParameter(completionCandidates, argument, null,
                                        commandTarget.getGfshMethodTarget());
                            }
                        }
                        if (completionCandidates.size() > 0) {
                            modifyCompletionCandidates(completionCandidates, argumentSeparator, (String[]) null);
                        }
                    }
                    if (warning) {
                        String argMessage = argument.getArgumentName()
                                + ((argument.getHelp() != null && !argument.getHelp().equals(""))
                                        ? ": " + argument.getHelp()
                                        : "");
                        logWarning(CliStrings.format(CliStrings.GFSHPARSER__MSG__REQUIRED_ARGUMENT_0, argMessage));
                        return desiredCursorPosition + userOptionSet.getNoOfSpacesRemoved();
                    }
                }
                argumentSeparator = SyntaxConstants.ARGUMENT_SEPARATOR;
            }
            if (completionCandidates.size() > 0) {
                return desiredCursorPosition + userOptionSet.getNoOfSpacesRemoved();
            }

            // Now process options

            boolean warningValueRequired = false;
            Option warningOption = null;

            for (Option option : commandTarget.getOptionParser().getOptions()) {
                if (userOptionSet.hasOption(option)) {
                    // We are supporting option synonyms,
                    // so we need to check that here
                    for (String string : userOptionSet.getSplit()) {
                        if (string.startsWith(SyntaxConstants.LONG_OPTION_SPECIFIER)) {
                            // Remove option prefix
                            string = StringUtils.removeStart(string, SyntaxConstants.LONG_OPTION_SPECIFIER);
                            // Remove value specifier
                            string = StringUtils.removeEnd(string, SyntaxConstants.OPTION_VALUE_SPECIFIER);
                            if (!string.equals("")) {
                                if (option.getLongOption().equals(string)) {
                                    // This means that user has entered the whole option and
                                    // Increment desiredCursorPostion by the length of the
                                    // option and the option specifier, including space
                                    desiredCursorPosition += /* space */1
                                            + SyntaxConstants.LONG_OPTION_SPECIFIER.length()
                                            + option.getLongOption().length();
                                    break;

                                } else {
                                    // This is only possible if the user has
                                    // entered one of the synonyms of the options
                                    // which wasn't displayed initially
                                    for (String optionSynonym : option.getSynonyms()) {
                                        if (optionSynonym.equals(string)) {
                                            // This means that what the user has
                                            // entered is actually a
                                            // synonym for the option
                                            desiredCursorPosition += /* space */1
                                                    + SyntaxConstants.LONG_OPTION_SPECIFIER.length()
                                                    + optionSynonym.length();
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    optionsPresentMap.put(option.getLongOption(), option);

                    // For option value

                    if (userOptionSet.hasValue(option)) {
                        String value = userOptionSet.getValue(option);
                        boolean valueActuallySpecified = false;

                        String valueSeparator = SyntaxConstants.VALUE_SEPARATOR;
                        if (option.getValueSeparator() != null) {
                            valueSeparator = option.getValueSeparator();
                        }

                        // JOpt doesn't maintain trailing comma (separator), hence reading it from buffer.
                        boolean bufferEndsWithValueSeparator = remainingBuffer.endsWith(valueSeparator);
                        // Check whether the value assigned to the option is
                        // actually part of the split array or has been
                        // assigned using the specifiedDefaultValue attribute

                        // userOptionElement can be option name or value of that option.
                        // E.g. "--opt=val" has elements "opt" & "val"
                        for (String userOptionElement : userOptionSet.getSplit()) {
                            if (userOptionElement.equals(value)
                                    || (userOptionElement).equals(value + valueSeparator)) {
                                valueActuallySpecified = true;
                            }
                        }
                        if (!valueActuallySpecified) {
                            continue;
                        }
                        boolean incrementCursor = true;
                        boolean considerLastValue = false;
                        int lengthToBeAdded = 0;
                        int lastIndexOf = 0;

                        // This should only be invoked if we don't have any
                        // completionCandidates beforeHand
                        if (completionCandidates.size() == 0) {
                            // Here also we might need to invoke converter to
                            // get values apt for the option
                            if (!endsWithOptionSpecifiers(userOptionSet.getUserInput())
                                    && getAllPossibleValuesForParameter(completionCandidates, option, value,
                                            commandTarget.getGfshMethodTarget())) {

                                // If the value returned by getAllPossibleValues
                                // is the same as that entered by the
                                // user we need to remove it from the
                                // completionCandidates and move forward

                                String prefix = "";
                                String[] split = ParserUtils.splitValues(value, valueSeparator);

                                if (completionCandidates.size() > 0) {
                                    if (PreprocessorUtils.isSyntaxValid(value) && bufferEndsWithValueSeparator) {
                                        // This means that the user wants to
                                        // enter more values,
                                        prefix = valueSeparator;
                                    } else if (perfectMatch(completionCandidates, split)) {
                                        // If the user does not want to enter
                                        // more values, and it matches one
                                        // of the values then we do not
                                        // need to suggest anything for
                                        // this option
                                        completionCandidates.clear();
                                        considerLastValue = true;
                                    } else if (ParserUtils.contains(value, valueSeparator)) {
                                        prefix = valueSeparator;
                                    } else {
                                        incrementCursor = false;
                                        if (value.startsWith(" ")) {
                                            prefix = "  ";
                                        } else if (value.startsWith("\n")) {
                                            prefix = "\n";
                                        } else {
                                            prefix = SyntaxConstants.OPTION_VALUE_SPECIFIER;
                                        }
                                    }
                                }
                                modifyCompletionCandidates(completionCandidates, prefix,
                                        bufferEndsWithValueSeparator, split);
                                if (completionCandidates.size() == 0) {
                                    incrementCursor = true;
                                    considerLastValue = true;
                                }
                            } else {
                                // The completion candidates should be cleared if the Converter has
                                // populated it with some values
                                completionCandidates.clear();
                                considerLastValue = true;
                            }
                        } else {
                            // Make everything true
                            considerLastValue = true;
                        }
                        // FIX for: 46265
                        // if bufferEndsWithValueSeparator, append a valueSeparator to get the real lastIndexOd
                        // e.g. Let's say remainingBuffer is: cmd --opt1=val1,val2,
                        // value would be: cmd --opt1=val1,val2 ---> not there's no comma in the end.
                        // This doesn't give us the real last index of valueSeparator, hence add extra
                        // valueSeparator.
                        lastIndexOf = ParserUtils.lastIndexOf(
                                bufferEndsWithValueSeparator ? value + valueSeparator : value, valueSeparator);
                        lengthToBeAdded = value.substring(0, (lastIndexOf > 0 ? lastIndexOf : value.length()))
                                .length();
                        // Increment desiredCursorPosition
                        if (incrementCursor) {
                            desiredCursorPosition += /* value specifier length */SyntaxConstants.OPTION_VALUE_SPECIFIER
                                    .length() + lengthToBeAdded
                                    + ((considerLastValue) ? value.length() - lengthToBeAdded : 0);
                            if (value.endsWith(" ") && considerLastValue) {
                                desiredCursorPosition--;
                            }
                        }
                        if (completionCandidates.size() == 0) {
                            if (!PreprocessorUtils.isSyntaxValid(value)) {
                                return desiredCursorPosition + userOptionSet.getNoOfSpacesRemoved();
                            } else {
                                // Check whether the value ends with
                                // VALUE_SEPARATOR,
                                // if yes then we need to return
                                if (value.endsWith(valueSeparator)) {
                                    return desiredCursorPosition + userOptionSet.getNoOfSpacesRemoved();
                                }
                            }
                        }
                    } else {
                        // Here the converter is useful to invoke
                        // auto-suggestion, get Values from Converter
                        if (completionCandidates.size() == 0) {
                            if (getAllPossibleValuesForParameter(completionCandidates, option, null,
                                    commandTarget.getGfshMethodTarget())) {
                                if (completionCandidates.size() == 0) {
                                    warningValueRequired = true;
                                } else {
                                    modifyCompletionCandidates(completionCandidates,
                                            SyntaxConstants.OPTION_VALUE_SPECIFIER, new String[] { null });
                                }
                            } else {
                                // The completion candidates should be cleared if the Converter
                                // has populated it with some values
                                completionCandidates.clear();
                                warningValueRequired = true;
                            }
                        }
                    }
                } else {

                    // As we have reached here, the OptionParser was not able to
                    // detect anything which proves that the option is present
                    // So, we check with what the user provided and add only
                    // that to the list of options to prompt for

                    for (String userOptString : userOptionSet.getSplit()) {
                        // Now to determine whether what the user specified was
                        // an option, we need to check whether it starts
                        // with an option specifier
                        if (userOptString.startsWith(SyntaxConstants.LONG_OPTION_SPECIFIER)) {
                            // Now remove the option specifier part
                            userOptString = StringUtils.removeStart(userOptString,
                                    SyntaxConstants.LONG_OPTION_SPECIFIER);
                            if (option.getLongOption().startsWith(userOptString) && !userOptString.equals("")
                                    && !option.getLongOption().equals(userOptString)
                                    && !optionsPresentMap.containsKey(userOptString)) {

                                completionCandidates.add(new Completion(
                                        " " + SyntaxConstants.LONG_OPTION_SPECIFIER + option.getLongOption(),
                                        option.getLongOption(), "", 0));
                            } else {
                                for (String optionSynonym : option.getSynonyms()) {
                                    if (optionSynonym.startsWith(userOptString) && !userOptString.equals("")
                                            && !optionSynonym.equals(userOptString)) {
                                        completionCandidates.add(new Completion(
                                                " " + SyntaxConstants.LONG_OPTION_SPECIFIER + optionSynonym,
                                                optionSynonym, "", 0));
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    if (completionCandidates.size() == 0) {
                        if (option.isRequired()) {
                            mandatoryOptions.add(option);
                        } else {
                            unspecifiedOptions.add(option);
                        }
                    }
                }
                if (warningValueRequired/* || warningMultipleValuesNotSupported */) {
                    warningOption = option;
                    warningValueRequired = false;
                }
            }

            // Display warning if something not specified
            if (warningOption != null) {
                String optionMsg = warningOption.getLongOption()
                        + ((warningOption.getHelp() != null && !warningOption.getHelp().equals(""))
                                ? ": " + warningOption.getHelp()
                                : "");
                logWarning(CliStrings.format(CliStrings.GFSHPARSER__MSG__VALUE_REQUIRED_FOR_OPTION_0, optionMsg));

                desiredCursorPosition += userOptionSet.getNoOfSpacesRemoved();
                completionCandidates.add(new Completion(SyntaxConstants.OPTION_VALUE_SPECIFIER, "", null, 0));
                return desiredCursorPosition;
            }

        }

        // Calculate the cursor position
        int newCursor = desiredCursorPosition
                + ((userOptionSet != null) ? userOptionSet.getNoOfSpacesRemoved() : 0);

        String subString = remainingBuffer;
        if (newCursor != cursorStart) {
            int sizedReducedAdj = sizeReduced ? -1 : 0;
            int begin = newCursor + sizedReducedAdj - cursorStart;
            subString = remainingBuffer.substring(begin).trim();
        }

        // Exception handling
        if (coe != null // hasException
                && newCursor < cursor // newCursorIsEarlierThanCursor
                && completionCandidates.size() == 0 // zeroCompletionCandidates
                && !(PreprocessorUtils.containsOnlyWhiteSpaces(subString) // onlyHasWhiteSpaces
                        || ((subString.endsWith(SyntaxConstants.LONG_OPTION_SPECIFIER)
                                && subString.startsWith(SyntaxConstants.LONG_OPTION_SPECIFIER)) // isHypenHyphen
                                || (subString.startsWith(SyntaxConstants.SHORT_OPTION_SPECIFIER)
                                        && subString.endsWith(SyntaxConstants.SHORT_OPTION_SPECIFIER))))) { // isHyphen

            ExceptionHandler.handleException(coe);
            return cursor;
        }

        // If nothing has been specified for auto-completion then we need to suggest options
        if (completionCandidates.size() == 0) {
            if (mandatoryOptions.size() > 0) {

                for (Option option : mandatoryOptions) {
                    completionCandidates.add(
                            new Completion(" " + SyntaxConstants.LONG_OPTION_SPECIFIER + option.getLongOption(),
                                    option.getLongOption(), "", 0));
                }
            } else {
                // As all the mandatory options have been specified we can prompt the
                // user for optional options.
                unspecifiedOptions = getUnspecifiedOptionsWithMode(unspecifiedOptions, commandTarget,
                        optionsPresentMap);
                for (Option option : unspecifiedOptions) {
                    completionCandidates.add(
                            new Completion(" " + SyntaxConstants.LONG_OPTION_SPECIFIER + option.getLongOption(),
                                    option.getLongOption(), "", 0));
                }
            }
        }
        return newCursor;
    }

    private List<Option> getUnspecifiedOptionsWithMode(List<Option> unspecifiedOptions, CommandTarget commandTarget,
            Map<String, Option> optionsPresentMap) {

        Collection<CommandMode> cmodes = CommandModes.getInstance().getCommandModes(commandTarget.getCommandName());
        if (cmodes != null) {
            List<Option> filteredList = new ArrayList<Option>();

            // Populate with default options
            CommandMode defaultMode = CommandModes.getInstance().getCommandMode(commandTarget.getCommandName(),
                    CommandModes.DEFAULT_MODE);
            for (String opt : defaultMode.options) {
                for (Option option : unspecifiedOptions) {
                    if (option.getLongOption().equals(opt)) {
                        filteredList.add(option);
                    }
                }
            }

            // Now add options only for detected command mode
            boolean leadOptionFound = false;
            for (CommandMode cmd : cmodes) {
                if (optionsPresentMap.containsKey(cmd.leadOption)) {
                    leadOptionFound = true;
                    for (String opt : cmd.options) {
                        if (!optionsPresentMap.containsKey(opt)) {
                            for (Option option : unspecifiedOptions) {
                                if (option.getLongOption().equals(opt)) {
                                    filteredList.add(option);
                                }
                            }
                        }
                    }
                    break;
                }
            }

            if (leadOptionFound) {
                return filteredList;
            }

            if (optionsPresentMap.isEmpty()) {
                // Here return only lead-option of the command-modes
                filteredList.clear();
                for (CommandMode cmd2 : cmodes) {
                    for (Option option2 : unspecifiedOptions) {
                        if (option2.getLongOption().equals(cmd2.leadOption)) {
                            filteredList.add(option2);
                        }
                    }
                }
                return filteredList;
            }
            return unspecifiedOptions;
        } else {
            return unspecifiedOptions;
        }
    }

    private void checkOptionSetForValidCommandModes(OptionSet userOptionSet, CommandTarget commandTarget)
            throws CliCommandMultiModeOptionException {
        CommandModes modes = CommandModes.getInstance();
        Collection<CommandMode> cmodes = modes.getCommandModes(commandTarget.getCommandName());

        if (cmodes != null) {
            CommandMode defaultMode = modes.getCommandMode(commandTarget.getCommandName(),
                    CommandModes.DEFAULT_MODE);
            Map<String, Option> userOptions = new HashMap<String, Option>();
            Map<String, CommandMode> loToModeMap = new HashMap<String, CommandMode>();
            for (Option option : commandTarget.getOptionParser().getOptions()) {
                if (userOptionSet.hasOption(option)) {
                    userOptions.put(option.getLongOption(), option);
                }
            }

            List<String> leadOptionList = new ArrayList<String>();
            for (CommandMode cmd : cmodes) {
                loToModeMap.put(cmd.leadOption, cmd);
                if (userOptions.containsKey(cmd.leadOption)) {
                    leadOptionList.add(cmd.leadOption);
                }

                if (leadOptionList.size() > 1) {

                    StringBuilder sb = new StringBuilder();
                    for (String leadOption : leadOptionList) {
                        sb.append(loToModeMap.get(leadOption).name).append(",");
                    }
                    throw new CliCommandMultiModeOptionException(commandTarget, userOptions.get(cmd.leadOption),
                            sb.toString(), CliCommandMultiModeOptionException.MULTIPLE_LEAD_OPTIONS);
                }
            }

            if (leadOptionList.size() == 1) {
                CommandMode modeDetected = loToModeMap.get(leadOptionList.get(0));
                for (Option opt : userOptions.values()) {
                    // Check only for non-default options, default options are allowed with any other mode
                    if (!isDefaultOption(opt.getLongOption(), defaultMode)) {
                        boolean isOptionFromDetectedMode = false;
                        if (modeDetected.options.length > 0) {
                            for (String commandOpt : modeDetected.options) {
                                if (commandOpt.equals(opt.getLongOption())) {
                                    isOptionFromDetectedMode = true;
                                }
                            }
                            if (!isOptionFromDetectedMode) {
                                throw new CliCommandMultiModeOptionException(commandTarget, opt,
                                        opt.getLongOption(),
                                        CliCommandMultiModeOptionException.OPTIONS_FROM_MULTIPLE_MODES);
                            }
                        }
                    }
                }
            }
        }
    }

    private boolean isDefaultOption(String longOption, CommandMode commandMode) {
        for (String str : commandMode.options) {
            if (longOption.equals(str)) {
                return true;
            }
        }
        return false;
    }

    private boolean endsWithOptionSpecifiers(String userInput) {
        userInput = userInput.trim();
        if (userInput.endsWith(" " + SyntaxConstants.LONG_OPTION_SPECIFIER)
                || userInput.endsWith(" " + SyntaxConstants.SHORT_OPTION_SPECIFIER)) {
            return true;
        } else {
            return false;
        }
    }

    /*
     * Verifies whether the userInput has any one of the following: --some-opt --s --some-opt=some-val
     * --something-else
     */
    private boolean hasOptionSpecified(String userInput) {
        userInput = userInput.trim();
        return Pattern.matches("^(.*)(-+)(\\w+)(.*)$", userInput);
    }

    private String getSystemProvidedValue(Parameter parameter) {
        if (parameter.isSystemProvided()) {
            // TODO fetch from system properties
            // Assume value is null for now.
            return null;
        } else {
            return null;
        }
    }

    private boolean perfectMatch(List<Completion> completionCandidates, String... argumentValue) {
        // Here only the last value should match one of the
        // completionCandidates
        if (argumentValue.length > 0) {
            for (Completion completion : completionCandidates) {
                if (completion.getValue().equals(argumentValue[argumentValue.length - 1])) {
                    return true;
                }
            }
        }
        return false;
    }

    private void modifyCompletionCandidates(List<Completion> completionCandidates, String prefix,
            String... existingData) {
        modifyCompletionCandidates(completionCandidates, prefix, false, existingData);
    }

    private void modifyCompletionCandidates(List<Completion> completionCandidates, String prefix,
            boolean endsWithValueSeparator, String... existingData) {
        List<Completion> temp = new ArrayList<Completion>();
        while (completionCandidates.size() > 0) {
            temp.add(completionCandidates.remove(0));
        }
        for (Completion completion : temp) {
            boolean includeCompletion = true;
            String value = completion.getValue();
            if (existingData != null) {
                for (String string : existingData) {
                    if (string != null) {
                        // Check whether that value matches any of the
                        // existingData
                        // If it matches any one of existing data then we do not
                        // need to include it in the list of completion
                        // candidates
                        if (value.equals(string)) {
                            includeCompletion = false;
                        }
                    }
                }
                if (includeCompletion) {
                    if (existingData[existingData.length - 1] != null
                            && (!value.startsWith(existingData[existingData.length - 1])
                                    && !endsWithValueSeparator)) {
                        includeCompletion = false;
                    }
                }
            }
            if (includeCompletion) {
                // Also we only need to check with the last string of
                // existingData
                // whether the completion value starts with it.
                completionCandidates
                        .add(new Completion(prefix + completion.getValue(), completion.getValue(), "", 0));
            }
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private boolean getAllPossibleValuesForParameter(List<Completion> completionCandidates, Parameter parameter,
            String existingData, GfshMethodTarget gfshMethodTarget) {
        Converter<?> converter = parameter.getConverter();
        // Check if any new converter is available which
        // satisfies the requirements for this argument
        if (converter == null) {
            parameter.setConverter(commandManager.getConverter(parameter.getDataType(), parameter.getContext()));
            converter = parameter.getConverter();
        }
        // If still we do not have any matching converters, we return
        if (converter == null) {
            return false;
        } else {
            // Now pass the getAllPossibleValues function of Converter interface
            // all the required parameters

            // Check whether it is a MultipleValueConverter
            String valueSeparator = SyntaxConstants.VALUE_SEPARATOR;
            if (parameter instanceof Option && ((Option) parameter).getValueSeparator() != null) {
                valueSeparator = ((Option) parameter).getValueSeparator();
            }
            if (converter instanceof MultipleValueConverter) {
                ((MultipleValueConverter) converter).getAllPossibleValues(completionCandidates,
                        parameter.getDataType(), ParserUtils.splitValues(existingData, valueSeparator),
                        parameter.getContext(),
                        new MethodTarget(gfshMethodTarget.getMethod(), gfshMethodTarget.getTarget(),
                                gfshMethodTarget.getRemainingBuffer(), gfshMethodTarget.getKey()));
            } else {
                converter.getAllPossibleValues(completionCandidates, parameter.getDataType(), existingData,
                        parameter.getContext(),
                        new MethodTarget(gfshMethodTarget.getMethod(), gfshMethodTarget.getTarget(),
                                gfshMethodTarget.getRemainingBuffer(), gfshMethodTarget.getKey()));
            }
        }
        if (completionCandidates.size() > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     */
    public ParseResult parse(String userInput) {
        GfshParseResult parseResult = null;
        // First remove the trailing white spaces
        userInput = StringUtils.stripEnd(userInput, null);
        if ((ParserUtils.contains(userInput, SyntaxConstants.COMMAND_DELIMITER)
                && StringUtils.endsWithIgnoreCase(userInput, SyntaxConstants.COMMAND_DELIMITER))) {
            userInput = StringUtils.removeEnd(userInput, SyntaxConstants.COMMAND_DELIMITER);
        }

        try {
            boolean error = false;
            CliCommandOptionException coe = null;
            List<CommandTarget> targets = locateTargets(ParserUtils.trimBeginning(userInput), false);
            if (targets.size() > 1) {
                if (userInput.length() > 0) {
                    handleCondition(CliStrings.format(
                            CliStrings.GFSHPARSER__MSG__AMBIGIOUS_COMMAND_0_FOR_ASSISTANCE_USE_1_OR_HINT_HELP,
                            new Object[] { userInput, AbstractShell.completionKeys }),
                            CommandProcessingException.COMMAND_NAME_AMBIGUOUS, userInput);
                }
            } else {
                if (targets.size() == 1) {
                    OptionSet parse = null;
                    List<MethodParameter> parameters = new ArrayList<MethodParameter>();
                    Map<String, String> paramValMap = new HashMap<String, String>();
                    CommandTarget commandTarget = targets.get(0);
                    GfshMethodTarget gfshMethodTarget = commandTarget.getGfshMethodTarget();
                    preConfigureConverters(commandTarget);

                    try {
                        parse = commandTarget.getOptionParser().parse(gfshMethodTarget.getRemainingBuffer());
                    } catch (CliException ce) {
                        if (ce instanceof CliCommandOptionException) {
                            coe = (CliCommandOptionException) ce;
                            coe.setCommandTarget(commandTarget);
                            parse = coe.getOptionSet();
                            error = true;
                        }
                    }

                    try {
                        checkOptionSetForValidCommandModes(parse, commandTarget);
                    } catch (CliCommandMultiModeOptionException ce) {
                        error = true;
                        coe = ce;
                    }

                    error = processArguments(parse, commandTarget, paramValMap, parameters, error);
                    // TODO: next call throws when space before closing "
                    error = processOptions(parse, commandTarget, paramValMap, parameters, error);

                    if (!error) {
                        Object[] methodParameters = new Object[parameters.size()];
                        for (MethodParameter parameter : parameters) {
                            methodParameters[parameter.getParameterNo()] = parameter.getParameter();
                        }
                        parseResult = new GfshParseResult(gfshMethodTarget.getMethod(),
                                gfshMethodTarget.getTarget(), methodParameters, userInput,
                                commandTarget.getCommandName(), paramValMap);
                    } else {
                        if (coe != null) {
                            logWrapper.fine("Handling exception: " + coe.getMessage());
                            ExceptionHandler.handleException(coe); // TODO: this eats exception that would make it
                                                                   // easier to debug GemfireDataCommandsDUnitTest
                                                                   // ExceptionHandler.handleException() only logs it on console.
                                                                   // When on member, we need to handle this.
                            if (!CliUtil.isGfshVM()) {
                                handleCondition(
                                        CliStrings.format(CliStrings.GFSHPARSER__MSG__INVALID_COMMAND_STRING_0,
                                                userInput),
                                        coe, CommandProcessingException.COMMAND_INVALID, userInput);
                            }
                        }
                    }

                } else {
                    String message = CliStrings.format(CliStrings.GFSHPARSER__MSG__COMMAND_0_IS_NOT_VALID,
                            userInput);
                    CommandTarget commandTarget = locateExactMatchingTarget(userInput);
                    if (commandTarget != null) {
                        String commandName = commandTarget.getCommandName();
                        AvailabilityTarget availabilityIndicator = commandTarget.getAvailabilityIndicator();
                        message = CliStrings.format(CliStrings.GFSHPARSER__MSG__0_IS_NOT_AVAILABLE_REASON_1,
                                new Object[] { commandName, availabilityIndicator.getAvailabilityDescription() });
                    }
                    handleCondition(message, CommandProcessingException.COMMAND_INVALID_OR_UNAVAILABLE, userInput);
                }
            }
        } catch (IllegalArgumentException e1) {
            logWrapper.warning(CliUtil.stackTraceAsString(e1));
        } catch (IllegalAccessException e1) {
            logWrapper.warning(CliUtil.stackTraceAsString(e1));
        } catch (InvocationTargetException e1) {
            logWrapper.warning(CliUtil.stackTraceAsString(e1));
        }
        return parseResult;
    }

    // Pre-configure the converters so that we can test against them when parsing the command line
    private void preConfigureConverters(CommandTarget commandTarget) {
        for (Option option : commandTarget.getOptionParser().getOptions()) {
            Converter<?> converter = option.getConverter();
            if (converter == null) {
                option.setConverter(commandManager.getConverter(option.getDataType(), option.getContext()));
                converter = option.getConverter();
            }
        }

        for (Argument argument : commandTarget.getOptionParser().getArguments()) {
            Converter<?> converter = argument.getConverter();
            if (converter == null) {
                argument.setConverter(commandManager.getConverter(argument.getDataType(), argument.getContext()));
                converter = argument.getConverter();
            }
        }
    }

    private boolean processOptions(OptionSet parse, CommandTarget commandTarget, Map<String, String> paramValMap,
            List<MethodParameter> parameters, boolean errorState) {
        boolean error = errorState;
        for (Option option : commandTarget.getOptionParser().getOptions()) {
            String value = null;
            if (parse.hasOption(option)) {
                if (parse.hasValue(option)) {
                    value = parse.getValue(option);
                }
                if (value == null) {
                    handleCondition(
                            CliStrings.format(CliStrings.GFSHPARSER__MSG__VALUE_REQUIRED_FOR_OPTION_0,
                                    option.getLongOption()),
                            CommandProcessingException.OPTION_VALUE_REQUIRED, option.getLongOption());
                    logWrapper.fine("Value required for Parameter " + option.getLongOption());
                    error = true;
                }
            } else {
                if (option.isRequired()) {
                    handleCondition(
                            CliStrings.format(CliStrings.GFSHPARSER__MSG__COMMAND_OPTION_0_IS_REQUIRED_USE_HELP,
                                    option.getLongOption()),
                            CommandProcessingException.REQUIRED_OPTION_MISSING, option.getLongOption());
                    logWrapper.fine("Required Parameter " + option.getLongOption());
                    error = true;
                } else {
                    // Try to get the unspecifiedDefaultValue for the
                    // option
                    value = option.getUnspecifiedDefaultValue();
                    if (value == null) {
                        // Now try the system provide value
                        value = getSystemProvidedValue(option);
                    }
                }
            }

            String valueSeparator = SyntaxConstants.VALUE_SEPARATOR;
            if (option.getValueSeparator() != null) {
                valueSeparator = option.getValueSeparator();
            }

            Object object = getConversionObject(option.getConverter(), value, option.getDataType(),
                    option.getContext(), valueSeparator);
            // Check if conversion fails
            if (value != null && object == null) {
                handleCondition(
                        CliStrings.format(CliStrings.GFSHPARSER__MSG__VALUE_0_IS_NOT_APPLICABLE_FOR_1,
                                new Object[] { value.trim(), option.getLongOption() }),
                        CommandProcessingException.OPTION_VALUE_INVALID, option.getLongOption() + "=" + value);
                logWrapper.fine("Value \"" + value.trim() + "\" is not applicable for " + option.getLongOption());
                error = true;
            }
            parameters.add(new MethodParameter(object, option.getParameterNo()));
            paramValMap.put(option.getLongOption(), value);
        }
        return error;
    }

    private boolean processArguments(OptionSet parse, CommandTarget commandTarget, Map<String, String> paramValMap,
            List<MethodParameter> parameters, boolean errorState) {
        boolean error = errorState;
        for (Argument argument : commandTarget.getOptionParser().getArguments()) {
            String value = null;

            if (parse.hasArgument(argument)) {
                value = parse.getValue(argument);
            } else {
                if (argument.isRequired()) {
                    handleCondition(
                            CliStrings.format(CliStrings.GFSHPARSER__MSG__COMMAND_ARGUMENT_0_IS_REQUIRED_USE_HELP,
                                    argument.getArgumentName()),
                            CommandProcessingException.REQUIRED_ARGUMENT_MISSING, argument.getArgumentName());
                    logWrapper.fine("Required Argument " + argument.getArgumentName());
                    error = true;
                } else {
                    // try to get unspecifiedDefaultValue for
                    // the argument
                    value = argument.getUnspecifiedDefaultValue();
                    if (value == null) {
                        // Now try the system provided value
                        value = getSystemProvidedValue(argument);
                    }
                }

            }

            Object conversionObject = getConversionObject(argument.getConverter(), value, argument.getDataType(),
                    argument.getContext(), SyntaxConstants.VALUE_SEPARATOR);
            if (value != null && conversionObject == null) {
                handleCondition(
                        CliStrings.format(CliStrings.GFSHPARSER__MSG__VALUE_0_IS_NOT_APPLICABLE_FOR_1,
                                new Object[] { value.trim(), argument.getArgumentName() }),
                        CommandProcessingException.ARGUMENT_INVALID, argument.getArgumentName() + "=" + value);
                logWrapper.fine(
                        "Value '" + value.trim() + "' not applicable for argument: " + argument.getArgumentName());
                error = true;
            } else {
                parameters.add(new MethodParameter(conversionObject, argument.getParameterNo()));
                paramValMap.put(argument.getArgumentName(), value);
            }
        }
        return error;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private Object getConversionObject(Converter<?> converter, String string, Class<?> dataType, String context,
            String valueSeparator) {

        try {
            if (converter != null && converter instanceof MultipleValueConverter) {
                return ((MultipleValueConverter) converter).convertFromText(
                        ParserUtils.splitValues(((string != null) ? string.trim() : null), valueSeparator),
                        dataType, context);
            }

            // Remove outer single or double quotes if found
            boolean hasDoubleQuotes = string.startsWith("\"") && string.endsWith("\"");
            boolean hasSingleQuotes = string.startsWith("\'") && string.endsWith("\'");

            while (string != null && (hasDoubleQuotes || hasSingleQuotes)) {
                string = string.substring(1, string.length() - 1);
                hasDoubleQuotes = string.startsWith("\"") && string.endsWith("\"");
                hasSingleQuotes = string.startsWith("\'") && string.endsWith("\'");
            }

            if (converter != null) {
                return converter.convertFromText((string != null) ? string.trim() : null, dataType, context);
            }

            // TODO consider multiple value case for primitives
            if (string != null) {
                if (String.class.isAssignableFrom(dataType)) {
                    return string.trim();
                } else if (Byte.class.isAssignableFrom(dataType) || byte.class.isAssignableFrom(dataType)) {
                    return Integer.parseInt(string);
                } else if (Short.class.isAssignableFrom(dataType) || short.class.isAssignableFrom(dataType)) {
                    return Integer.parseInt(string);
                } else if (Boolean.class.isAssignableFrom(dataType) || boolean.class.isAssignableFrom(dataType)) {
                    return Integer.parseInt(string);
                } else if (Integer.class.isAssignableFrom(dataType) || int.class.isAssignableFrom(dataType)) {
                    return Integer.parseInt(string);
                } else if (Long.class.isAssignableFrom(dataType) || long.class.isAssignableFrom(dataType)) {
                    return Long.parseLong(string);
                } else if (Float.class.isAssignableFrom(dataType) || float.class.isAssignableFrom(dataType)) {
                    return Float.parseFloat(string);
                } else if (Double.class.isAssignableFrom(dataType) || double.class.isAssignableFrom(dataType)) {
                    return Double.parseDouble(string);
                } else if (Character.class.isAssignableFrom(dataType) || char.class.isAssignableFrom(dataType)) {
                    if (string.length() == 1) {
                        string.charAt(0);
                    } else {
                        // FIXME Use a constant here
                        return '0';
                    }
                }
            }
        } catch (Exception e) {
            // TODO add logging
            // Do nothing, just return null
        }
        return null;
    }

    private List<CommandTarget> locateTargets(String userInput)
            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        return locateTargets(userInput, true);
    }

    private List<CommandTarget> locateTargets(String userInput, boolean matchIncomplete)
            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        List<CommandTarget> commandTargets = new ArrayList<CommandTarget>();
        Map<String, CommandTarget> commands = commandManager.getCommands();
        // Now we need to locate the CommandTargets from the entries in the map
        for (String commandName : commands.keySet()) {
            if (userInput.startsWith(commandName)) {
                // This means that the user has entered the command
                CommandTarget commandTarget = commands.get(commandName);
                if (isAvailable(commandTarget, commandName)) {
                    String remainingBuffer = StringUtils.removeStart(userInput, commandName);
                    if (remainingBuffer.length() == 0 || remainingBuffer.startsWith(" ")
                            || remainingBuffer.startsWith(GfshParser.LINE_SEPARATOR)) {
                        // We need to duplicate with a new MethodTarget as this
                        // parser will be used in a concurrent execution environment
                        if (!commandTargets.contains(commandTarget)) {
                            // This test is necessary as the command may have similar
                            // synonyms or which are prefix for the command
                            commandTargets.add(commandTarget.duplicate(commandName, remainingBuffer));
                        }
                    }
                }
            } else if (matchIncomplete && commandName.startsWith(userInput)) {
                // This means that the user is yet to enter the command properly
                CommandTarget commandTarget = commands.get(commandName);
                if (isAvailable(commandTarget, commandName)) {
                    // We need to duplicate with a new MethodTarget as this
                    // parser will be used in a concurrent execution environment
                    if (!commandTargets.contains(commandTarget)) {
                        // This test is necessary as the command may have similar
                        // synonyms or which are prefix for the command
                        commandTargets.add(commandTarget.duplicate(commandName));
                    }
                }
            }
        }
        return commandTargets;
    }

    // TODO - Abhishek - create an inner CommandTargetLocater instead of multiple
    // methods like these.
    private CommandTarget locateExactMatchingTarget(final String userInput)// exact matching
            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        CommandTarget commandTarget = null;

        Map<String, CommandTarget> commandTargetsMap = commandManager.getCommands();
        // Reverse sort the command names because we should start from longer names
        // E.g. Consider commands "A", "A B" & user input as "A B --opt1=val1"
        // In this case, "A B" is the most probable match & should be matched first
        // which can be achieved by reversing natural order of sorting.
        Set<String> commandNamesReverseSorted = new TreeSet<String>(Collections.reverseOrder());
        commandNamesReverseSorted.addAll(commandTargetsMap.keySet());

        // Now we need to locate the CommandTargets from the entries in the map
        for (final String commandName : commandNamesReverseSorted) {
            if (userInput.startsWith(commandName) && commandWordsMatch(userInput, commandName)) {
                // This means that the user has entered the command & name matches exactly
                commandTarget = commandTargetsMap.get(commandName);
                if (commandTarget != null) {
                    String remainingBuffer = StringUtils.removeStart(userInput, commandName);
                    commandTarget = commandTarget.duplicate(commandName, remainingBuffer);
                    break;
                }
            }
        }
        return commandTarget;
    }

    private static boolean commandWordsMatch(final String userInput, final String commandName) {
        boolean commandWordsMatch = true;

        String[] commandNameWords = commandName.split(" ");
        String[] userInputWords = userInput.split(" ");

        // commandName is fixed & hence should have less or same number of words as
        // the user input. E.g. "create disk-store" should match with
        // "create disk-store --name=xyz" but not with "create disk store"
        if (commandNameWords.length <= userInputWords.length) {
            // if both have length zero, words can be considered to be matching.
            for (int i = 0; i < commandNameWords.length; i++) {
                if (!commandNameWords[i].equals(userInputWords[i])) {
                    commandWordsMatch = false;
                    break;
                }
            }
        } else {
            commandWordsMatch = false;
        }

        return commandWordsMatch;
    }

    private Map<String, CommandTarget> getRequiredCommandTargets(Set<String> requiredCommands) {
        Map<String, CommandTarget> existingCommands = commandManager.getCommands();
        Map<String, CommandTarget> requiredCommandsMap = existingCommands;

        if (requiredCommands != null && !requiredCommands.isEmpty()) {
            requiredCommandsMap = new TreeMap<String, CommandTarget>();
            for (String commandName : requiredCommands) {
                CommandTarget commandTarget = existingCommands.get(commandName);
                if (commandTarget != null) {
                    requiredCommandsMap.put(commandName, commandTarget);
                }
            }
        }

        return requiredCommandsMap;
    }

    private Map<Short, List<CommandTarget>> findMatchingCommands(String userSpecifiedCommand,
            Set<String> requiredCommands)
            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {

        Map<String, CommandTarget> existingCommands = getRequiredCommandTargets(requiredCommands);
        CommandTarget exactCommandTarget = existingCommands.get(userSpecifiedCommand);

        // 1. First find exactly matching commands.
        List<CommandTarget> exactCommandTargets = Collections.emptyList();
        if (exactCommandTarget != null) {
            // This means that the user has entered the command
            // NOTE: we are not skipping synonym here.
            exactCommandTargets = Collections.singletonList(exactCommandTarget);
        }

        // 2. Now find command names that start with 'userSpecifiedCommand'
        List<CommandTarget> possibleCommandTargets = new ArrayList<CommandTarget>();
        // Now we need to locate the CommandTargets from the entries in the map
        for (Map.Entry<String, CommandTarget> entry : existingCommands.entrySet()) {
            CommandTarget commandTarget = entry.getValue();
            String commandName = commandTarget.getCommandName();
            // This check is done to remove commands that are synonyms as
            // CommandTarget.getCommandName() will return name & not a synonym
            if (entry.getKey().equals(commandName)) {
                if (commandName.startsWith(userSpecifiedCommand) && !commandTarget.equals(exactCommandTarget)) {
                    // This means that the user is yet to enter the command properly
                    possibleCommandTargets.add(commandTarget);
                }
            }
        }

        Map<Short, List<CommandTarget>> commandTargetsArr = new HashMap<Short, List<CommandTarget>>();
        commandTargetsArr.put(EXACT_TARGET, exactCommandTargets);
        commandTargetsArr.put(MATCHING_TARGETS, possibleCommandTargets);
        return commandTargetsArr;
    }

    private boolean isAvailable(CommandTarget commandTarget, String commandName)
            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        AvailabilityTarget availabilityIndicator = commandTarget.getAvailabilityIndicator();
        if (availabilityIndicator == null) {
            availabilityIndicator = commandManager.getAvailabilityIndicator(commandName);
            commandTarget.setAvailabilityIndicator(availabilityIndicator);
        }
        return commandTarget.isAvailable();
    }

    public List<String> obtainHelpCommandNames(String userInput) {
        List<String> commandNames = new ArrayList<String>();

        try {
            if (userInput == null) {
                userInput = "";
            }

            List<CommandTarget> commandTargets = new ArrayList<CommandTarget>();
            Map<Short, List<CommandTarget>> matchingCommandsMap = findMatchingCommands(userInput, null);
            commandTargets.addAll(matchingCommandsMap.get(EXACT_TARGET));
            commandTargets.addAll(matchingCommandsMap.get(MATCHING_TARGETS));

            for (CommandTarget commandTarget : commandTargets) {
                commandNames.add(commandTarget.getCommandName());
            }
        } catch (IllegalArgumentException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (IllegalAccessException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (InvocationTargetException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        }

        return commandNames;
    }

    public String obtainHelp(String userInput, Set<String> commandNames) {
        final boolean withinShell = commandNames == null || commandNames.isEmpty();
        final String appName = withinShell ? "" : HelpUtils.EXE_PREFIX_FOR_EXTERNAL_HELP;

        StringBuilder helpText = new StringBuilder();
        try {
            if (userInput == null) {
                userInput = "";
            }

            Map<Short, List<CommandTarget>> matchingCommandsMap = findMatchingCommands(userInput, commandNames);
            List<CommandTarget> exactCommandTargets = matchingCommandsMap.get(EXACT_TARGET);
            List<CommandTarget> matchingCommandTargets = matchingCommandsMap.get(MATCHING_TARGETS);
            matchingCommandsMap.clear();

            if (exactCommandTargets.isEmpty() && matchingCommandTargets.isEmpty()) {
                // No matching commands
                helpText.append(CliStrings.GFSHPARSER__MSG__NO_MATCHING_COMMAND).append(GfshParser.LINE_SEPARATOR);
            } else {
                if (exactCommandTargets.size() == 1) {
                    helpText.append(obtainCommandSpecificHelp(exactCommandTargets.get(0), withinShell));
                    if (!matchingCommandTargets.isEmpty()) {
                        helpText.append(GfshParser.LINE_SEPARATOR);
                        helpText.append(CliStrings
                                .format(CliStrings.GFSHPARSER__MSG__OTHER_COMMANDS_STARTING_WITH_0_ARE, userInput));
                        for (int i = 0; i < matchingCommandTargets.size(); i++) {
                            CommandTarget commandTarget = matchingCommandTargets.get(i);
                            helpText.append(commandTarget.getCommandName());
                            if (i < matchingCommandTargets.size() - 1) {
                                helpText.append(", ");
                            }
                        }
                        helpText.append(GfshParser.LINE_SEPARATOR);
                    }
                } else {
                    List<CommandTarget> commandTargets = new ArrayList<CommandTarget>();
                    commandTargets.addAll(exactCommandTargets);
                    commandTargets.addAll(matchingCommandTargets);
                    for (CommandTarget commandTarget : commandTargets) {
                        String availability = commandTarget.isAvailable() ? HelpUtils.HELP__COMMAND_AVAILABLE
                                : HelpUtils.HELP__COMMAND_NOTAVAILABLE;
                        // Many matching commands, provide one line description
                        helpText.append(commandTarget.getCommandName());
                        if (withinShell) {
                            helpText.append(" (").append(availability).append(")");
                        }
                        helpText.append(GfshParser.LINE_SEPARATOR);
                        helpText.append(Gfsh.wrapText(commandTarget.getCommandHelp(), 1))
                                .append(GfshParser.LINE_SEPARATOR);
                    }
                    helpText.append(GfshParser.LINE_SEPARATOR);

                    if (withinShell) {
                        helpText.append(Gfsh.wrapText(CliStrings.format(
                                CliStrings.GFSHPARSER__MSG__USE_0_HELP_COMMAND_TODISPLAY_DETAILS, appName), 0))
                                .append(GfshParser.LINE_SEPARATOR);
                        helpText.append(Gfsh.wrapText(
                                CliStrings.format(CliStrings.GFSHPARSER__MSG__HELP_CAN_ALSO_BE_OBTAINED_BY_0_KEY,
                                        AbstractShell.completionKeys),
                                0));
                    }
                }
            }
        } catch (IllegalArgumentException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (IllegalAccessException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (InvocationTargetException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        }
        return helpText.toString();
    }

    private String obtainCommandSpecificHelp(CommandTarget commandTarget, boolean withinShell) {
        NewHelp newHelp = HelpUtils.getNewHelp(commandTarget, withinShell);
        return newHelp.toString();
    }

    public List<String> getCommandNames(String string) {
        List<String> commandNames = new ArrayList<String>();
        try {
            if (string == null) {
                string = "";
            }
            List<CommandTarget> locateTargets = locateTargets(string);
            for (CommandTarget commandTarget : locateTargets) {
                String key = commandTarget.getGfshMethodTarget().getKey();
                if (key.startsWith(string)) {
                    commandNames.add(key);
                }
            }
        } catch (IllegalArgumentException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (IllegalAccessException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        } catch (InvocationTargetException e) {
            logWrapper.warning(CliUtil.stackTraceAsString(e));
        }
        return commandNames;
    }

    // ///////////////// Parser interface Methods End //////////////////////////

    private void handleCondition(String message, int errorType, Object errorData) {
        this.handleCondition(message, null, errorType, errorData);
    }

    private void handleCondition(String message, Throwable th, int errorType, Object errorData) {
        if (CliUtil.isGfshVM()) {
            logWarning(message); // TODO - Abhishek add throwable if debug is ON
        } else {
            if (th != null) {
                throw new CommandProcessingException(message + ": " + th.getMessage(), errorType, errorData);
            }
            throw new CommandProcessingException(message, errorType, errorData);
        }
    }

    private void logWarning(String message) {
        if (canLogToConsole()) {
            consoleLogger.warning(CLIConsoleBufferUtil.processMessegeForExtraCharactersFromConsoleBuffer(message));
        } else {
            Gfsh.println(message);
        }
    }

    private boolean canLogToConsole() {
        Gfsh gfsh = Gfsh.getCurrentInstance();
        return gfsh != null && !gfsh.isHeadlessMode() && consoleLogger != null;
    }

    // private void logInfo(String message) {
    // if (consoleLogger != null) {
    // consoleLogger.info(message);
    // } else {
    // Gfsh.println(message);
    // }
    // }
}