Evaluator.java

biz/hammurapi/eval/Evaluator.java

Violations

Inspector Message Severity Location
Java Inspector 006 Missing default case for switch statement 1 488:25
Java Inspector 031 Switch statement case without 'break' 1 492:33
Java Inspector 048 Copyrights information should be present in each file. 1
Java Inspector 070-B Cyclomatic complexity is too high: 31, maximum allowed is 20 1 130:9
Java Inspector 049 Use a Collection instead of arrays Object[] 2 54:9
Java Inspector 049 Use a Collection instead of arrays Object[] 2 113:35
Java Inspector 049 Use a Collection instead of arrays Object[] 2 232:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 245:31
Java Inspector 049 Use a Collection instead of arrays Object[] 2 281:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 289:15
Java Inspector 049 Use a Collection instead of arrays Object[] 2 302:15
Java Inspector 049 Use a Collection instead of arrays Object[] 2 397:43
Java Inspector 049 Use a Collection instead of arrays Object[] 2 400:51
Java Inspector 049 Use a Collection instead of arrays Object[] 2 408:51
Java Inspector 049 Use a Collection instead of arrays Object[] 2 430:27
Java Inspector 049 Use a Collection instead of arrays Object[] 2 463:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 526:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 544:19
Java Inspector 049 Use a Collection instead of arrays Object[] 2 547:19
Java Inspector 049 Use a Collection instead of arrays Object[] 2 549:23
Java Inspector 070-A Cyclomatic complexity is too high: 31, maximum allowed is 12 2 130:9
Java Inspector 070-A Cyclomatic complexity is too high: 14, maximum allowed is 12 2 319:5
Java Inspector 070-A Cyclomatic complexity is too high: 13, maximum allowed is 12 2 386:9
Java Inspector 070-A Cyclomatic complexity is too high: 19, maximum allowed is 12 2 450:5
Java Inspector 070-A Cyclomatic complexity is too high: 14, maximum allowed is 12 2 562:5
Java Inspector 073 [java.lang.StringBuffer] In Java 5 use StringBuilder instead of StringBuffer if access is single-threaded, e.g. StringBuffer is used as a local variable . 2 164:33
Java Inspector 073 [java.lang.StringBuffer] In Java 5 use StringBuilder instead of StringBuffer if access is single-threaded, e.g. StringBuffer is used as a local variable . 2 504:25
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 59:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 94:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 106:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 130:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 214:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 264:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 319:5
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 386:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 450:5
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 562:5
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 628:9
Java Inspector 089 Type is not documented 2 51:1
Java Inspector 089 Undocumented constructor 2 59:9
Java Inspector 089 Undocumented method 2 94:9
Java Inspector 089 Undocumented method 2 106:9
Java Inspector 089 Undocumented method 2 224:17
Java Inspector 089 Undocumented method 2 237:17
Java Inspector 089 Parameter name is not documented 2 645:9
Java Inspector 089 Method return value is not properly documented 2 645:9
Java Inspector 005 Classes, interfaces, methods, and variables should be named according to Sun's naming conventions. 3 52:9
Java Inspector 024 Avoid hardwired character literals 3 646:33
Java Inspector 024 Avoid hardwired character literals 3 646:38
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 166:53
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 169:53
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 172:53
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 60:30
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 86:78
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 144:63
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 165:55
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 165:83
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 166:56
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 168:55
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 168:83
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 169:56
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 171:55
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 171:83
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 172:56
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 204:30
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 204:94
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 204:113
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 204:133
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 220:93
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 270:55
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 270:85
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 270:121
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 321:43
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 321:72
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 321:107
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 338:43
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 338:73
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 338:94
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 351:43
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 351:73
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 351:94
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 364:43
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 364:73
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 364:94
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 375:39
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 460:55
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 460:92
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 500:55
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 500:92
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 504:59
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 504:91
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 507:44
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 580:43
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 592:57
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 603:57
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 614:57
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 625:57
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 633:78
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 635:63
Java Inspector 040 Parameter name methods clashes with field name in Evaluator 3 450:94
Java Inspector 051 It is good practice to call in any case super() in a constructor. 3 59:9

