001: // Copyright © 2004-2005 ASERT. Released under the Canoo Webtest license.
002: package com.canoo.webtest.util;
003:
004: /**
005: * Evaluates a mathematical expression.
006: *
007: * @author Paul King
008: * @author Rob Nielsen
009: */
010: public class Expression {
011:
012: private Evaluator fEvaluator;
013: /**
014: * a private instance for the static evaluate method
015: */
016: private static Expression sInstance;
017:
018: /**
019: * Constructs a new blank expression
020: */
021: public Expression() {
022: }
023:
024: /**
025: * Constructor for the Expression object
026: *
027: * @param eval the fEvaluator to use for unknown expressions
028: */
029: public Expression(Evaluator eval) {
030: setEvaluator(eval);
031: }
032:
033: /**
034: * Sets the Evaluator attribute of the Expression object
035: *
036: * @param evaluator The new Evaluator value
037: */
038: public void setEvaluator(Evaluator evaluator) {
039: this .fEvaluator = evaluator;
040: }
041:
042: /**
043: * Evaluates the expression string and returns the result (which is also available
044: * with getValue()). Any parse errors will throw a IllegalArgumentException
045: *
046: * @param exp the string to parse
047: * @return the double value of the expression
048: * @throws IllegalArgumentException
049: */
050: public double evaluate(String exp) {
051: if (exp == null) {
052: return 0.0;
053: }
054: try {
055: return Double.parseDouble(exp);
056: } catch (NumberFormatException e) {
057: return evaluate(exp.toCharArray(), 0, exp.length());
058: }
059: }
060:
061: /**
062: * Evaluates a string found in the expression which isn't an arithmetic
063: * expression. Calls out to the given fEvaluator.
064: *
065: * @param s the substring to evaluate
066: * @return the double value of the evaluation
067: */
068: protected double evaluateString(String s) {
069: if (fEvaluator != null) {
070: return fEvaluator.evaluate(s.trim());
071: }
072: throw new IllegalArgumentException("Cannot parse: '" + s
073: + "': No evaluator");
074: }
075:
076: /**
077: * Recursively evaluates the exp contained in the char array e between
078: * start and end.
079: *
080: * @param e the exp
081: * @param origStart the start index (inclusive) to evaluate from
082: * @param origEnd the end index (exclusive) to evaluate to
083: * @return the evaluated exp
084: * @throws IllegalArgumentException if the exp can't be parsed.
085: */
086: private double evaluate(char[] e, int origStart, int origEnd) {
087: int start = origStart;
088: int end = origEnd;
089: while (start < end && e[start] == ' ') {
090: start++;
091: }
092: if (end == start) {
093: return 0;
094: }
095:
096: while (e[end - 1] == ' ') {
097: end--;
098: }
099:
100: boolean number = true;
101: int bracket = 0;
102: for (int add = 0; add < 2; add++) {
103: for (int i = end - 1; i >= start; i--) {
104: if (e[i] == ')') {
105: bracket++;
106: } else if (e[i] == '(') {
107: bracket--;
108: } else if (bracket == 0) {
109: if (add == 0) {
110: if (e[i] == '+') {
111: return evaluate(e, start, i)
112: + evaluate(e, i + 1, end);
113: }
114: if (e[i] == '-') {
115: return evaluate(e, start, i)
116: - evaluate(e, i + 1, end);
117: }
118: } else {
119: if (e[i] == '*') {
120: return evaluate(e, start, i)
121: * evaluate(e, i + 1, end);
122: }
123: if (e[i] == '/') {
124: return evaluate(e, start, i)
125: / evaluate(e, i + 1, end);
126: }
127: if (e[i] == '%') {
128: return evaluate(e, start, i)
129: % evaluate(e, i + 1, end);
130: }
131: }
132: }
133: if ((e[i] < '0' || e[i] > '9') && e[i] != '.') {
134: number = false;
135: }
136: }
137: }
138: if (e[end - 1] == ')' && e[start] == '(') {
139: start++;
140: end--;
141: if (end == start) {
142: return 0;
143: }
144: return evaluate(e, start, end);
145: }
146:
147: String s = new String(e, start, end - start);
148: if (number) {
149: return Double.parseDouble(s);
150: }
151: return evaluateString(s);
152: }
153:
154: /**
155: * Evaluates a string expression without requiring the creation of an instance.
156: *
157: * @param s the expression to evaluate
158: * @return the double value of the expression
159: */
160: public static double evaluateExpression(String s) {
161: return evaluateExpression(s, null);
162: }
163:
164: /**
165: * Evaluates a string expression without requiring the creation of an instance.
166: *
167: * @param s the expression to evaluate
168: * @param eval the fEvaluator to parse special values
169: * @return the double value of the expression
170: */
171: public static double evaluateExpression(String s, Evaluator eval) {
172: if (sInstance == null) {
173: sInstance = new Expression(eval);
174: } else {
175: sInstance.setEvaluator(eval);
176: }
177:
178: return sInstance.evaluate(s);
179: }
180: }
|