EvaluatingContext.java

biz/hammurapi/eval/EvaluatingContext.java

Violations

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