// Copyright � 2004-2005 ASERT. Released under the Canoo Webtest license.
package com.canoo.webtest.util;
/**
* Evaluates a mathematical expression.
*
* @author Paul King
* @author Rob Nielsen
*/
public class Expression
{
private Evaluator fEvaluator;
/**
* a private instance for the static evaluate method
*/
private static Expression sInstance;
/**
* Constructs a new blank expression
*/
public Expression() {
}
/**
* Constructor for the Expression object
*
* @param eval the fEvaluator to use for unknown expressions
*/
public Expression(Evaluator eval) {
setEvaluator(eval);
}
/**
* Sets the Evaluator attribute of the Expression object
*
* @param evaluator The new Evaluator value
*/
public void setEvaluator(Evaluator evaluator) {
this.fEvaluator = evaluator;
}
/**
* Evaluates the expression string and returns the result (which is also available
* with getValue()). Any parse errors will throw a IllegalArgumentException
*
* @param exp the string to parse
* @return the double value of the expression
* @throws IllegalArgumentException
*/
public double evaluate(String exp) {
if (exp == null) {
return 0.0;
}
try {
return Double.parseDouble(exp);
}
catch (NumberFormatException e) {
return evaluate(exp.toCharArray(), 0, exp.length());
}
}
/**
* Evaluates a string found in the expression which isn't an arithmetic
* expression. Calls out to the given fEvaluator.
*
* @param s the substring to evaluate
* @return the double value of the evaluation
*/
protected double evaluateString(String s) {
if (fEvaluator != null) {
return fEvaluator.evaluate(s.trim());
}
throw new IllegalArgumentException("Cannot parse: '" + s + "': No evaluator");
}
/**
* Recursively evaluates the exp contained in the char array e between
* start and end.
*
* @param e the exp
* @param origStart the start index (inclusive) to evaluate from
* @param origEnd the end index (exclusive) to evaluate to
* @return the evaluated exp
* @throws IllegalArgumentException if the exp can't be parsed.
*/
private double evaluate(char[] e, int origStart, int origEnd) {
int start = origStart;
int end = origEnd;
while (start < end && e[start] == ' ') {
start++;
}
if (end == start) {
return 0;
}
while (e[end - 1] == ' ') {
end--;
}
boolean number = true;
int bracket = 0;
for (int add = 0; add < 2; add++) {
for (int i = end - 1; i >= start; i--) {
if (e[i] == ')') {
bracket++;
} else if (e[i] == '(') {
bracket--;
} else if (bracket == 0) {
if (add == 0) {
if (e[i] == '+') {
return evaluate(e, start, i) + evaluate(e, i + 1, end);
}
if (e[i] == '-') {
return evaluate(e, start, i) - evaluate(e, i + 1, end);
}
} else {
if (e[i] == '*') {
return evaluate(e, start, i) * evaluate(e, i + 1, end);
}
if (e[i] == '/') {
return evaluate(e, start, i) / evaluate(e, i + 1, end);
}
if (e[i] == '%') {
return evaluate(e, start, i) % evaluate(e, i + 1, end);
}
}
}
if ((e[i] < '0' || e[i] > '9') && e[i] != '.') {
number = false;
}
}
}
if (e[end - 1] == ')' && e[start] == '(') {
start++;
end--;
if (end == start) {
return 0;
}
return evaluate(e, start, end);
}
String s = new String(e, start, end - start);
if (number) {
return Double.parseDouble(s);
}
return evaluateString(s);
}
/**
* Evaluates a string expression without requiring the creation of an instance.
*
* @param s the expression to evaluate
* @return the double value of the expression
*/
public static double evaluateExpression(String s) {
return evaluateExpression(s, null);
}
/**
* Evaluates a string expression without requiring the creation of an instance.
*
* @param s the expression to evaluate
* @param eval the fEvaluator to parse special values
* @return the double value of the expression
*/
public static double evaluateExpression(String s, Evaluator eval) {
if (sInstance == null) {
sInstance = new Expression(eval);
} else {
sInstance.setEvaluator(eval);
}
return sInstance.evaluate(s);
}
}