From 717a54a7f4a01f01c76d9ce4cb08296b74b678a9 Mon Sep 17 00:00:00 2001 From: Eric Bottard Date: Mon, 14 Apr 2014 11:11:06 +0200 Subject: [PATCH] Revert "SHL-113 Allow for there two be options with two empty keys" This reverts commit 388c04b10a4dbd8f10f45f557eba820bb0e4cc91. --- .../shell/commands/OsCommands.java | 2 + .../shell/core/SimpleParser.java | 8 +++- .../springframework/shell/core/Tokenizer.java | 22 ++++++--- .../core/annotation/PassThroughOptions.java | 45 +++++++++++++++++++ .../shell/core/TokenizerTests.java | 2 - 5 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/springframework/shell/core/annotation/PassThroughOptions.java diff --git a/src/main/java/org/springframework/shell/commands/OsCommands.java b/src/main/java/org/springframework/shell/commands/OsCommands.java index 67fcb5a7..e39ae138 100644 --- a/src/main/java/org/springframework/shell/commands/OsCommands.java +++ b/src/main/java/org/springframework/shell/commands/OsCommands.java @@ -21,6 +21,7 @@ import java.util.logging.Logger; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; +import org.springframework.shell.core.annotation.PassThroughOptions; import org.springframework.shell.support.logging.HandlerUtils; import org.springframework.stereotype.Component; @@ -39,6 +40,7 @@ public class OsCommands implements CommandMarker { private OsOperations osOperations = new OsOperationsImpl(); @CliCommand(value = "!", help = "Allows execution of operating system (OS) commands") + @PassThroughOptions public void command( @CliOption(key = { "", "command" }, mandatory = false, specifiedDefaultValue = "", unspecifiedDefaultValue = "", help = "The command to execute") final String command) { diff --git a/src/main/java/org/springframework/shell/core/SimpleParser.java b/src/main/java/org/springframework/shell/core/SimpleParser.java index 23d3d2a7..ca9c0a04 100644 --- a/src/main/java/org/springframework/shell/core/SimpleParser.java +++ b/src/main/java/org/springframework/shell/core/SimpleParser.java @@ -36,6 +36,7 @@ import java.util.logging.Logger; import org.springframework.shell.core.annotation.CliAvailabilityIndicator; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; +import org.springframework.shell.core.annotation.PassThroughOptions; import org.springframework.shell.event.ParseResult; import org.springframework.shell.support.logging.HandlerUtils; import org.springframework.shell.support.util.ExceptionUtils; @@ -159,8 +160,13 @@ public class SimpleParser implements Parser { // Attempt to parse Map options = null; + // Hint to the parser that options will not be of the form --key=value, but should attempt to parse and + // pass through the command option 'as is' + PassThroughOptions passThroughOptions = methodTarget.getMethod() + .getAnnotation(PassThroughOptions.class); try { - options = new Tokenizer(methodTarget.getRemainingBuffer()).getTokens(); + options = new Tokenizer(methodTarget.getRemainingBuffer(), + false, passThroughOptions != null).getTokens(); } catch (IllegalArgumentException e) { LOGGER.warning(ExceptionUtils.extractRootCause(e).getMessage()); return null; diff --git a/src/main/java/org/springframework/shell/core/Tokenizer.java b/src/main/java/org/springframework/shell/core/Tokenizer.java index e5f55fe6..8156a0ff 100644 --- a/src/main/java/org/springframework/shell/core/Tokenizer.java +++ b/src/main/java/org/springframework/shell/core/Tokenizer.java @@ -19,7 +19,6 @@ package org.springframework.shell.core; import java.util.LinkedHashMap; import java.util.Map; -import org.springframework.shell.support.util.StringUtils; import org.springframework.util.Assert; /** @@ -56,6 +55,12 @@ public class Tokenizer { /** Useful when trying to do auto complete. */ private boolean allowUnbalancedLastQuotedValue; + /** + * When processing 'raw' command options, allow them to be aggregated + * together + */ + private boolean allowMultipleEmptyOptions; + /** * Used to indicate that the last value was indeed half enclosed in quotes. * Useful so that parser can re-add it. @@ -71,8 +76,14 @@ public class Tokenizer { } public Tokenizer(String text, boolean allowUnbalancedLastQuotedValue) { + this(text, allowUnbalancedLastQuotedValue, false); + } + + public Tokenizer(String text, boolean allowUnbalancedLastQuotedValue, + boolean allowMultipleEmptyOptions) { this.buffer = text.toCharArray(); this.allowUnbalancedLastQuotedValue = allowUnbalancedLastQuotedValue; + this.allowMultipleEmptyOptions = allowMultipleEmptyOptions; tokenize(); } @@ -125,9 +136,9 @@ public class Tokenizer { } private void store(String key, String value) { - if (key.length() == 0) { - // collect up all the value for the empty key, common when - // trying to use a default value for a command option. + if (this.allowMultipleEmptyOptions) { + //collect up all the options and reconstruct the full 'string' by appending + //each parsed option together with a space. if (result.containsKey(key)) { String originalValue = result.get(key); result.put(key, originalValue + " " + value); @@ -135,12 +146,9 @@ public class Tokenizer { result.put(key, value); } } else if (result.put(key, value) != null) { - //don't allow key value pairs such as --foo bar --foo buzz" throw new IllegalArgumentException("You cannot specify option '" + key + "' more than once in a single command"); - } - } /** diff --git a/src/main/java/org/springframework/shell/core/annotation/PassThroughOptions.java b/src/main/java/org/springframework/shell/core/annotation/PassThroughOptions.java new file mode 100644 index 00000000..90d6f314 --- /dev/null +++ b/src/main/java/org/springframework/shell/core/annotation/PassThroughOptions.java @@ -0,0 +1,45 @@ +/* + * Copyright 2011-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.shell.core.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * A marker annotation on a method that indicates if the options for this command + * should not be parsed by the shell. + + * + *

+ * An example of when to use this is when passing arguments through to the the underying + * Operating System Shell, e.g using the command " ! ls /tmp" * + *

+ * + * @author Mark Pollack + * @since 1.1 + */ +@Inherited +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface PassThroughOptions { + +} diff --git a/src/test/java/org/springframework/shell/core/TokenizerTests.java b/src/test/java/org/springframework/shell/core/TokenizerTests.java index 82bd15e9..3c0bb7fc 100644 --- a/src/test/java/org/springframework/shell/core/TokenizerTests.java +++ b/src/test/java/org/springframework/shell/core/TokenizerTests.java @@ -24,7 +24,6 @@ import static org.junit.Assert.assertTrue; import java.util.HashMap; import java.util.Map; -import org.junit.Ignore; import org.junit.Test; /** @@ -81,7 +80,6 @@ public class TokenizerTests { tokenize("--foo bar --foo buzz"); } - @Ignore @Test(expected = IllegalArgumentException.class) public void testTwoOptionsSameEmptyKey() { tokenize("bar buzz");