/*
* 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);
// }
// }
}