Source code

1/*
2 * hgcommons 9
3 * Hammurapi Group Common Library
4 * Copyright (C) 2003 Hammurapi Group
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
21 * e-Mail: support@hammurapi.biz
22 */
23package biz.hammurapi.eval;
24
25import java.io.StringReader;
26import java.lang.reflect.Array;
27import java.lang.reflect.Method;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.Enumeration;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Map;
34
35import org.apache.log4j.Logger;
36
37import antlr.RecognitionException;
38import antlr.TokenStreamException;
39import biz.hammurapi.antlr.AST;
40import biz.hammurapi.antlr.Token;
41import biz.hammurapi.config.BeanContext;
42import biz.hammurapi.config.Context;
43import biz.hammurapi.convert.Converter;
44
45
46/**
47 * @deprecated Use EvaluatingContext instead.
48 * @author Pavel Vlasov
49 * @revision $Revision$
50 */
51public class Evaluator {
52 private static final Logger logger=Logger.getLogger(Evaluator.class);
53
54 private AST[] nodes;
55 private Converter converter;
56
57 private Collection methods=new ArrayList();
58
59 public Evaluator(String expression, Collection methods, Converter converter) throws EvaluationException {
60 logger.debug("New evaluator for expression: "+expression);
61 this.converter=converter;
62 ExpressionLexer lexer=new ExpressionLexer(new StringReader(expression));
63 lexer.setTokenObjectClass(Token.class.getName());
64 ExpressionRecognizer parser=new ExpressionRecognizer(lexer);
65 parser.setASTNodeClass(AST.class.getName());
66 try {
67 parser.expressionList();
68 } catch (RecognitionException e) {
69 throw new EvaluationException(e);
70 } catch (TokenStreamException e) {
71 throw new EvaluationException(e);
72 }
73
74 List nodeList=new ArrayList();
75 for (AST ast=(AST) parser.getAST().getFirstChild(); ast!=null; ast=(AST) ast.getNextSibling()) {
76 nodeList.add(ast);
77 }
78
79 nodes=(AST[]) nodeList.toArray(new AST[nodeList.size()]);
80
81 if (methods!=null) {
82 this.methods.addAll(methods);
83 }
84
85 try {
86 this.methods.add(new MethodEntry(null, Evaluator.class.getMethod("forEach", new Class[] {Object.class})));
87 } catch (SecurityException e) {
88 throw new EvaluationException(e);
89 } catch (NoSuchMethodException e) {
90 throw new EvaluationException(e);
91 }
92 }
93
94 public static MultiResult forEach(Object param) throws EvaluationException {
95 Collection values = new ArrayList();
96 populate(((SingleResult) param).getValue(), values);
97 return new MultiResult(null, values, null);
98 }
99
100 void print() {
101 if (nodes.length!=0) {
102 nodes[0].print(ExpressionRecognizer._tokenNames, true);
103 }
104 }
105
106 public Collection evaluate(Context context) throws EvaluationException {
107 Collection ret=new ArrayList();
108 for (int i=0; i<nodes.length; i++) {
109 Result result = evaluate(null, nodes[i], context);
110 if (result instanceof SingleResult) {
111 ret.add(((SingleResult) result).getValue());
112 } else {
113 Object[] values=((MultiResult) result).getValues();
114 for (int j=0; j<values.length; j++) {
115 ret.add(values[j]);
116 }
117 }
118 }
119 return ret;
120 }
121
122
123 /**
124 * @param o
125 * @param ast
126 * @param context
127 * @return evaluation result
128 * @throws EvaluationException
129 */
130 private Result evaluate(Result res, AST ast, Context context) throws EvaluationException {
131 logAst(ast);
132 switch (ast.getType()) {
133 case ExpressionTokenTypes.MINUS:
134 return minus(res, ast, context);
135 case ExpressionTokenTypes.PLUS:
136 return plus(res, ast, context);
137 case ExpressionTokenTypes.LNOT:
138 return lnot(res, ast, context);
139 case ExpressionTokenTypes.TYPECAST:
140 return typecast(res, ast, context);
141 case ExpressionTokenTypes.METHOD_CALL:
142 return invoke(res, ast, context);
143 case ExpressionTokenTypes.ARRAY_DECLARATOR:
144 throw new EvaluationException("Handle it!");
145 case ExpressionTokenTypes.INDEX_OP:
146 return index(res, ast, context);
147 case ExpressionTokenTypes.DOT:
148 return dot(res, ast, context);
149 case ExpressionTokenTypes.IDENT:
150 Object obj = context.get(ast.getText());
151 return obj instanceof Result ? (Result) obj : new SingleResult(this.converter, null, obj);
152 case ExpressionTokenTypes.LITERAL_true:
153 return new SingleResult(this.converter, boolean.class, Boolean.TRUE);
154 case ExpressionTokenTypes.LITERAL_false:
155 return new SingleResult(this.converter, boolean.class, Boolean.FALSE);
156 case ExpressionTokenTypes.LITERAL_null:
157 return new SingleResult(this.converter, null, null);
158 case ExpressionTokenTypes.NUM_INT:
159 return new SingleResult(this.converter, int.class, new Integer(ast.getText()));
160 case ExpressionTokenTypes.CHAR_LITERAL:
161 return new SingleResult(this.converter, char.class, ast.getText().substring(0,1));
162 case ExpressionTokenTypes.STRING_LITERAL:
163 String rawText=ast.getText();
164 StringBuffer sb=new StringBuffer(rawText.substring(1, rawText.length()-1));
165 for (int i=sb.indexOf("\\n"); i!=-1; i=sb.indexOf("\\n")) {
166 sb.replace(i, i+2, "\n");
167 }
168 for (int i=sb.indexOf("\\r"); i!=-1; i=sb.indexOf("\\r")) {
169 sb.replace(i, i+2, "\r");
170 }
171 for (int i=sb.indexOf("\\t"); i!=-1; i=sb.indexOf("\\t")) {
172 sb.replace(i, i+2, "\t");
173 }
174 return new SingleResult(this.converter, String.class, sb.toString());
175 case ExpressionTokenTypes.NUM_FLOAT:
176 return new SingleResult(this.converter, float.class, new Float(ast.getText()));
177 case ExpressionTokenTypes.NUM_LONG:
178 return new SingleResult(this.converter, long.class, new Long(ast.getText()));
179 case ExpressionTokenTypes.NUM_DOUBLE:
180 return new SingleResult(this.converter, double.class, new Double(ast.getText()));
181 case ExpressionTokenTypes.LITERAL_boolean:
182 return new SingleResult(this.converter, boolean.class, null);
183 case ExpressionTokenTypes.LITERAL_byte:
184 return new SingleResult(this.converter, byte.class, null);
185 case ExpressionTokenTypes.LITERAL_char:
186 return new SingleResult(this.converter, char.class, null);
187 case ExpressionTokenTypes.LITERAL_short:
188 return new SingleResult(this.converter, short.class, null);
189 case ExpressionTokenTypes.LITERAL_float:
190 return new SingleResult(this.converter, float.class, null);
191 case ExpressionTokenTypes.LITERAL_long:
192 return new SingleResult(this.converter, long.class, null);
193 case ExpressionTokenTypes.LITERAL_double:
194 return new SingleResult(this.converter, double.class, null);
195 default:
196 throw new EvaluationException(ast);
197 }
198 }
199
200 /**
201 * @param ast
202 */
203 private static void logAst(AST ast) {
204 logger.debug("Evaluating: ["+ExpressionRecognizer._tokenNames[ast.getType()]+"] "+ast.getLine()+":"+ast.getColumn()+" "+ast.toString());
205 }
206
207 /**
208 * @param res Current result
209 * @param ast Current node
210 * @param context Context
211 * @return evaluation result
212 * @throws EvaluationException
213 */
214 private Result dot(Result res, AST ast, Context context) throws EvaluationException {
215 Result result=evaluate(res, (AST) ast.getFirstChild(), context);
216 String property = ast.getFirstChild().getNextSibling().getText();
217 if (result instanceof SingleResult) {
218 Object value = ((SingleResult) result).getValue();
219 if (value==null) {
220 throw new EvaluationException(ast.getFirstChild().getText()+" is null");
221 }
222
223 Context ctx= value instanceof Context ? (Context) value : new BeanContext(value) {
224 protected String translate(String name) {
225 return Evaluator.this.translate(name);
226 }
227 };
228 Object ret = ctx.get(property);
229 return ret instanceof Result ? (Result) ret : new SingleResult(this.converter, null, ret);
230 }
231
232 Object[] values = ((MultiResult) result).getValues();
233
234 Collection cres=new ArrayList();
235 for (int i=0; i<values.length; i++) {
236 Context ctx = values[i] instanceof Context ? (Context) values[i] : new BeanContext(values[i]) {
237 protected String translate(String name) {
238 return Evaluator.this.translate(name);
239 }
240 };
241 Object ret = ctx.get(property);
242 if (ret instanceof SingleResult) {
243 cres.add(((SingleResult) ret).getValue());
244 } else if (ret instanceof MultiResult) {
245 Object[] rets=((MultiResult) ret).getValues();
246 for (int j=0; j<rets.length; j++) {
247 cres.add(rets[j]);
248 }
249 } else {
250 cres.add(ret);
251 }
252 }
253 return new MultiResult(null, cres, converter);
254 }
255
256 /**
257 * @param res
258 * @param ast
259 * @param context
260 * @param converter
261 * @return
262 * @throws EvaluationException
263 */
264 private Result index(Result res, AST ast, Context context) throws EvaluationException {
265 AST objectNode = (AST) ast.getFirstChild();
266 Result result = evaluate(null, objectNode, context);
267 if (result instanceof SingleResult) {
268 Object obj=((SingleResult) result).getValue();
269 if (obj==null) {
270 throw new EvaluationException("Value "+objectNode.getText()+" is null at "+objectNode.getLine()+":"+objectNode.getColumn());
271 }
272
273 AST indexNode = (AST) objectNode.getNextSibling();
274 Result idr = evaluate(null, indexNode, context);
275 if (idr instanceof SingleResult) {
276 Object idx=((SingleResult) idr).getValue();
277 return new SingleResult(this.converter, null, index(ast, obj, indexNode, idx));
278 }
279
280 Collection values=new ArrayList();
281 Object[] idxa=((MultiResult) idr).getValues();
282 for (int i=0; i<idxa.length; i++) {
283 values.add(index(ast, obj, indexNode, idxa[i]));
284 }
285
286 return new MultiResult(null, values, converter);
287 }
288
289 Object[] objs=((MultiResult) result).getValues();
290
291 AST indexNode = (AST) objectNode.getNextSibling();
292 Collection values=new ArrayList();
293 Result idr = evaluate(null, indexNode, context);
294 if (idr instanceof SingleResult) {
295 Object idx=((SingleResult) idr).getValue();
296 for (int i=0; i<objs.length; i++) {
297 values.add(index(ast, objs[i], indexNode, idx));
298 }
299 return new MultiResult(null, values, converter);
300 }
301
302 Object[] idxa=((MultiResult) idr).getValues();
303 for (int j=0; j<objs.length; j++) {
304 for (int i=0; i<idxa.length; i++) {
305 values.add(index(ast, objs[j], indexNode, idxa[i]));
306 }
307 }
308
309 return new MultiResult(null, values, converter);
310 }
311
312 /**
313 * @param ast
314 * @param obj
315 * @param indexNode
316 * @param idx
317 * @throws EvaluationException
318 */
319 private Object index(AST ast, Object obj, AST indexNode, Object idx) throws EvaluationException {
320 if (idx==null) {
321 throw new EvaluationException("Index "+indexNode.getText()+" is null at "+indexNode.getLine()+":"+indexNode.getColumn());
322 }
323
324 if (obj.getClass().isArray()) {
325 return Array.get(obj, ((Number) converter.convert(idx, Number.class, null)).intValue());
326 }
327
328 if (obj instanceof Collection) {
329 int index=((Number) converter.convert(idx, Number.class, null)).intValue();
330 Iterator it=((Collection) obj).iterator();
331 for (int i=0; it.hasNext(); i++) {
332 Object next = it.next();
333 if (i==index) {
334 return next;
335 }
336 }
337
338 throw new EvaluationException("Index out of bounds: "+index+" at "+ast.getLine()+":"+ast.getColumn());
339 }
340
341 if (obj instanceof Iterator) {
342 int index=((Number) converter.convert(idx, Number.class, null)).intValue();
343 Iterator it=(Iterator) obj;
344 for (int i=0; it.hasNext(); i++) {
345 Object next = it.next();
346 if (i==index) {
347 return next;
348 }
349 }
350
351 throw new EvaluationException("Index out of bounds: "+index+" at "+ast.getLine()+":"+ast.getColumn());
352 }
353
354 if (obj instanceof Enumeration) {
355 int index=((Number) converter.convert(idx, Number.class, null)).intValue();
356 Enumeration enm=(Enumeration) obj;
357 for (int i=0; enm.hasMoreElements(); i++) {
358 Object nextElement = enm.nextElement();
359 if (i==index) {
360 return nextElement;
361 }
362 }
363
364 throw new EvaluationException("Index out of bounds: "+index+" at "+ast.getLine()+":"+ast.getColumn());
365 }
366
367 if (obj instanceof Context) {
368 return ((Context) obj).get(idx.toString());
369 }
370
371 if (obj instanceof Map) {
372 return ((Map) obj).get(idx);
373 }
374
375 throw new EvaluationException("Can't apply index operation to class "+obj.getClass().getName());
376 }
377
378 /**
379 * @param res
380 * @param ast
381 * @param context
382 * @param converter
383 * @return
384 * @throws EvaluationException
385 */
386 private Result invoke(Result res, AST ast, Context context) throws EvaluationException {
387 int paramCount = ast.getFirstChild().getNextSibling().getNumberOfChildren();
388 AST nameNode = (AST) ast.getFirstChild();
389 Object object;
390 String methodName;
391 switch (nameNode.getType()) {
392 case ExpressionRecognizer.DOT:
393 methodName = nameNode.getFirstChild().getNextSibling().getText();
394 Result result = evaluate(res, (AST) nameNode.getFirstChild(), context);
395 if (result instanceof MultiResult) {
396 Collection ret=new ArrayList();
397 Object[] values=((MultiResult) result).getValues();
398 for (int i=0; i<values.length; i++) {
399 ArrayList vCandidates=new ArrayList();
400 Method[] ma=values[i].getClass().getMethods();
401 for (int j=0; j<ma.length; j++) {
402 vCandidates.add(new MethodEntry(values[i], ma[j]));
403 }
404 Result ir = invokeInternal(res, context, paramCount, nameNode, vCandidates, methodName);
405 if (ir instanceof SingleResult) {
406 ret.add(((SingleResult) ir).getValue());
407 } else {
408 Object[] vv=((MultiResult) ir).getValues();
409 for (int k=0; k<vv.length; k++) {
410 ret.add(vv[k]);
411 }
412 }
413 }
414 return new MultiResult(null, ret, converter);
415 }
416 object = ((SingleResult) result).getValue();
417 break;
418 case ExpressionRecognizer.IDENT:
419 object=context;
420 methodName=nameNode.getText();
421 break;
422 default:
423 throw new EvaluationException(nameNode);
424 }
425
426 ArrayList candidates=new ArrayList();
427 if (object==null) {
428 candidates.addAll(methods);
429 } else {
430 Method[] ma=object.getClass().getMethods();
431 for (int i=0; i<ma.length; i++) {
432 candidates.add(new MethodEntry(object, ma[i]));
433 }
434 }
435
436 return invokeInternal(res, context, paramCount, nameNode, candidates, methodName);
437 }
438
439 /**
440 * @param res
441 * @param context
442 * @param paramCount
443 * @param methods
444 * @param nameNode
445 * @param object
446 * @param methodName
447 * @return
448 * @throws EvaluationException
449 */
450 private Result invokeInternal(Result res, Context context, int paramCount, AST nameNode, ArrayList methods, String methodName) throws EvaluationException {
451 Iterator it=methods.iterator();
452 while (it.hasNext()) {
453 MethodEntry me=(MethodEntry) it.next();
454 if (!me.name.equals(methodName) || me.method.getParameterTypes().length!=paramCount) {
455 it.remove();
456 }
457 }
458
459 if (methods.isEmpty()) {
460 throw new EvaluationException("No appropriate method '"+methodName+"'");
461 }
462
463 Result[] params=new Result[paramCount];
464 int idx=0;
465 boolean multiResult=false;
466 for (AST paramNode=(AST) nameNode.getNextSibling().getFirstChild(); paramNode!=null; paramNode=(AST) paramNode.getNextSibling(), idx++) {
467 params[idx]=evaluate(res, paramNode, context);
468 if (params[idx] instanceof MultiResult) {
469 multiResult=true;
470 }
471
472 if (params[idx].getType()!=null) {
473 it=methods.iterator();
474 while (it.hasNext()) {
475 MethodEntry me=(MethodEntry) it.next();
476 if (!me.method.getParameterTypes()[idx].isAssignableFrom(params[idx].getType())) {
477 it.remove();
478 }
479 }
480 }
481 }
482
483 it=methods.iterator();
484 Z: while (it.hasNext()) {
485 MethodEntry me=(MethodEntry) it.next();
486 Iterator jt=methods.iterator();
487 while (jt.hasNext()) {
488 switch (((MethodEntry) jt.next()).isMoreSpecific(me)) {
489 case 1:
490 it.remove();
491 break Z;
492 case -1:
493 jt.remove();
494 }
495 }
496 }
497
498 // Finding proper method
499 if (methods.isEmpty()) {
500 throw new EvaluationException("No appropriate method '"+methodName+"'");
501 }
502
503 if (methods.size()>1) {
504 StringBuffer msg=new StringBuffer("Ambiguous method '"+methodName+"': ");
505 it=methods.iterator();
506 while (it.hasNext()) {
507 msg.append("\n\t");
508 msg.append(((MethodEntry) it.next()).method);
509 }
510
511 throw new EvaluationException(msg.toString());
512 }
513
514 final MethodEntry methodEntry=(MethodEntry) methods.get(0);
515
516 if (multiResult) {
517 Collection ret=new ArrayList();
518 Collection args=new ArrayList();
519 args.add(new Object[params.length]);
520 for (int i=0; i<params.length; i++) {
521 args=setArgs(args, i, params[i], methodEntry.method.getParameterTypes()[i]);
522 }
523 return new MultiResult(methodEntry.method.getReturnType(), ret, converter);
524 }
525
526 Object[] args=new Object[params.length];
527 for (int i=0; i<params.length; i++) {
528 args[i]=converter.convert(((SingleResult) params[i]).getValue(), methodEntry.method.getParameterTypes()[i], null);
529 }
530
531 return new SingleResult(this.converter, methodEntry.method.getReturnType(), methodEntry.invoke(args));
532 }
533
534 private Collection setArgs(Collection args, int idx, Result arg, Class paramType) {
535 if (arg instanceof SingleResult) {
536 Iterator it=args.iterator();
537 while (it.hasNext()) {
538 ((Object[]) it.next())[idx]=converter.convert(((SingleResult) arg).getValue(), paramType, null);
539 }
540 return args;
541 }
542
543 Collection ret=new ArrayList();
544 Object[] values=((MultiResult) arg).getValues();
545 Iterator it=args.iterator();
546 while (it.hasNext()) {
547 Object[] objs = (Object[]) it.next();
548 for (int i=0; i<values.length; i++) {
549 Object[] cobjs=(Object[]) objs.clone();
550 cobjs[idx]=converter.convert(values[i], paramType, null);
551 ret.add(cobjs);
552 }
553 }
554 return ret;
555 }
556
557 /**
558 * @param object
559 * @param values
560 * @throws EvaluationException
561 */
562 private static void populate(Object object, Collection values) throws EvaluationException {
563 if (object.getClass().isArray()) {
564 for (int i=0, j=Array.getLength(object); i<j; i++) {
565 values.add(Array.get(object, i));
566 }
567 } else if (object instanceof Collection) {
568 values.addAll((Collection) object);
569 } else if (object instanceof Map) {
570 values.addAll(((Map) object).entrySet());
571 } else if (object instanceof Iterator) {
572 while (((Iterator) object).hasNext()) {
573 values.add(((Iterator) object).next());
574 }
575 } else if (object instanceof Enumeration) {
576 while (((Enumeration) object).hasMoreElements()) {
577 values.add(((Enumeration) object).nextElement());
578 }
579 } else {
580 throw new EvaluationException("forEach() is not applicable for "+object.getClass());
581 }
582 }
583
584 /**
585 * @param res
586 * @param ast
587 * @param context
588 * @return
589 */
590 private Result typecast(Result res, AST ast, Context context) {
591 ast.print(ExpressionRecognizer._tokenNames, false);
592 throw new UnsupportedOperationException("Not yet implemented");
593 }
594
595 /**
596 * @param res
597 * @param ast
598 * @param context
599 * @return
600 */
601 private Result lnot(Result res, AST ast, Context context) {
602 ast.print(ExpressionRecognizer._tokenNames, false);
603 throw new UnsupportedOperationException("Not yet implemented");
604 }
605
606 /**
607 * @param res
608 * @param ast
609 * @param context
610 * @return
611 */
612 private Result plus(Result res, AST ast, Context context) {
613 ast.print(ExpressionRecognizer._tokenNames, false);
614 throw new UnsupportedOperationException("Not yet implemented");
615 }
616
617 /**
618 * @param res
619 * @param ast
620 * @param context
621 * @return
622 */
623 private Result minus(Result res, AST ast, Context context) {
624 ast.print(ExpressionRecognizer._tokenNames, false);
625 throw new UnsupportedOperationException("Not yet implemented");
626 }
627
628 private String identifier(AST ast) throws EvaluationException {
629 switch (ast.getType()) {
630 case ExpressionTokenTypes.IDENT:
631 return ast.getText();
632 case ExpressionTokenTypes.DOT:
633 return identifier((AST) ast.getFirstChild())+"."+identifier((AST) ast.getFirstChild().getNextSibling());
634 default:
635 throw new EvaluationException("Unexpected node type: "+ExpressionRecognizer._tokenNames[ast.getType()]);
636 }
637 }
638
639 /**
640 * Translates "indexed" property name.
641 * By default replaces '_' with ' '
642 * @param name
643 * @return
644 */
645 protected String translate(String name) {
646 return name.replace('_', ' ');
647 }
648}