1 /***
2 * ExpressionCompiler.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.ArrayList;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14
15 import net.sf.jec.functions.CurrencyFunction;
16 import net.sf.jec.functions.DateFunction;
17 import net.sf.jec.functions.PercentFunction;
18 import net.sf.jec.namedop.AvgOperator;
19 import net.sf.jec.namedop.CountOperator;
20 import net.sf.jec.namedop.GroupByOperator;
21 import net.sf.jec.namedop.IfOperator;
22 import net.sf.jec.namedop.MaxOperator;
23 import net.sf.jec.namedop.MinOperator;
24 import net.sf.jec.namedop.OrderByOperator;
25 import net.sf.jec.namedop.SelectOperator;
26 import net.sf.jec.namedop.SumOperator;
27 import net.sf.jec.namedop.TableOperator;
28
29 /*** This class precompiles bean expressions so their evaluation becomes faster.
30 *
31 * @author mballesteros
32 */
33 public class ExpressionCompiler {
34
35 /*** A map of defined functions
36 */
37 private Map namedOperatorMap;
38 private Map functionMap;
39
40 private boolean throwExceptions;
41
42 /*** Creates a new ExpressionCompiler that uses the specified ObjectConverter
43 */
44 public ExpressionCompiler(boolean throwExceptions) {
45 this.throwExceptions = throwExceptions;
46 this.namedOperatorMap = new HashMap(8);
47 this.functionMap = new HashMap(3);
48 }
49
50 /***
51 * <p>Returns an {@link ExpressionCompiler} with default operators and
52 * functions. It is a convenience method that creates a new ExpressionCompiler
53 * and adds all the basic operators and functions with compehensive names.
54 * The complete list of added operators and functions is:<p>
55 * <ul>
56 * <li><b>Operators:</b>
57 * <ol>
58 * <li><b>$avg</b>: See {@link AvgOperator}</li>
59 * <li><b>$count</b>: See {@link CountOperator}</li>
60 * <li><b>$groupby</b>: See {@link GroupByOperator}</li>
61 * <li><b>$max</b>: See {@link MaxOperator}</li>
62 * <li><b>$min</b>: See {@link MinOperator}</li>
63 * <li><b>$orderby</b>: See {@link OrderByOperator}</li>
64 * <li><b>$select</b>: See {@link SelectOperator}</li>
65 * <li><b>$sum</b>: See {@link SumOperator}</li>
66 * <li><b>$table</b>: See {@link TableOperator}</li>
67 * </ol>
68 * </li>
69 * <li><b>Functions:</b>
70 * <ol>
71 * <li><b>$currency</b>: See {@link CurrencyOperator}</li>
72 * <li><b>$date</b>: See {@link DateFunction} with "dd/MM/yyyy"</li>
73 * <li><b>$percent</b>: See {@link PercentOperator}</li>
74 * </ol>
75 * </li>
76 * </ul>
77 * @param throwExceptions
78 * @return ExpressionCompiler
79 */
80 public static ExpressionCompiler getDefaultInstance(boolean throwExceptions) {
81 ExpressionCompiler ec = new ExpressionCompiler(throwExceptions);
82 ec.addOperator("$avg", AvgOperator.class);
83 ec.addOperator("$count", CountOperator.class);
84 ec.addOperator("$groupby", GroupByOperator.class);
85 ec.addOperator("$if", IfOperator.class);
86 ec.addOperator("$max", MaxOperator.class);
87 ec.addOperator("$min", MinOperator.class);
88 ec.addOperator("$orderby", OrderByOperator.class);
89 ec.addOperator("$sum", SumOperator.class);
90 ec.addOperator("$select", SelectOperator.class);
91 ec.addOperator("$table", TableOperator.class);
92 ec.addFunction("$currency", new CurrencyFunction());
93 ec.addFunction("$date", new DateFunction("dd/MM/yyyy"));
94 ec.addFunction("$percent", new PercentFunction());
95 return ec;
96 }
97
98 /*** Adds a new function to the compiler
99 */
100 public void addOperator(String name, Class operatorClass) {
101 this.namedOperatorMap.put(name, operatorClass);
102 }
103
104 /*** Adds a new function to the compiler
105 */
106 public void addFunction(String name, Function function) {
107 this.functionMap.put(name, function);
108 }
109
110 /*** Compiles an expression into an operator
111 */
112 public Operator compileOperator(String expr) throws ParseException {
113 // Build an expression lexer
114 Lexer lex = new Lexer(expr);
115
116 // Parse expression
117 return parse(lex);
118 }
119
120 // Root expression parse level
121 private Operator parse(Lexer lex) throws ParseException {
122 return parse5(lex);
123 }
124
125 // Parse level 5: '='
126 private Operator parse5(Lexer lex) throws ParseException {
127 Operator out = parse4(lex);
128
129 lex.next();
130 String token = lex.getToken();
131
132 // Operator: '='
133 if (token.equals("=")) {
134 if (!(out instanceof InvertibleOperator))
135 throw new ParseException(
136 "Cannot assign to a non invertible operator: " + out);
137 lex.next();
138 Operator nextOp = parse5(lex);
139 out =
140 new AssignOperator(
141 (InvertibleOperator) out,
142 nextOp,
143 throwExceptions);
144 } else {
145 lex.previous();
146 return out;
147 }
148 return out;
149 }
150
151 // Parse level 4: boolean operators
152 private Operator parse4(Lexer lex) throws ParseException {
153 Operator out = parse3(lex);
154
155 lex.next();
156 String token = lex.getToken();
157
158 // Operator: '=='
159 if (token.equals("==")
160 || token.equals("!=")
161 || token.equals("<")
162 || token.equals(">")
163 || token.equals("&&")
164 || token.equals("||")) {
165 lex.next();
166 Operator nextOp = parse4(lex);
167 out = new BooleanOperator(token, out, nextOp, throwExceptions);
168 } else {
169 lex.previous();
170 return out;
171 }
172 return out;
173 }
174
175 // Parse level 3: Arithmetic operators +.-
176 private Operator parse3(Lexer lex) throws ParseException {
177 Operator out = parse2(lex);
178
179 lex.next();
180 String token = lex.getToken();
181
182 // Operator: '+', '-'
183 if (token.equals("+") || token.equals("-")) {
184 lex.next();
185 Operator nextOp = parse3(lex);
186 out = new ArithmeticOperator(token, out, nextOp, throwExceptions);
187 } else {
188 lex.previous();
189 return out;
190 }
191 return out;
192 }
193
194 // Parse level 2: Arithmetic operators *,/,%
195 private Operator parse2(Lexer lex) throws ParseException {
196 Operator out = parse1(lex);
197
198 lex.next();
199 String token = lex.getToken();
200
201 // Operator: '*', '/', '%'
202 if (token.equals("*") || token.equals("/") || token.equals("%")) {
203 lex.next();
204 Operator nextOp = parse2(lex);
205 out = new ArithmeticOperator(token, out, nextOp, throwExceptions);
206 } else {
207 lex.previous();
208 return out;
209 }
210 return out;
211 }
212
213 // Parse level 1: '.', '[]'
214 private Operator parse1(Lexer lex) throws ParseException {
215 Operator out = parse0(lex);
216
217 while (lex.next()) {
218 String token = lex.getToken();
219
220 // Accessor or functions...
221 if (out == null && (Character.isLetter(token.charAt(0)) ||
222 token.charAt(0)=='_') ) {
223 out = new AccessorOperator(token, out, false);
224 } else if (token.equals(".") || token.startsWith("$")) {
225 if (token.equals(".")) lex.next();
226 token = lex.getToken();
227 lex.next();
228 if (lex.getToken().equals("(")) {
229 // Found operator...
230 Operator nestedOp = out;
231 Class operatorClass = (Class) namedOperatorMap.get(token);
232 if (operatorClass != null) {
233 try {
234 out = (Operator) operatorClass.newInstance();
235 out.setNestedOperator(nestedOp);
236 } catch (Exception ex) {
237 throw new ParseException(
238 "Couldn't instantiate operator: "
239 + operatorClass);
240 }
241 } else {
242 Function function = (Function) functionMap.get(token);
243 if (function != null) {
244 out = new FunctionOperator(function);
245 out.setNestedOperator(nestedOp);
246 } else {
247 out = new MethodOperator(token);
248 out.setNestedOperator(nestedOp);
249 }
250 }
251
252 List argOperators = new ArrayList();
253 boolean keep = true;
254 lex.next();
255 while (true) {
256 Operator argOp = parse(lex);
257 if (argOp != null) argOperators.add(argOp);
258 lex.next();
259 if (lex.getToken().equals(")"))
260 break;
261 else if (lex.getToken().equals(","))
262 lex.next();
263 else
264 throw new ParseException("Expected: ) or , tokens");
265 }
266 if (argOperators.size() > 0) {
267 Operator[] argOperatorsArray =
268 new Operator[argOperators.size()];
269 for (int i = 0; i < argOperators.size(); i++) {
270 argOperatorsArray[i] =
271 (Operator) argOperators.get(i);
272 }
273 out.setArgumentOperators(argOperatorsArray);
274 }
275 } else {
276 // Not a function, just an accessor
277 lex.previous();
278 out = new AccessorOperator(token, out, false);
279 }
280 }
281
282 // Operator: '['
283 else if (token.equals("[")) {
284 lex.next();
285 Operator indexOperator = parse(lex);
286 out = new IndexerOperator(indexOperator, out, throwExceptions);
287 lex.next();
288 if (!lex.getToken().equals("]"))
289 throw new ParseException("Expected: ] token");
290 } else {
291 lex.previous();
292 return out;
293 }
294 }
295 return out;
296 }
297
298 // Parse level 0: constants, accessor, and ()
299 private Operator parse0(Lexer lex) throws ParseException {
300 Operator out = null;
301 String token = lex.getToken();
302
303 if (token.equals("'")) {
304 // Constant string...
305 lex.next();
306 out = new ConstantOperator(lex.getToken());
307 lex.next();
308 if (!lex.getToken().equals("'"))
309 throw new ParseException("Expected: ' token");
310 } else if (Character.isDigit(token.charAt(0))) {
311 // Constant integer...
312 out = new ConstantOperator(new Integer(lex.getToken()));
313 } else if (token.equals("(")) {
314 // ()
315 lex.next();
316 out = parse(lex);
317 lex.next();
318 if (!lex.getToken().equals(")"))
319 throw new ParseException(
320 "Expected token ')' found token '" + lex.getToken() + "'");
321 } else {
322 lex.previous();
323 }
324 return out;
325 }
326 }
This page was automatically generated by Maven