diff --git a/src/main/java/org/springframework/shell/core/Tokenizer.java b/src/main/java/org/springframework/shell/core/Tokenizer.java
index 890ad8f1..5cabe74a 100644
--- a/src/main/java/org/springframework/shell/core/Tokenizer.java
+++ b/src/main/java/org/springframework/shell/core/Tokenizer.java
@@ -73,7 +73,7 @@ public class Tokenizer {
}
private void eatWhiteSpace() {
- while (pos < buffer.length && buffer[pos] == ' ') {
+ while (lookAhead(' ')) {
pos++;
}
}
@@ -92,10 +92,23 @@ public class Tokenizer {
}
/**
- * Consume either {@code --key=value} or just {@code value}, eating extra spaces.
+ * Return true if the remaining buffer matches the given String (return false if there is not enough input).
+ */
+ private boolean lookAhead(char... toMatch) {
+ for (int i = 0; i < toMatch.length; i++) {
+ if (pos + i >= buffer.length || buffer[pos + i] != toMatch[i]) {
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * Consume either {@code --key[=value]} or just {@code value}, eating extra spaces.
*/
private void eatKeyValuePair() {
- if (buffer[pos] == '-' && pos + 1 < buffer.length && buffer[pos + 1] == '-') {
+ if (lookAhead('-', '-')) {
pos += 2;
eatKeyEqualsValue();
}
@@ -113,6 +126,9 @@ public class Tokenizer {
}
}
+ /**
+ * Eat a value that may be enclosed in some delimiters.
+ */
private String eatValue() {
StringBuilder sb = new StringBuilder();
char endDelimiter = ' ';
@@ -124,7 +140,7 @@ public class Tokenizer {
lastValueDelimiter = endDelimiter;
lastValueStartOffset = pos;
while (pos < buffer.length && buffer[pos] != endDelimiter) {
- if (buffer[pos] == ESCAPE_CHAR && pos + 1 < buffer.length && buffer[pos + 1] == endDelimiter) {
+ if (lookAhead(ESCAPE_CHAR, endDelimiter)) {
sb.append(endDelimiter);
pos += 2;
continue;
@@ -191,25 +207,32 @@ public class Tokenizer {
}
/**
- * Consume a full @code{--key value} pair *unless* we're at the very end, in which case allow for @code {--key},
- * using "" for the value.
+ * Consume a full {@code --key value} pair *unless*
+ *
+ * - we're at the very end,
+ * - or the next token starts with {@code --}
+ *
+ * in which case allow for just {@code --key}, using "" for the value.
*/
private void eatKeyEqualsValue() {
String key = eatKey();
eatWhiteSpace();
String value;
- if (pos < buffer.length) {
- value = eatValue();
- }
- else {
+ // We're at the very end or it's a valueless option
+ // Make last* fields consistent
+ if (pos >= buffer.length || lookAhead('-', '-')) {
lastValueDelimiter = ' ';
lastValueStartOffset = pos;
value = "";
}
- if (!key.equals("") || !key.equals(value)) {
- // Don't store the ""="" that would result from having a pending " --" at the end
- store(key, value);
+ else {
+ value = eatValue();
}
+ // Don't store the ""="" that would result from having a pending " --" at the end
+ if (key.equals("") && value.equals("")) {
+ return;
+ }
+ store(key, value);
}
private String eatKey() {
diff --git a/src/test/java/org/springframework/shell/core/TokenizerTests.java b/src/test/java/org/springframework/shell/core/TokenizerTests.java
index e8cae0f6..3c0bb7fc 100644
--- a/src/test/java/org/springframework/shell/core/TokenizerTests.java
+++ b/src/test/java/org/springframework/shell/core/TokenizerTests.java
@@ -152,6 +152,36 @@ public class TokenizerTests {
assertEquals(singletonMap("foo", "bar"), result);
}
+ @Test
+ public void testKeyWithDefaultValueAtEnd() {
+ Map result = tokenize("--foo bar --recursive");
+ Map expected = new HashMap();
+ expected.put("foo", "bar");
+ expected.put("recursive", "");
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testKeyWithDefaultValueNotAtEnd() {
+ Map result = tokenize("--foo bar --recursive --wizz blow");
+ Map expected = new HashMap();
+ expected.put("foo", "bar");
+ expected.put("recursive", "");
+ expected.put("wizz", "blow");
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testValueThatStartsWithDashDashStillSupportedIfQuoted() {
+ Map result = tokenize("--foo bar --recursive \"--wizz\" blow");
+ Map expected = new HashMap();
+ expected.put("foo", "bar");
+ expected.put("recursive", "--wizz");
+ expected.put("", "blow");
+ assertEquals(expected, result);
+
+ }
+
private Map tokenize(String what) {
return new Tokenizer(what).getTokens();
}