GEODE-1912: use Spring shell's parser and delete our own parsing code
Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/809d64d7 Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/809d64d7 Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/809d64d7 Branch: refs/heads/feature/GEODE-1912 Commit: 809d64d712c0e31115b79c424e40fb87b3c46d79 Parents: 2777aec Author: Jinmei Liao <jil...@pivotal.io> Authored: Thu Oct 27 10:24:07 2016 -0700 Committer: Jinmei Liao <jil...@pivotal.io> Committed: Sun Nov 6 18:50:51 2016 -0800 ---------------------------------------------------------------------- .../management/internal/cli/CommandManager.java | 530 ++---- .../internal/cli/GfshParseResult.java | 71 +- .../management/internal/cli/GfshParser.java | 1563 ++---------------- .../geode/management/internal/cli/Launcher.java | 51 +- .../internal/cli/annotation/CliArgument.java | 81 - .../internal/cli/commands/GfshHelpCommands.java | 111 +- .../internal/cli/commands/ShellCommands.java | 418 +++-- .../cli/converters/BooleanConverter.java | 54 - .../internal/cli/converters/EnumConverter.java | 64 - .../internal/cli/converters/HelpConverter.java | 68 - .../cli/converters/HintTopicConverter.java | 71 - .../cli/converters/StringArrayConverter.java | 53 - .../cli/converters/StringListConverter.java | 56 - .../cli/exceptions/CliCommandException.java | 65 - .../exceptions/CliCommandInvalidException.java | 38 - .../CliCommandMultiModeOptionException.java | 48 - .../CliCommandNotAvailableException.java | 35 - .../exceptions/CliCommandOptionException.java | 64 - ...CommandOptionHasMultipleValuesException.java | 47 - .../CliCommandOptionInvalidException.java | 36 - .../CliCommandOptionMissingException.java | 44 - .../CliCommandOptionNotApplicableException.java | 45 - ...liCommandOptionValueConversionException.java | 37 - .../CliCommandOptionValueException.java | 48 - .../CliCommandOptionValueMissingException.java | 45 - .../cli/exceptions/ExceptionGenerator.java | 48 - .../cli/exceptions/ExceptionHandler.java | 92 -- .../management/internal/cli/help/CliTopic.java | 132 -- .../internal/cli/help/utils/HelpUtils.java | 401 ----- .../internal/cli/parser/Argument.java | 71 - .../internal/cli/parser/AvailabilityTarget.java | 106 -- .../internal/cli/parser/CommandTarget.java | 176 -- .../internal/cli/parser/GfshMethodTarget.java | 121 -- .../internal/cli/parser/GfshOptionParser.java | 37 - .../internal/cli/parser/MethodParameter.java | 39 - .../management/internal/cli/parser/Option.java | 217 --- .../internal/cli/parser/OptionSet.java | 128 -- .../internal/cli/parser/Parameter.java | 116 -- .../internal/cli/parser/ParserUtils.java | 186 --- .../internal/cli/parser/SyntaxConstants.java | 34 - .../cli/parser/jopt/JoptOptionParser.java | 302 ---- .../preprocessor/EnclosingCharacters.java | 32 - .../cli/parser/preprocessor/Preprocessor.java | 151 -- .../parser/preprocessor/PreprocessorUtils.java | 327 ---- .../internal/cli/parser/preprocessor/Stack.java | 52 - .../cli/parser/preprocessor/TrimmedInput.java | 44 - .../management/internal/cli/shell/Gfsh.java | 567 +++---- .../internal/cli/shell/MultiCommandHelper.java | 10 +- .../internal/cli/shell/jline/GfshHistory.java | 21 +- .../internal/cli/util/CommandStringBuilder.java | 29 +- .../internal/cli/CommandManagerJUnitTest.java | 137 +- .../internal/cli/GfshParserIntegrationTest.java | 323 +++- .../internal/cli/GfshParserJUnitTest.java | 863 ---------- .../internal/cli/JoptOptionParserTest.java | 527 ------ .../internal/cli/NewGfshParserJUnitTest.java | 76 + .../cli/annotations/CliArgumentJUnitTest.java | 154 -- .../cli/commands/CliCommandTestBase.java | 117 +- .../commands/HelpCommandsIntegrationTest.java | 142 -- .../cli/parser/ParserUtilsJUnitTest.java | 81 - .../preprocessor/PreprocessorJUnitTest.java | 296 ---- .../PreprocessorUtilsJUnitTest.java | 121 -- .../shell/GfshExecutionStrategyJUnitTest.java | 17 +- 62 files changed, 1323 insertions(+), 8713 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/809d64d7/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandManager.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandManager.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandManager.java index 4400445..ab3637d 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandManager.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandManager.java @@ -14,46 +14,47 @@ */ package org.apache.geode.management.internal.cli; +import static org.apache.geode.distributed.ConfigurationProperties.USER_COMMAND_PACKAGES; + import org.apache.geode.distributed.ConfigurationProperties; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.internal.ClassPathLoader; -import org.apache.geode.management.cli.CliMetaData; -import org.apache.geode.management.internal.cli.annotation.CliArgument; -import org.apache.geode.management.internal.cli.help.CliTopic; -import org.apache.geode.management.internal.cli.parser.*; -import org.apache.geode.management.internal.cli.parser.jopt.JoptOptionParser; import org.apache.geode.management.internal.cli.util.ClasspathScanLoadHelper; +import org.springframework.context.ApplicationContextAware; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.Converter; -import org.springframework.shell.core.annotation.CliAvailabilityIndicator; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.*; -import java.util.Map.Entry; - -import static org.apache.geode.distributed.ConfigurationProperties.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.StringTokenizer; /** - * + * + * this only takes care of loading all available command markers and converters from the application * @since GemFire 7.0 */ public class CommandManager { - // 1. Load Commands, availability indicators - Take from GfshParser - // 2. Load Converters - Take from GfshParser - // 3. Load Result Converters - Add - - private static final Object INSTANCE_LOCK = new Object(); - private static CommandManager INSTANCE = null; public static final String USER_CMD_PACKAGES_PROPERTY = DistributionConfig.GEMFIRE_PREFIX + USER_COMMAND_PACKAGES; public static final String USER_CMD_PACKAGES_ENV_VARIABLE = "GEMFIRE_USER_COMMAND_PACKAGES"; + private static final Object INSTANCE_LOCK = new Object(); + private static CommandManager INSTANCE = null; - private Properties cacheProperties; + /** + * List of converters which should be populated first before any command can be added + */ + private final List<Converter<?>> converters = new ArrayList<Converter<?>>(); + + private final List<CommandMarker> commandMarkers = new ArrayList<>(); + private Properties cacheProperties; private LogWrapper logWrapper; private CommandManager(final boolean loadDefaultCommands, final Properties cacheProperties) @@ -61,20 +62,60 @@ public class CommandManager { if (cacheProperties != null) { this.cacheProperties = cacheProperties; } - logWrapper = LogWrapper.getInstance(); if (loadDefaultCommands) { loadCommands(); + } + } + + private static void raiseExceptionIfEmpty(Set<Class<?>> foundClasses, String errorFor) + throws IllegalStateException { + if (foundClasses == null || foundClasses.isEmpty()) { + throw new IllegalStateException( + "Required " + errorFor + " classes were not loaded. Check logs for errors."); + } + } + + public static CommandManager getInstance() throws ClassNotFoundException, IOException { + return getInstance(true); + } - if (logWrapper.fineEnabled()) { - logWrapper.fine("Commands Loaded: " + commands.keySet()); - logWrapper - .fine("Command Availability Indicators Loaded: " + availabilityIndicators.keySet()); - logWrapper.fine("Converters Loaded: " + converters); + public static CommandManager getInstance(Properties cacheProperties) + throws ClassNotFoundException, IOException { + return getInstance(true, cacheProperties); + } + + // For testing. + public static void clearInstance() { + synchronized (INSTANCE_LOCK) { + INSTANCE = null; + } + } + + // This method exists for test code use only ... + /* package */ + static CommandManager getInstance(boolean loadDefaultCommands) + throws ClassNotFoundException, IOException { + return getInstance(loadDefaultCommands, null); + } + + private static CommandManager getInstance(boolean loadDefaultCommands, Properties cacheProperties) + throws ClassNotFoundException, IOException { + synchronized (INSTANCE_LOCK) { + if (INSTANCE == null) { + INSTANCE = new CommandManager(loadDefaultCommands, cacheProperties); } + return INSTANCE; } } + public static CommandManager getExisting() { + // if (INSTANCE == null) { + // throw new IllegalStateException("CommandManager doesn't exist."); + // } + return INSTANCE; + } + private void loadUserCommands() throws ClassNotFoundException, IOException { final Set<String> userCommandPackages = new HashSet<String>(); @@ -137,7 +178,7 @@ public class CommandManager { /** * Loads commands via {@link ServiceLoader} from {@link ClassPathLoader}. - * + * * @since GemFire 8.1 */ private void loadPluginCommands() { @@ -166,6 +207,7 @@ public class CommandManager { // CommandMarkers Set<Class<?>> foundClasses = null; try { + // geode's commands foundClasses = ClasspathScanLoadHelper.loadAndGet( "org.apache.geode.management.internal.cli.commands", CommandMarker.class, true); for (Class<?> klass : foundClasses) { @@ -173,6 +215,24 @@ public class CommandManager { add((CommandMarker) klass.newInstance()); } catch (Exception e) { logWrapper.warning( + "Could not load Command from: " + klass + " due to " + e + .getLocalizedMessage()); // continue + } + } + raiseExceptionIfEmpty(foundClasses, "Commands"); + + // Spring shell's commands + foundClasses = ClasspathScanLoadHelper.loadAndGet( + "org.springframework.shell.commands", CommandMarker.class, true); + for (Class<?> klass : foundClasses) { + if (ApplicationContextAware.class.isAssignableFrom(klass)) { + // skip the ApplicationContextAware commands. i.e. the HelpCommands + continue; + } + try { + add((CommandMarker) klass.newInstance()); + } catch (Exception e) { + logWrapper.warning( "Could not load Command from: " + klass + " due to " + e.getLocalizedMessage()); // continue } } @@ -201,55 +261,25 @@ public class CommandManager { } } raiseExceptionIfEmpty(foundClasses, "Converters"); - } catch (ClassNotFoundException e) { - logWrapper.warning("Could not load Converters due to " + e.getLocalizedMessage()); - throw e; - } catch (IOException e) { - logWrapper.warning("Could not load Converters due to " + e.getLocalizedMessage()); - throw e; - } catch (IllegalStateException e) { - logWrapper.warning(e.getMessage(), e); - throw e; - } - // Roo's Converters - try { + // Spring shell's converters foundClasses = ClasspathScanLoadHelper.loadAndGet("org.springframework.shell.converters", Converter.class, true); for (Class<?> klass : foundClasses) { try { - if (!SHL_CONVERTERS_TOSKIP.contains(klass.getName())) { - add((Converter<?>) klass.newInstance()); - } + add((Converter<?>) klass.newInstance()); } catch (Exception e) { logWrapper.warning( - "Could not load Converter from: " + klass + " due to " + e.getLocalizedMessage()); // continue + "Could not load Converter from: " + klass + " due to " + e + .getLocalizedMessage()); // continue } } raiseExceptionIfEmpty(foundClasses, "Basic Converters"); } catch (ClassNotFoundException e) { - logWrapper.warning("Could not load Default Converters due to " + e.getLocalizedMessage());// TODO - // - - // Abhishek: - // Should - // these - // converters - // be - // moved - // in - // GemFire? + logWrapper.warning("Could not load Converters due to " + e.getLocalizedMessage()); throw e; } catch (IOException e) { - logWrapper.warning("Could not load Default Converters due to " + e.getLocalizedMessage());// TODO - // - - // Abhishek: - // Should - // these - // converters - // be - // moved - // in - // GemFire? + logWrapper.warning("Could not load Converters due to " + e.getLocalizedMessage()); throw e; } catch (IllegalStateException e) { logWrapper.warning(e.getMessage(), e); @@ -257,84 +287,13 @@ public class CommandManager { } } - private static void raiseExceptionIfEmpty(Set<Class<?>> foundClasses, String errorFor) - throws IllegalStateException { - if (foundClasses == null || foundClasses.isEmpty()) { - throw new IllegalStateException( - "Required " + errorFor + " classes were not loaded. Check logs for errors."); - } - } - - public static CommandManager getInstance() throws ClassNotFoundException, IOException { - return getInstance(true); - } - - public static CommandManager getInstance(Properties cacheProperties) - throws ClassNotFoundException, IOException { - return getInstance(true, cacheProperties); - } - - // For testing. - public static void clearInstance() { - synchronized (INSTANCE_LOCK) { - INSTANCE = null; - } - } - - // This method exists for test code use only ... - /* package */static CommandManager getInstance(boolean loadDefaultCommands) - throws ClassNotFoundException, IOException { - return getInstance(loadDefaultCommands, null); - } - - private static CommandManager getInstance(boolean loadDefaultCommands, Properties cacheProperties) - throws ClassNotFoundException, IOException { - synchronized (INSTANCE_LOCK) { - if (INSTANCE == null) { - INSTANCE = new CommandManager(loadDefaultCommands, cacheProperties); - } - return INSTANCE; - } - } - - public static CommandManager getExisting() { - // if (INSTANCE == null) { - // throw new IllegalStateException("CommandManager doesn't exist."); - // } - return INSTANCE; + public List<Converter<?>> getConverters() { + return converters; } - /** Skip some of the Converters from Spring Shell for our customization */ - private static List<String> SHL_CONVERTERS_TOSKIP = new ArrayList<String>(); - static { - // Over-ridden by cggm.internal.cli.converters.BooleanConverter - SHL_CONVERTERS_TOSKIP.add("org.springframework.shell.converters.BooleanConverter"); - // Over-ridden by cggm.internal.cli.converters.EnumConverter - SHL_CONVERTERS_TOSKIP.add("org.springframework.shell.converters.EnumConverter"); + public List<CommandMarker> getCommandMarkers(){ + return commandMarkers; } - - /** - * List of converters which should be populated first before any command can be added - */ - private final List<Converter<?>> converters = new ArrayList<Converter<?>>(); - - /** - * Map of command string and actual CommandTarget object - * - * This map can also be implemented as a trie to support command abbreviation - */ - private final Map<String, CommandTarget> commands = new TreeMap<String, CommandTarget>(); - - /** - * This method will store the all the availabilityIndicators - */ - private final Map<String, AvailabilityTarget> availabilityIndicators = - new HashMap<String, AvailabilityTarget>(); - - /** - */ - private final Map<String, CliTopic> topics = new TreeMap<String, CliTopic>(); - /** * Method to add new Converter * @@ -350,295 +309,6 @@ public class CommandManager { * @param commandMarker */ public void add(CommandMarker commandMarker) { - // First we need to find out all the methods marked with - // Command annotation - Method[] methods = commandMarker.getClass().getMethods(); - for (Method method : methods) { - if (method.getAnnotation(CliCommand.class) != null) { - - // - // First Build the option parser - // - - // Create the empty LinkedLists for storing the argument and - // options - LinkedList<Argument> arguments = new LinkedList<Argument>(); - LinkedList<Option> options = new LinkedList<Option>(); - // Also we need to create the OptionParser for each command - GfshOptionParser optionParser = getOptionParser(); - // Now get all the parameters annotations of the method - Annotation[][] parameterAnnotations = method.getParameterAnnotations(); - // Also get the parameter Types - Class<?>[] parameterTypes = method.getParameterTypes(); - - int parameterNo = 0; - - for (int i = 0; i < parameterAnnotations.length; i++) { - // Get all the annotations for this specific parameter - Annotation[] annotations = parameterAnnotations[i]; - // Also get the parameter type for this parameter - Class<?> parameterType = parameterTypes[i]; - - boolean paramFound = false; - String valueSeparator = CliMetaData.ANNOTATION_NULL_VALUE; - for (Annotation annotation : annotations) { - if (annotation instanceof CliArgument) { - // Here we need to create the argument Object - Argument argumentToAdd = - createArgument((CliArgument) annotation, parameterType, parameterNo); - arguments.add(argumentToAdd); - parameterNo++; - } else if (annotation instanceof CliOption) { - Option createdOption = - createOption((CliOption) annotation, parameterType, parameterNo); - if (!CliMetaData.ANNOTATION_NULL_VALUE.equals(valueSeparator)) { // CliMetaData was - // found earlier - createdOption.setValueSeparator(valueSeparator); - - // reset valueSeparator back to null - valueSeparator = CliMetaData.ANNOTATION_NULL_VALUE; - } else { // CliMetaData is yet to be found - paramFound = true; - } - options.add(createdOption); - parameterNo++; - } else if (annotation instanceof CliMetaData) { - valueSeparator = ((CliMetaData) annotation).valueSeparator(); - if (!CliMetaData.ANNOTATION_NULL_VALUE.equals(valueSeparator)) { - if (paramFound) { // CliOption was detected earlier - Option lastAddedOption = options.getLast(); - lastAddedOption.setValueSeparator(valueSeparator); - // reset valueSeparator back to null - valueSeparator = CliMetaData.ANNOTATION_NULL_VALUE; - } // param not found yet, store valueSeparator value - } else { - // reset valueSeparator back to null - valueSeparator = CliMetaData.ANNOTATION_NULL_VALUE; - } - } - } - } - optionParser.setArguments(arguments); - optionParser.setOptions(options); - - // - // Now build the commandTarget - // - - // First build the MethodTarget for the command Method - GfshMethodTarget gfshMethodTarget = new GfshMethodTarget(method, commandMarker); - - // Fetch the value array from the cliCommand annotation - CliCommand cliCommand = method.getAnnotation(CliCommand.class); - String[] values = cliCommand.value(); - - // First string will point to the command - // rest of them will act as synonyms - String commandName = null; - String[] synonyms = null; - if (values.length > 1) { - synonyms = new String[values.length - 1]; - } - - commandName = values[0]; - - for (int j = 1; j < values.length; j++) { - synonyms[j - 1] = values[j]; - } - - // Create the commandTarget object - CommandTarget commandTarget = new CommandTarget(commandName, synonyms, gfshMethodTarget, - optionParser, null, cliCommand.help()); - - // Now for each string in values put an entry in the commands - // map - for (String string : values) { - if (commands.get(string) == null) { - commands.put(string, commandTarget); - } else { - // TODO Handle collision - logWrapper.info("Multiple commands configured with the same name: " + string); - } - } - - if (CliUtil.isGfshVM()) { - CliMetaData commandMetaData = method.getAnnotation(CliMetaData.class); - if (commandMetaData != null) { - String[] relatedTopics = commandMetaData.relatedTopic(); - // System.out.println("relatedTopic :: "+Arrays.toString(relatedTopics)); - for (String topicName : relatedTopics) { - CliTopic topic = topics.get(topicName); - if (topic == null) { - topic = new CliTopic(topicName); - topics.put(topicName, topic); - } - topic.addCommandTarget(commandTarget); - } - } - } - - } else if (method.getAnnotation(CliAvailabilityIndicator.class) != null) { - // Now add this availability Indicator to the list of - // availability Indicators - CliAvailabilityIndicator cliAvailabilityIndicator = - method.getAnnotation(CliAvailabilityIndicator.class); - - // Create a AvailabilityTarget for this availability Indicator - AvailabilityTarget availabilityIndicator = new AvailabilityTarget(commandMarker, method); - - String[] value = cliAvailabilityIndicator.value(); - for (String string : value) { - availabilityIndicators.put(string, availabilityIndicator); - } - - } - } - // Now we must update all the existing CommandTargets to add - // this availability Indicator if it applies to them - updateAvailabilityIndicators(); - } - - - /** - * Will update all the references to availability Indicators for commands - * - */ - public void updateAvailabilityIndicators() { - for (String string : availabilityIndicators.keySet()) { - CommandTarget commandTarget = commands.get(string); - if (commandTarget != null) { - commandTarget.setAvailabilityIndicator(availabilityIndicators.get(string)); - } - } - } - - /** - * Creates a new {@link Option} instance - * - * @param cliOption - * @param parameterType - * @param parameterNo - * @return Option - */ - public Option createOption(CliOption cliOption, Class<?> parameterType, int parameterNo) { - Option option = new Option(); - - // First set the Option identifiers - List<String> synonyms = new ArrayList<String>(); - for (String string : cliOption.key()) { - if (!option.setLongOption(string)) { - synonyms.add(string); - } - } - option.setSynonyms(synonyms); - if (!(option.getAggregate().size() > 0)) { - logWrapper.warning("Option should have a name"); - } - // Set the option Help - option.setHelp(cliOption.help()); - - // Set whether the option is required or not - option.setRequired(cliOption.mandatory()); - - // Set the fields related to option value - option.setSystemProvided(cliOption.systemProvided()); - option.setSpecifiedDefaultValue(cliOption.specifiedDefaultValue()); - option.setUnspecifiedDefaultValue(cliOption.unspecifiedDefaultValue()); - - // Set the things which are useful for value conversion and - // auto-completion - option.setContext(cliOption.optionContext()); - // Find the matching Converter<?> for this option - option.setConverter(getConverter(parameterType, option.getContext())); - - option.setDataType(parameterType); - option.setParameterNo(parameterNo); - return option; - } - - /** - * Creates a new {@link Argument} instance - * - * @param cliArgument - * @param parameterType - * @param parameterNo - * @return Argument - */ - public Argument createArgument(CliArgument cliArgument, Class<?> parameterType, int parameterNo) { - Argument argument = new Argument(); - argument.setArgumentName(cliArgument.name()); - argument.setContext(cliArgument.argumentContext()); - argument.setConverter(getConverter(parameterType, argument.getContext())); - argument.setHelp(cliArgument.help()); - argument.setRequired(cliArgument.mandatory()); - argument.setDataType(parameterType); - argument.setParameterNo(parameterNo); - argument.setUnspecifiedDefaultValue(cliArgument.unspecifiedDefaultValue()); - argument.setSystemProvided(cliArgument.systemProvided()); - return argument; - } - - /** - * Looks for a matching {@link Converter} - * - * @param parameterType - * @param context - * @return {@link Converter} - */ - public Converter<?> getConverter(Class<?> parameterType, String context) { - for (Converter<?> converter : converters) { - if (converter.supports(parameterType, context)) { - return converter; - } - } - return null; - } - - /** - * For the time being this method returns a {@link JoptOptionParser} object but in the future we - * can change which optionParser should be returned. - * - * @return {@link GfshOptionParser} - */ - private GfshOptionParser getOptionParser() { - return new JoptOptionParser(); - } - - /** - * @return the commands - */ - public Map<String, CommandTarget> getCommands() { - return Collections.unmodifiableMap(commands); - } - - AvailabilityTarget getAvailabilityIndicator(Object key) { - return availabilityIndicators.get(key); - } - - public Set<String> getTopicNames() { - Set<String> topicsNames = topics.keySet(); - return Collections.unmodifiableSet(topicsNames); - } - - public List<CliTopic> getTopics() { - List<CliTopic> topicsList = new ArrayList<CliTopic>(topics.values()); - return Collections.unmodifiableList(topicsList); - } - - public CliTopic getTopic(String topicName) { - CliTopic foundTopic = topics.get(topicName); - - if (foundTopic == null) { - Set<Entry<String, CliTopic>> entries = topics.entrySet(); - - for (Entry<String, CliTopic> entry : entries) { - if (entry.getKey().equalsIgnoreCase(topicName)) { - foundTopic = entry.getValue(); - break; - } - } - } - - return foundTopic; + commandMarkers.add(commandMarker); } } http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/809d64d7/geode-core/src/main/java/org/apache/geode/management/internal/cli/GfshParseResult.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/GfshParseResult.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/GfshParseResult.java index d879e2d..010b9cc 100755 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/GfshParseResult.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/GfshParseResult.java @@ -14,17 +14,20 @@ */ package org.apache.geode.management.internal.cli; +import org.apache.commons.lang.StringUtils; +import org.apache.geode.management.cli.CliMetaData; +import org.apache.geode.management.internal.cli.shell.GfshExecutionStrategy; +import org.apache.geode.management.internal.cli.shell.OperationInvoker; +import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; +import org.springframework.shell.event.ParseResult; + +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import org.springframework.shell.event.ParseResult; - -import org.apache.geode.management.cli.CliMetaData; -import org.apache.geode.management.internal.cli.shell.GfshExecutionStrategy; -import org.apache.geode.management.internal.cli.shell.OperationInvoker; - /** * Immutable representation of the outcome of parsing a given shell line. * Extends * {@link ParseResult} to add a field to specify the command string that was input by the user. @@ -41,7 +44,7 @@ import org.apache.geode.management.internal.cli.shell.OperationInvoker; public class GfshParseResult extends ParseResult { private String userInput; private String commandName; - private Map<String, String> paramValueStringMap; + private Map<String, String> paramValueStringMap = new HashMap<>(); /** * Creates a GfshParseResult instance to represent parsing outcome. @@ -52,12 +55,34 @@ public class GfshParseResult extends ParseResult { * @param userInput user specified commands string */ protected GfshParseResult(final Method method, final Object instance, final Object[] arguments, - final String userInput, final String commandName, - final Map<String, String> parametersAsString) { + final String userInput) { super(method, instance, arguments); - this.userInput = userInput; - this.commandName = commandName; - this.paramValueStringMap = new HashMap<String, String>(parametersAsString); + this.userInput = userInput.trim(); + + CliCommand cliCommand = method.getAnnotation(CliCommand.class); + commandName = cliCommand.value()[0]; + + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + if (arguments == null) { + return; + } + + for (int i = 0; i < arguments.length; i++) { + Object argument = arguments[i]; + if (argument == null) { + continue; + } + + CliOption cliOption = getCliOption(parameterAnnotations, i); + + String argumentAsString; + if (argument instanceof Object[]) { + argumentAsString = StringUtils.join((Object[]) argument, ","); + } else { + argumentAsString = argument.toString(); + } + paramValueStringMap.put(cliOption.key()[0], argumentAsString); + } } /** @@ -67,7 +92,6 @@ public class GfshParseResult extends ParseResult { return userInput; } - /** * @return the unmodifiable paramValueStringMap */ @@ -75,18 +99,17 @@ public class GfshParseResult extends ParseResult { return Collections.unmodifiableMap(paramValueStringMap); } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(GfshParseResult.class.getSimpleName()); - builder.append(" [method=").append(getMethod()); - builder.append(", instance=").append(getInstance()); - builder.append(", arguments=").append(CliUtil.arrayToString(getArguments())); - builder.append("]"); - return builder.toString(); - } - public String getCommandName() { return commandName; } + + private CliOption getCliOption(Annotation[][] parameterAnnotations, int index) { + Annotation[] annotations = parameterAnnotations[index]; + for (Annotation annotation : annotations) { + if (annotation instanceof CliOption) { + return (CliOption) annotation; + } + } + return null; + } }