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