Add q, Q, V, # flags
Simplify code a bit
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1736022 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java
index cc0350a..c66edd2 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java
@@ -34,9 +34,9 @@
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.PatternSyntaxException;
-import java.util.stream.Collector;
import java.util.stream.Collectors;
@SuppressWarnings("fallthrough")
@@ -57,7 +57,7 @@
}
private final Evaluate evaluate;
- private final boolean inQuote;
+ private boolean inQuote;
public Expander(CharSequence text, Evaluate evaluate, boolean inQuote)
{
@@ -383,7 +383,7 @@
.map(String::valueOf)
.map(s -> "\"" + s + "\"").collect(Collectors.toList());
} else if (expand instanceof Collection) {
- return ((Collection) expand).stream().map(String::valueOf).collect(Collectors.joining(" ", "\"", "\""));
+ return asCollection(expand).stream().map(String::valueOf).collect(Collectors.joining(" ", "\"", "\""));
} else if (expand != null) {
return "\"" + expand.toString() + "\"";
} else {
@@ -655,10 +655,45 @@
boolean flagG = false;
// expand flag
boolean flagExpand = false;
+ // visible chars
+ boolean flagV = false;
+ // codepoint
+ boolean flagSharp = false;
+ // quoting
+ int flagq = 0;
+ boolean flagQ = false;
+ // Parse flags
if (ch == '(') {
getch();
while (ch != EOT && ch != ')') {
switch (ch) {
+ case 'q':
+ if (flagq != 0) {
+ throw new IllegalArgumentException("error in flags");
+ }
+ flagq = 1;
+ if (peek() == '-') {
+ flagq = -1;
+ getch();
+ } else {
+ while (peek() == 'q') {
+ getch();
+ flagq++;
+ }
+ if (peek() == '-') {
+ throw new IllegalArgumentException("error in flags");
+ }
+ }
+ break;
+ case 'Q':
+ flagQ = true;
+ break;
+ case '#':
+ flagSharp = true;
+ break;
+ case 'V':
+ flagV = true;
+ break;
case 'o':
flago = true;
break;
@@ -712,6 +747,29 @@
getch();
}
+ // Map to List conversion
+ boolean _flagk = flagk;
+ boolean _flagv = flagv;
+ Function<Object, Object> mapToList = v -> v instanceof Map ? toList(asMap(v), _flagk, _flagv) : v;
+
+ //
+ // String transformations
+ //
+ BiFunction<Function<String, String>, Object, Object> stringApplyer = (func, v) -> {
+ v = mapToList.apply(v);
+ if (v instanceof Collection) {
+ return asCollection(v).stream()
+ .map(String::valueOf)
+ .map(func)
+ .collect(Collectors.toList());
+ }
+ else if (v != null) {
+ return func.apply(v.toString());
+ }
+ else
+ return null;
+ };
+
if (ch == '+') {
getch();
val = getAndEvaluateName();
@@ -793,9 +851,7 @@
r = "";
}
String m = op.charAt(0) == '#' ? "^" + p : op.charAt(0) == '%' ? p + "$" : p;
- if (val1 instanceof Map) {
- val1 = toList((Map) val1, flagk, flagv);
- }
+ val1 = mapToList.apply(val1);
if (val1 instanceof Collection) {
List<String> l = new ArrayList<>();
for (Object o : ((Collection) val1)) {
@@ -833,27 +889,29 @@
}
}
if (wordSplit) {
- if (val instanceof Map) {
- List<Object> c = toList((Map) val, flagk, flagv);
- val = new ArgList(c);
- }
- else if (val instanceof Collection) {
- if (!(val instanceof ArgList)) {
- List<Object> l = val instanceof List ? (List) val : new ArrayList<>((Collection<?>) val);
- val = new ArgList(l);
- }
+ val = mapToList.apply(val);
+ if (val instanceof Collection) {
+ val = asCollection(val).stream()
+ .map(String::valueOf)
+ .flatMap(s -> Arrays.stream(s.split("\\s+")))
+ .collect(Collectors.toList());
}
else if (val != null) {
- val = new ArgList(Arrays.asList(val.toString().split("\\s")));
+ val = Arrays.asList(val.toString().split("\\s"));
}
}
}
+ //
+ // Subscripts
+ //
while (ch == '[') {
// Token leftParam;
Object left;
+ boolean nLeft = false;
// Token rightParam;
Object right;
+ boolean nRight = false;
getch();
// if (ch == '(') {
// int start = index;
@@ -863,10 +921,19 @@
// } else {
// leftParam = null;
// }
- if (ch == '@') {
+ if (ch == '*') {
left = text.subSequence(index - 1, index);
getch();
+ }
+ else if (ch == '@') {
+ left = text.subSequence(index - 1, index);
+ flagExpand = true;
+ getch();
} else {
+ if (ch == '-') {
+ nLeft = true;
+ getch();
+ }
left = getName(']');
}
if (ch == ',') {
@@ -879,6 +946,10 @@
// } else {
// rightParam = null;
// }
+ if (ch == '-') {
+ nRight = true;
+ getch();
+ }
right = getName(']');
} else {
// rightParam = null;
@@ -890,41 +961,54 @@
getch();
if (right == null) {
left = left instanceof Token ? expand((Token) left) : left;
+ String sLeft = left.toString();
if (val instanceof Map) {
- if (left.toString().equals("@")) {
- val = new ArgList(toList((Map) val, flagk, flagv));
+ if (sLeft.equals("@") || sLeft.equals("*")) {
+ val = toList(asMap(val), flagk, flagv);
}
else {
- val = ((Map) val).get(left.toString());
+ val = ((Map) val).get(sLeft);
}
}
else if (val instanceof List) {
- if (left.toString().equals("@")) {
+ if (sLeft.equals("@") || sLeft.equals("*")) {
val = new ArgList((List) val);
}
else {
- val = ((List) val).get(Integer.parseInt(left.toString()));
+ int iLeft = Integer.parseInt(sLeft);
+ List list = (List) val;
+ val = list.get(nLeft ? list.size() - 1 - iLeft : iLeft);
}
}
else if (val != null) {
- if (left.toString().equals("@")) {
+ if (sLeft.equals("@") || sLeft.equals("*")) {
val = val.toString();
} else {
- val = val.toString().charAt(Integer.parseInt(left.toString()));
+ int iLeft = Integer.parseInt(sLeft);
+ String str = val.toString();
+ val = str.charAt(nLeft ? str.length() - 1 - iLeft : iLeft);
}
}
}
else {
- left = left instanceof Token ? expand((Token) left) : left;
- right = right instanceof Token ? expand((Token) right) : right;
if (val instanceof Map) {
val = null;
}
- else if (val instanceof List) {
- val = ((List) val).subList(Integer.parseInt(left.toString()), Integer.parseInt(right.toString()));
- }
else {
- val = val.toString().substring(Integer.parseInt(left.toString()), Integer.parseInt(right.toString()));
+ left = left instanceof Token ? expand((Token) left) : left;
+ right = right instanceof Token ? expand((Token) right) : right;
+ int iLeft = Integer.parseInt(left.toString());
+ int iRight = Integer.parseInt(right.toString());
+ if (val instanceof List) {
+ List list = (List) val;
+ val = list.subList(nLeft ? list.size() - iLeft : iLeft,
+ nRight ? list.size() - iRight : iRight);
+ }
+ else {
+ String str = val.toString();
+ val = str.substring(nLeft ? str.length() - iLeft : iLeft,
+ nRight ? str.length() - iRight : iRight);
+ }
}
}
}
@@ -936,116 +1020,66 @@
if (flagP) {
val = val != null ? evaluate.get(val.toString()) : null;
}
- if (flagC || flagL || flagU) {
- Function<String, String> cnv;
- if (flagC)
- cnv = s -> s.isEmpty() ? s : s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
- else if (flagL)
- cnv = String::toLowerCase;
- else
- cnv = String::toUpperCase;
- if (val instanceof Map) {
- val = toList((Map) val, flagk, flagv);
- }
+
+ if (flagC) {
+ val = stringApplyer.apply(this::toCamelCase, val);
+ }
+ else if (flagL) {
+ val = stringApplyer.apply(String::toLowerCase, val);
+ }
+ else if (flagU) {
+ val = stringApplyer.apply(String::toUpperCase, val);
+ }
+
+ if (flaga || flagi || flagn || flago || flagO) {
+ val = mapToList.apply(val);
if (val instanceof Collection) {
- List<String> list = new ArrayList<>();
- for (Object o : ((Collection) val)) {
- list.add(o != null ? cnv.apply(o.toString()) : null);
+ List<Object> list;
+ if (flagn) {
+ boolean _flagi = flagi;
+ list = asCollection(val).stream()
+ .map(String::valueOf)
+ .sorted((s1, s2) -> numericCompare(s1, s2, _flagi))
+ .collect(Collectors.toList());
+ } else if (flaga) {
+ list = new ArrayList<>(asCollection(val));
+ } else {
+ Comparator<String> comparator = flagi ? String.CASE_INSENSITIVE_ORDER : Comparator.naturalOrder();
+ list = asCollection(val).stream()
+ .map(String::valueOf)
+ .sorted(comparator)
+ .collect(Collectors.toList());
+ }
+ if (flagO) {
+ Collections.reverse(list);
}
val = list;
- } else if (val != null) {
- val = cnv.apply(val.toString());
}
}
- if (val instanceof Collection && (flaga || flagi || flagn || flago || flagO)) {
- List<Object> list;
- if (flagn) {
- boolean insensitive = flagi;
- Comparator<String> comparator = (s1, s2) -> {
- int i1s = 0, i2s = 0;
- while (i1s < s1.length() && i2s < s2.length()) {
- char c1 = s1.charAt(i1s);
- char c2 = s2.charAt(i2s);
- if (insensitive) {
- c1 = Character.toLowerCase(c1);
- c2 = Character.toLowerCase(c2);
- }
- if (c1 != c2) {
- if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9') {
- break;
- } else {
- return c1 < c2 ? -1 : 1;
- }
- }
- i1s++;
- i2s++;
- }
- while (i1s > 0) {
- char c1 = s1.charAt(i1s-1);
- if (c1 < '0' || c1 > '9') {
- break;
- }
- i1s--;
- }
- while (i2s > 0) {
- char c2 = s2.charAt(i2s-1);
- if (c2 < '0' || c2 > '9') {
- break;
- }
- i2s--;
- }
- int i1e = i1s;
- int i2e = i2s;
- while (i1e < s1.length() - 1) {
- char c1 = s1.charAt(i1e+1);
- if (c1 < '0' || c1 > '9') {
- break;
- }
- i1e++;
- }
- while (i2e < s2.length() - 1) {
- char c2 = s2.charAt(i2e+1);
- if (c2 < '0' || c2 > '9') {
- break;
- }
- i2e++;
- }
- int i1 = Integer.parseInt(s1.substring(i1s, i1e + 1));
- int i2 = Integer.parseInt(s2.substring(i2s, i2e + 1));
- if (i1 < i2) {
- return -1;
- } else if (i1 > i2) {
- return 1;
- } else {
- return i1e > i2e ? -1 : 1;
- }
- };
- list = ((Collection<Object>) val).stream()
- .map(String::valueOf)
- .sorted(comparator)
- .collect(Collectors.toList());
- } else if (flaga) {
- list = new ArrayList<>((Collection<Object>) val);
- } else {
- Comparator<String> comparator = flagi ? String.CASE_INSENSITIVE_ORDER : Comparator.naturalOrder();
- list = ((Collection<Object>) val).stream()
- .map(String::valueOf)
- .sorted(comparator)
- .collect(Collectors.toList());
- }
- if (flagO) {
- Collections.reverse(list);
- }
- val = list;
+ if (flagSharp) {
+ val = stringApplyer.apply(this::sharp, val);
+ }
+
+ if (flagV) {
+ val = stringApplyer.apply(this::visible, val);
+ }
+
+ // Quote
+ if (flagq != 0) {
+ int _flagq = flagq;
+ val = stringApplyer.apply(s -> quote(s, _flagq), val);
+ inQuote = true;
+ }
+ // Unquote
+ else if (flagQ) {
+ val = stringApplyer.apply(this::unquote, val);
}
if (inQuote) {
- if (val instanceof Map) {
- val = toList((Map) val, flagk, flagv);
- }
+ val = mapToList.apply(val);
if (val instanceof Collection) {
- List<Object> l = val instanceof List ? (List) val : new ArrayList<>((Collection) val);
+ List<Object> l = new ArrayList<>(asCollection(val));
if (flagExpand) {
val = new ArgList(l);
} else {
@@ -1065,6 +1099,252 @@
return val;
}
+ private String quote(String s, int flagq) {
+ StringBuilder buf = new StringBuilder();
+ // Backslashes
+ if (flagq == 1) {
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch < 32 || ch >= 127) {
+ buf.append("$'\\").append(Integer.toOctalString(ch)).append("\'");
+ } else if (" !\"#$&'()*;<=>?[\\]{|}~%".indexOf(ch) >= 0) {
+ buf.append("\\").append(ch);
+ } else {
+ buf.append(ch);
+ }
+ }
+ }
+ // Single quotes
+ else if (flagq == 2) {
+ buf.append("'");
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch == '\'') {
+ buf.append("'\\''");
+ } else {
+ buf.append(ch);
+ }
+ }
+ buf.append("'");
+ }
+ // Double quotes
+ else if (flagq == 3) {
+ buf.append("\"");
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if ("\"\\$%".indexOf(ch) >= 0) {
+ buf.append("\\").append(ch);
+ } else {
+ buf.append(ch);
+ }
+ }
+ buf.append("\"");
+ }
+ // Posix
+ else if (flagq == 4) {
+ buf.append("$'");
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch < 32 || ch >= 127) {
+ buf.append("\\").append(Integer.toOctalString(ch));
+ } else {
+ switch (ch) {
+ case '\n':
+ buf.append("\\n");
+ break;
+ case '\t':
+ buf.append("\\t");
+ break;
+ case '\r':
+ buf.append("\\r");
+ break;
+ case '\'':
+ buf.append("\\'");
+ break;
+ default:
+ buf.append(ch);
+ break;
+ }
+ }
+ }
+ buf.append("'");
+ }
+ // Readable
+ else {
+ boolean needQuotes = false;
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch < 32 || ch >= 127 || " !\"#$&'()*;<=>?[\\]{|}~%".indexOf(ch) >= 0) {
+ needQuotes = true;
+ break;
+ }
+ }
+ return needQuotes ? quote(s, 2) : s;
+ }
+ return buf.toString();
+ }
+
+ private String unquote(String arg) {
+ boolean hasEscape = false;
+ for (int i = 0; i < arg.length(); i++) {
+ int c = arg.charAt(i);
+ if (c == '\\' || c == '"' || c == '\'') {
+ hasEscape = true;
+ break;
+ }
+ }
+ if (!hasEscape) {
+ return arg;
+ }
+ boolean singleQuoted = false;
+ boolean doubleQuoted = false;
+ boolean escaped = false;
+ StringBuilder buf = new StringBuilder(arg.length());
+ for (int i = 0; i < arg.length(); i++) {
+ char c = arg.charAt(i);
+ if (doubleQuoted && escaped) {
+ if (c != '"' && c != '\\' && c != '$' && c != '%') {
+ buf.append('\\');
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ else if (escaped) {
+ buf.append(c);
+ escaped = false;
+ }
+ else if (singleQuoted) {
+ if (c == '\'') {
+ singleQuoted = false;
+ } else {
+ buf.append(c);
+ }
+ }
+ else if (doubleQuoted) {
+ if (c == '\\') {
+ escaped = true;
+ }
+ else if (c == '\"') {
+ doubleQuoted = false;
+ }
+ else {
+ buf.append(c);
+ }
+ }
+ else if (c == '\\') {
+ escaped = true;
+ }
+ else if (c == '\'') {
+ singleQuoted = true;
+ }
+ else if (c == '"') {
+ doubleQuoted = true;
+ }
+ else {
+ buf.append(c);
+ }
+ }
+ return buf.toString();
+ }
+
+ private int numericCompare(String s1, String s2, boolean caseInsensitive) {
+ int i1s = 0, i2s = 0;
+ while (i1s < s1.length() && i2s < s2.length()) {
+ char c1 = s1.charAt(i1s);
+ char c2 = s2.charAt(i2s);
+ if (caseInsensitive) {
+ c1 = Character.toLowerCase(c1);
+ c2 = Character.toLowerCase(c2);
+ }
+ if (c1 != c2) {
+ if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9') {
+ break;
+ } else {
+ return c1 < c2 ? -1 : 1;
+ }
+ }
+ i1s++;
+ i2s++;
+ }
+ while (i1s > 0) {
+ char c1 = s1.charAt(i1s - 1);
+ if (c1 < '0' || c1 > '9') {
+ break;
+ }
+ i1s--;
+ }
+ while (i2s > 0) {
+ char c2 = s2.charAt(i2s - 1);
+ if (c2 < '0' || c2 > '9') {
+ break;
+ }
+ i2s--;
+ }
+ int i1e = i1s;
+ int i2e = i2s;
+ while (i1e < s1.length() - 1) {
+ char c1 = s1.charAt(i1e + 1);
+ if (c1 < '0' || c1 > '9') {
+ break;
+ }
+ i1e++;
+ }
+ while (i2e < s2.length() - 1) {
+ char c2 = s2.charAt(i2e + 1);
+ if (c2 < '0' || c2 > '9') {
+ break;
+ }
+ i2e++;
+ }
+ int i1 = Integer.parseInt(s1.substring(i1s, i1e + 1));
+ int i2 = Integer.parseInt(s2.substring(i2s, i2e + 1));
+ if (i1 < i2) {
+ return -1;
+ } else if (i1 > i2) {
+ return 1;
+ } else {
+ return i1e > i2e ? -1 : 1;
+ }
+ }
+
+ private String toCamelCase(String s) {
+ return s.isEmpty() ? s : s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
+ }
+
+ private String sharp(String s) {
+ int codepoint = 0;
+ try {
+ codepoint = Integer.parseInt(s);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ return new String(Character.toChars(codepoint));
+ }
+
+ private String visible(String s) {
+ StringBuilder sb = new StringBuilder(s.length() * 2);
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch < 32) {
+ sb.append('^');
+ sb.append((char)(ch + '@'));
+ } else {
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ private Collection<Object> asCollection(Object val) {
+ return (Collection) val;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<Object, Object> asMap(Object val) {
+ return (Map) val;
+ }
+
private List<Object> toList(Map<Object, Object> val1, boolean flagk, boolean flagv) {
List<Object> l = new ArrayList<>();
if (flagk && flagv) {
@@ -1165,109 +1445,107 @@
boolean inGroup = false;
StringBuilder sb = new StringBuilder();
int index = 0;
- while (true) {
- while (index < str.length()) {
- char ch = str.charAt(index++);
- switch (ch) {
- case '*':
- sb.append(shortest ? ".*?" : ".*");
- break;
- case ',':
- if (inGroup) {
- sb.append(")|(?:");
- } else {
- sb.append(',');
- }
- break;
- case '?':
- sb.append(".");
- break;
- case '[':
- sb.append("[");
- if (next(str, index) == '^') {
- sb.append("\\^");
+ while (index < str.length()) {
+ char ch = str.charAt(index++);
+ switch (ch) {
+ case '*':
+ sb.append(shortest ? ".*?" : ".*");
+ break;
+ case ',':
+ if (inGroup) {
+ sb.append(")|(?:");
+ } else {
+ sb.append(',');
+ }
+ break;
+ case '?':
+ sb.append(".");
+ break;
+ case '[':
+ sb.append("[");
+ if (next(str, index) == '^') {
+ sb.append("\\^");
+ ++index;
+ } else {
+ if (next(str, index) == '!') {
+ sb.append('^');
++index;
- } else {
- if (next(str, index) == '!') {
- sb.append('^');
- ++index;
- }
- if (next(str, index) == '-') {
- sb.append('-');
- ++index;
- }
}
- boolean inLeft = false;
- char left = 0;
- while (index < str.length()) {
- ch = str.charAt(index++);
- if (ch == ']') {
- break;
- }
- if (ch == '\\' || ch == '[' || ch == '&' && next(str, index) == '&') {
- sb.append('\\');
- }
- sb.append(ch);
- if (ch == '-') {
- if (!inLeft) {
- throw new PatternSyntaxException("Invalid range", str, index - 1);
- }
- if ((ch = next(str, index++)) == EOL || ch == ']') {
- break;
- }
- if (ch < left) {
- throw new PatternSyntaxException("Invalid range", str, index - 3);
- }
- sb.append(ch);
- inLeft = false;
- } else {
- inLeft = true;
- left = ch;
- }
+ if (next(str, index) == '-') {
+ sb.append('-');
+ ++index;
}
- if (ch != ']') {
- throw new PatternSyntaxException("Missing \']", str, index - 1);
+ }
+ boolean inLeft = false;
+ char left = 0;
+ while (index < str.length()) {
+ ch = str.charAt(index++);
+ if (ch == ']') {
+ break;
}
- sb.append("]");
- break;
- case '\\':
- if (index == str.length()) {
- throw new PatternSyntaxException("No character to escape", str, index - 1);
- }
- char ch2 = str.charAt(index++);
- if (isGlobMeta(ch2) || isRegexMeta(ch2)) {
- sb.append('\\');
- }
- sb.append(ch2);
- break;
- case '{':
- if (inGroup) {
- throw new PatternSyntaxException("Cannot nest groups", str, index - 1);
- }
- sb.append("(?:(?:");
- inGroup = true;
- break;
- case '}':
- if (inGroup) {
- sb.append("))");
- inGroup = false;
- } else {
- sb.append('}');
- }
- break;
- default:
- if (isRegexMeta(ch)) {
+ if (ch == '\\' || ch == '[' || ch == '&' && next(str, index) == '&') {
sb.append('\\');
}
sb.append(ch);
- break;
- }
+ if (ch == '-') {
+ if (!inLeft) {
+ throw new PatternSyntaxException("Invalid range", str, index - 1);
+ }
+ if ((ch = next(str, index++)) == EOL || ch == ']') {
+ break;
+ }
+ if (ch < left) {
+ throw new PatternSyntaxException("Invalid range", str, index - 3);
+ }
+ sb.append(ch);
+ inLeft = false;
+ } else {
+ inLeft = true;
+ left = ch;
+ }
+ }
+ if (ch != ']') {
+ throw new PatternSyntaxException("Missing \']", str, index - 1);
+ }
+ sb.append("]");
+ break;
+ case '\\':
+ if (index == str.length()) {
+ throw new PatternSyntaxException("No character to escape", str, index - 1);
+ }
+ char ch2 = str.charAt(index++);
+ if (isGlobMeta(ch2) || isRegexMeta(ch2)) {
+ sb.append('\\');
+ }
+ sb.append(ch2);
+ break;
+ case '{':
+ if (inGroup) {
+ throw new PatternSyntaxException("Cannot nest groups", str, index - 1);
+ }
+ sb.append("(?:(?:");
+ inGroup = true;
+ break;
+ case '}':
+ if (inGroup) {
+ sb.append("))");
+ inGroup = false;
+ } else {
+ sb.append('}');
+ }
+ break;
+ default:
+ if (isRegexMeta(ch)) {
+ sb.append('\\');
+ }
+ sb.append(ch);
+ break;
}
- if (inGroup) {
- throw new PatternSyntaxException("Missing \'}", str, index - 1);
- }
- return sb.toString();
}
+ if (inGroup) {
+ throw new PatternSyntaxException("Missing \'}", str, index - 1);
+ }
+ return sb.toString();
}
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
index e4477af..3664413 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
@@ -23,9 +23,11 @@
import java.io.PrintStream;
import java.net.URI;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
@@ -183,15 +185,18 @@
assertEquals("baz", expand("${map[a1]}"));
assertEquals("baz", expand("${map[$key]}"));
- assertEquals("az", expand("${map[a1][1,3]}"));
+ assertEquals("az", expand("${map[a1][1,-0]}"));
assertEquals("AZ", expand("${(U)map[a1][1,3]}"));
assertEquals(map, expand("${map}"));
assertEquals("baz bar foo", expand("\"${map}\""));
+ assertEquals("baz bar foo", expand("\"${map[*]}\""));
assertEquals(Arrays.asList("baz", "bar", "foo"), expand("\"${map[@]}\""));
assertEquals(Arrays.asList("a1", "a2", "b1"), expand("\"${(k)map[@]}\""));
assertEquals(Arrays.asList("a1", "baz", "a2", "bar", "b1", "foo"), expand("\"${(kv)map[@]}\""));
assertEquals(Arrays.asList("a2", "bar"), expand("${${(kv)map[@]}[2,4]}"));
assertEquals(Arrays.asList("a2", "bar"), expand("${${(kv)=map}[2,4]}"));
+
+ // TODO: test subscripts on array resulting in a single element
}
@Test
@@ -217,6 +222,41 @@
}
@Test
+ public void testQuotes() throws Exception {
+ vars.clear();
+ vars.put("foo", "\"{a'}\\b");
+ vars.put("q1", "\"foo\"");
+
+ assertEquals("\\\"\\{a\\'\\}\\\\b", expand("${(q)foo}"));
+ assertEquals("'\"{a'\\''}\\b'", expand("${(qq)foo}"));
+ assertEquals("\"\\\"{a'}\\\\b\"", expand("${(qqq)foo}"));
+ assertEquals("$'\"{a\\'}\\b'", expand("${(qqqq)foo}"));
+ assertEquals("'\"{a'\\''}\\b'", expand("${(q-)foo}"));
+
+ assertEquals("foo", expand("${(Q)q1}"));
+ }
+
+ @Test
+ public void testChars() throws Exception {
+ vars.clear();
+
+ List<Integer> array = new ArrayList<>();
+ for (int i = 0; i < 64; i++) {
+ array.add(i);
+ }
+ vars.put("array", array);
+
+ assertEquals(Arrays.asList("^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G",
+ "^H", "^I", "^J", "^K", "^L", "^M", "^N", "^O",
+ "^P", "^Q", "^R", "^S", "^T", "^U", "^V", "^W",
+ "^X", "^Y", "^Z", "^\\[", "^\\\\", "^\\]", "^^", "^_",
+ "\\ ", "\\!", "\\\"", "\\#", "\\$", "\\%", "\\&", "\\'",
+ "\\(", "\\)", "\\*", "+", ",", "-", ".", "/",
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", ":", "\\;", "\\<", "\\=", "\\>", "\\?"), expand("${(qV#)array}"));
+ }
+
+ @Test
public void testSorting() throws Exception {
vars.clear();
vars.put("array", Arrays.asList("foo1", "foo02", "foo2", "fOo3", "Foo20", "foo23"));