in brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/WildcardGlobs.java [113:319]
public static List<String> getGlobsAfterBraceExpansion(String pattern, boolean allowNumericRanges, PhraseTreatment quoteTreatment, PhraseTreatment parenthesesTreatment) throws InvalidPatternException {
List<ExpressionToExpand> patterns = new ArrayList<ExpressionToExpand>();
List<String> result = new ArrayList<String>();
patterns.add(new ExpressionToExpand("", pattern, ""));
while (!patterns.isEmpty()) {
ExpressionToExpand cs = patterns.remove(0);
StringBuffer resultSoFar = new StringBuffer(cs.resultSoFar);
String operatorStack = cs.operatorStack;
boolean inQuote = operatorStack.contains("\"");
boolean expanded = false;
for (int i=0; i<cs.todo.length(); i++) {
assert !expanded;
char c = cs.todo.charAt(i);
boolean inParen = operatorStack.contains("(") &&
(!inQuote || operatorStack.lastIndexOf('\"')<operatorStack.lastIndexOf('('));
if (inQuote && !(inParen && parenthesesTreatment.equals(PhraseTreatment.INTERIOR_NOT_EXPANDABLE))) {
if (c=='"') {
if (i>0 && cs.todo.charAt(i-1)=='\\') {
//this escaped quote, keep
resultSoFar.append(c);
continue;
}
//unquote
resultSoFar.append(c);
inQuote = false;
if (operatorStack.charAt(operatorStack.length()-1)!='\"')
throw new InvalidPatternException("Quoted string contents not valid, after parsing "+resultSoFar);
operatorStack = operatorStack.substring(0, operatorStack.length()-1);
continue;
}
if (quoteTreatment.equals(PhraseTreatment.INTERIOR_NOT_EXPANDABLE)) {
resultSoFar.append(c);
continue;
}
//interior is expandable, continue parsing as usual below
}
if (inParen) {
if (c==')') {
//unparen
resultSoFar.append(c);
if (operatorStack.charAt(operatorStack.length()-1)!='(')
throw new InvalidPatternException("Parenthetical contents not valid, after parsing "+resultSoFar);
operatorStack = operatorStack.substring(0, operatorStack.length()-1);
continue;
}
if (parenthesesTreatment.equals(PhraseTreatment.INTERIOR_NOT_EXPANDABLE)) {
resultSoFar.append(c);
if (c=='(')
operatorStack+="(";
continue;
}
//interior is expandable, continue parsing as usual below
}
if (c=='"' && !quoteTreatment.equals(PhraseTreatment.NOT_A_SPECIAL_CHAR)) {
resultSoFar.append(c);
inQuote = true;
operatorStack += "\"";
continue;
}
if (c=='(' && !parenthesesTreatment.equals(PhraseTreatment.NOT_A_SPECIAL_CHAR)) {
resultSoFar.append(c);
operatorStack += "(";
continue;
}
if (c!='{') {
resultSoFar.append(c);
continue;
}
//brace.. we will need to expand
expanded = true;
String operatorStackBeforeExpansion = operatorStack;
int braceStartIndex = i;
int tokenStartIndex = i+1;
//find matching close brace
List<String> tokens = new ArrayList<String>();
operatorStack += "{";
while (true) {
if (++i>=cs.todo.length()) {
throw new InvalidPatternException("Curly brace not closed, parsing '"+cs.todo.substring(braceStartIndex)+"' after "+resultSoFar);
}
c = cs.todo.charAt(i);
inParen = operatorStack.contains("(") &&
(!inQuote || operatorStack.lastIndexOf('\"')<operatorStack.lastIndexOf('('));
if (inQuote && !(inParen && parenthesesTreatment.equals(PhraseTreatment.INTERIOR_NOT_EXPANDABLE))) {
if (c=='"') {
if (i>0 && cs.todo.charAt(i-1)=='\\') {
//this is escaped quote, doesn't affect status
continue;
}
//unquote
inQuote = false;
if (operatorStack.charAt(operatorStack.length()-1)!='\"')
throw new InvalidPatternException("Quoted string contents not valid, after parsing "+resultSoFar+cs.todo.substring(braceStartIndex, i));
operatorStack = operatorStack.substring(0, operatorStack.length()-1);
continue;
}
if (quoteTreatment.equals(PhraseTreatment.INTERIOR_NOT_EXPANDABLE)) {
continue;
}
//interior is expandable, continue parsing as usual below
}
if (inParen) {
if (c==')') {
//unparen
if (operatorStack.charAt(operatorStack.length()-1)!='(')
throw new InvalidPatternException("Parenthetical contents not valid, after parsing "+resultSoFar+cs.todo.substring(braceStartIndex, i));
operatorStack = operatorStack.substring(0, operatorStack.length()-1);
continue;
}
if (parenthesesTreatment.equals(PhraseTreatment.INTERIOR_NOT_EXPANDABLE)) {
if (c=='(')
operatorStack+="(";
continue;
}
//interior is expandable, continue parsing as usual below
}
if (c=='"' && !quoteTreatment.equals(PhraseTreatment.NOT_A_SPECIAL_CHAR)) {
inQuote = true;
operatorStack += "\"";
continue;
}
if (c=='(' && !parenthesesTreatment.equals(PhraseTreatment.NOT_A_SPECIAL_CHAR)) {
operatorStack += "(";
continue;
}
if (c=='}') {
if (operatorStack.charAt(operatorStack.length()-1)!='{')
throw new InvalidPatternException("Brace contents not valid, mismatched operators "+operatorStack+" after parsing "+resultSoFar+cs.todo.substring(braceStartIndex, i));
operatorStack = operatorStack.substring(0, operatorStack.length()-1);
if (operatorStack.equals(operatorStackBeforeExpansion)) {
tokens.add(cs.todo.substring(tokenStartIndex, i));
break;
}
continue;
}
if (c==',') {
if (operatorStack.length()==operatorStackBeforeExpansion.length()+1) {
tokens.add(cs.todo.substring(tokenStartIndex, i));
tokenStartIndex = i+1;
continue;
}
continue;
}
if (c=='{') {
operatorStack += c;
continue;
}
//any other char is irrelevant
continue;
}
assert operatorStack.equals(operatorStackBeforeExpansion);
assert cs.todo.charAt(i)=='}';
assert !tokens.isEmpty();
String suffix = cs.todo.substring(i+1);
List<ExpressionToExpand> newPatterns = new ArrayList<ExpressionToExpand>();
for (String token : tokens) {
//System.out.println("adding: "+pre+token+post);
if (allowNumericRanges && token.matches("\\s*[0-9]+\\s*-\\s*[0-9]+\\s*")) {
int dashIndex = token.indexOf('-');
String startS = token.substring(0, dashIndex).trim();
String endS = token.substring(dashIndex+1).trim();
int start = Integer.parseInt(startS);
int end = Integer.parseInt(endS);
if (startS.startsWith("-")) startS=startS.substring(1).trim();
if (endS.startsWith("-")) endS=endS.substring(1).trim();
int minLen = Math.min(startS.length(), endS.length());
for (int ti=start; ti<=end; ti++) {
//partial support for negative numbers, but of course they cannot (yet) be specified in the regex above so it is moot
String tokenI = ""+Math.abs(ti);
while (tokenI.length()<minLen) tokenI = "0"+tokenI;
if (ti<0) tokenI = "-"+tokenI;
newPatterns.add(new ExpressionToExpand(resultSoFar.toString(), tokenI+suffix, operatorStackBeforeExpansion));
}
} else {
newPatterns.add(new ExpressionToExpand(resultSoFar.toString(), token+suffix, operatorStackBeforeExpansion));
}
}
// insert new patterns at the start, so we continue to expand them next
patterns.addAll(0, newPatterns);
break;
}
if (!expanded) {
if (operatorStack.length()>0) {
throw new InvalidPatternException("Unclosed operators "+operatorStack+" parsing "+resultSoFar);
}
result.add(resultSoFar.toString());
}
}
assert !result.isEmpty();
return result;
}