View Javadoc
1 /*** 2 * ExpressionEval.java 3 * 4 * $Author: mballesteros $ 5 * $Date: 2003/11/28 19:18:03 $ 6 * $Revision: 1.1 $ 7 */ 8 package net.sf.jec; 9 10 import java.util.HashMap; 11 import java.util.Iterator; 12 import java.util.Map; 13 14 /*** 15 * <p>ExpressionEval is an expression evaluator.</p> 16 * <p>It works caching class+expr pairs with their associated compiled operator. 17 * Whenever someone calls ExpressionEval for evaluating a 'get' or 'set' 18 * expression on a context 'ctx', it checks for the pair (cxt.getClass() + expr) 19 * in the operator cache. If there's nothing, the expression is compiled on the 20 * context using a {@link ExpressionCompiler} and a new entry is created within 21 * the cache.</p> 22 * <p>So, ExpressionEval grows over the time, filled with pairs of class plus 23 * expression, and its associated operator trees.</p> 24 * <p><b>WARNING:</b>Be careful with using ExpressionEval as a singleton in your 25 * application. For expressions that evaluate on fixed clases, an expression 26 * evaluated on a given context will have always the same operator tree. But 27 * that isn't true for classes with dynamic properties, as those returning 28 * <code>java.util.Map</code>. Anyway, I'm not absolutely sure about this and 29 * it would be great to have a criteria for choosing the singleton option when 30 * possible.</p> 31 * 32 * @author mballesteros 33 */ 34 public class ExpressionEval { 35 36 //-------------------------------------------------------------------------- 37 // INTERNAL FIELDS 38 //-------------------------------------------------------------------------- 39 /*** 40 * Expression compiler. Compiles expressions into operator trees. 41 */ 42 private ExpressionCompiler expressionCompiler; 43 44 /*** 45 * Context classes cache. Each entry holds a "java.lang.Class" as key, and 46 * a "java.util.HashMap" as value, which in turn holds "java.lang.String" 47 * as keys (expressions) and "Operator" objects as values (associated 48 * operators). 49 */ 50 private Map ctxClassCache; 51 52 //-------------------------------------------------------------------------- 53 // REPRESENTATION INVARIANT 54 //-------------------------------------------------------------------------- 55 /*** 56 * Checks the rep invariant, being the following for this class: 57 * 58 * - <code>expressionCompiler and ctxClassCache are never 59 * <code>null</code>. 60 * 61 * - <code>ctxClassCache</code> holds <code>java.lang.Class</code> keys 62 * and <code>java.util.HashMap</code> values. 63 * 64 * - Each <code>ctxClassCache</code> value holds 65 * <code>java.lang.String</code> as keys and <code>Operator</code> 66 * values. 67 * 68 * @return boolean If the rep invariant holds. 69 */ 70 private boolean checkRepInv() { 71 return this.expressionCompiler != null 72 && this.ctxClassCache != null 73 && checkCtxClassCache(); 74 } 75 76 private boolean checkCtxClassCache() { 77 Iterator it = this.ctxClassCache.entrySet().iterator(); 78 while (it.hasNext()) { 79 Map.Entry entry = (Map.Entry) it.next(); 80 if (!(entry.getKey() instanceof Class) 81 || !(entry.getValue() instanceof HashMap)) 82 return false; 83 if (!checkOperatorCache((HashMap) entry.getValue())) 84 return false; 85 } 86 return true; 87 } 88 89 private boolean checkOperatorCache(HashMap operatorCache) { 90 Iterator it = operatorCache.entrySet().iterator(); 91 while (it.hasNext()) { 92 Map.Entry entry = (Map.Entry) it.next(); 93 if (!(entry.getKey() instanceof String) 94 || !(entry.getValue() instanceof Operator)) 95 return false; 96 } 97 return true; 98 } 99 100 //-------------------------------------------------------------------------- 101 // CONSTRUCTORS 102 //-------------------------------------------------------------------------- 103 /*** 104 * Creates a new instance of ExpressionEval 105 * 106 * @param throwExceptions True if evaluation exceptions are desirable 107 */ 108 public ExpressionEval(boolean throwExceptions) { 109 this.expressionCompiler = new ExpressionCompiler(throwExceptions); 110 this.ctxClassCache = new HashMap(); 111 //assert (this.checkRepInv()); 112 } 113 114 /*** 115 * Returns a default expression evaluator that uses a default expression 116 * compiler (see {@link ExpressionCompiler#getDefaultInstance}). 117 * 118 * @param throwExceptions 119 * @return ExpressionEval 120 */ 121 public static ExpressionEval getDefaultInstance(boolean throwExceptions) { 122 return new ExpressionEval( 123 ExpressionCompiler.getDefaultInstance(throwExceptions)); 124 } 125 126 /*** 127 * Creates a new instance of ExpressionEval 128 * 129 * @param expComp The {@link ExpressionCompiler} used to compile 130 * expressions into {@link Operator} objects. 131 */ 132 public ExpressionEval(ExpressionCompiler expComp) { 133 this.expressionCompiler = expComp; 134 this.ctxClassCache = new HashMap(); 135 //assert (this.checkRepInv()); 136 } 137 138 //-------------------------------------------------------------------------- 139 // MUTADORES 140 //-------------------------------------------------------------------------- 141 /*** 142 * Evaluates expression <code>expr</code> over the context <code>ctx</code> 143 * 144 * @param ctx Expression context 145 * @param expr Expression to evaluate 146 * @return Object Final evaluation result 147 * @throws ParseException Thrown when the expression is not well formed 148 * @throws EvaluationException Thrown when, although the expression is 149 * valid, the evaluation cannot be performed. 150 */ 151 // This method calls a mutator, so it's a mutator too. 152 // As it doesn't modify the internal representation, there's no need to 153 // check the invariant. 154 public Object get(Object ctx, String expr) 155 throws ParseException, EvaluationException { 156 Operator op = getCachedOperator(ctx, expr); 157 return op.apply(ctx, ctx); 158 } 159 160 /*** 161 * Sets the expression <code>expr</code> over the context <code>ctx</code> 162 * to the specified value <code>value</code>, so 163 * <code>assert(value==get(ctx, expr))</code> 164 * 165 * @param ctx Expression context 166 * @param expr Expression to evaluate 167 * @param value New expression value 168 * @throws ParseException Thrown when the expression is not well formed 169 * @throws EvaluationException Thrown when, although the expression is 170 * valid, the evaluation cannot be performed. 171 */ 172 // This method calls a mutator, so it's a mutator too. 173 // As it doesn't modify the internal representation, there's no need to 174 // check the invariant. 175 public void set(Object ctx, String expr, Object value) 176 throws ParseException, EvaluationException { 177 178 Operator op = getCachedOperator(ctx, expr); 179 if (op instanceof InvertibleOperator) { 180 ((InvertibleOperator) op).applyInverse(ctx, ctx, value); 181 } else { 182 throw new EvaluationException( 183 "Expression " 184 + expr 185 + " doesn't admit inverse on context: " 186 + ctx); 187 } 188 } 189 190 /*** 191 * This method completely encapsulates the internal data representation. 192 * Provides the function: <code>f(ctx, expr) --> Operator</code> 193 */ 194 public Operator getCachedOperator(Object ctx, String expr) 195 throws ParseException { 196 197 //assert(this.checkRepInv()); // precondition 198 199 Map exprCache = (Map) ctxClassCache.get(ctx.getClass()); 200 if (exprCache == null) { 201 exprCache = new HashMap(); 202 ctxClassCache.put(ctx.getClass(), exprCache); 203 } 204 Operator op = (Operator) exprCache.get(expr); 205 if (op == null) { 206 op = expressionCompiler.compileOperator(expr); 207 //assert(op != null); 208 exprCache.put(expr, op); 209 } 210 211 //assert(this.checkRepInv()); // postcondition 212 213 return op; 214 } 215 216 /*** 217 * Removes all stored operators, so the garbage collector can collect them. 218 */ 219 public void clearOperatorCache() { 220 221 //assert(this.checkRepInv()); // precondition 222 223 this.ctxClassCache = null; 224 this.ctxClassCache = new HashMap(); 225 226 //assert(this.checkRepInv()); // postcondition 227 } 228 229 /*** 230 * Removes all stored operator for objects of the given class 231 * 232 * @param clazz The class whose Operator objects must be cleared 233 */ 234 public void clearOperatorCache(Class clazz) { 235 236 //assert(this.checkRepInv()); // precondition 237 238 this.ctxClassCache.remove(clazz); 239 240 //assert(this.checkRepInv()); // postcondition 241 } 242 }

This page was automatically generated by Maven