sb.append(evalProperty(string, currentIndex, nextIndex));
currentIndex = nextIndex + 1;
} else if (nextIndex == expressionIndex) {
Stack<Operator> operators = new Stack<Operator>();
Stack<String> operands = new Stack<String>();
Tokenizer tokenizer = new Tokenizer(string, Operator.symbols(), true, false, currentIndex);
boolean closed = false;
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
Operator op = Operator.from(token);
if (op == null) {
operands.push(token);
} else if (op.isWhite()) {
continue;
} else if (op == Operator.OPENVAR) {
if (!tokenizer.hasMoreTokens()) throw new IllegalArgumentException(string);
StringBuilder var = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken();
if ((op = Operator.from(token)) == null || op.isWhite()) {
var.append(token);
} else {
break;
}
}
if (op != Operator.CLOSEVAR) {
throw new IllegalArgumentException("Expected '}' but found " + token + " in " + string);
}
operands.push(evalProperty(var.toString(), 0, var.length()).trim());
} else if (op == Operator.CLOSEVAR) {
// end of expression to be evaluated
closed = true;
break;
} else if (op == Operator.OPENPAR) {
operators.push(op);
} else if (op == Operator.CLOSEPAR) {
while ((op = operators.pop()) != Operator.OPENPAR) {
String second = operands.pop();
String first = operands.pop();
operands.push(op.exec(first, second));
}
} else {
while (true) {
if ((operators.empty()) || (operators.peek() == Operator.OPENPAR) ||
(operators.peek().precedence() < op.precedence())) {
operators.push(op);
break;
}
Operator last = operators.pop();
String second = operands.pop();
String first = operands.pop();
operands.push(last.exec(first, second));
}
}
}
if (!closed) {
throw new IllegalArgumentException("Expression is missing closing '}': " + string);
}
while (!operators.empty()) {
Operator last = operators.pop();
String second = operands.pop();
String first = operands.pop();
operands.push(last.exec(first, second));
}
sb.append(operands.pop());
if (!operands.empty()) {
throw new IllegalArgumentException(operands.size() + " operands not processed: top=" + operands.pop() + " all=" + operands);
}
currentIndex = tokenizer.getPosition();
}
}
return sb.toString();
}