001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.eval; 005 006 import java.io.StringReader; 007 import java.lang.reflect.Array; 008 import java.lang.reflect.Method; 009 import java.util.ArrayList; 010 import java.util.Collection; 011 import java.util.Enumeration; 012 import java.util.Iterator; 013 import java.util.List; 014 import java.util.Map; 015 016 import org.apache.log4j.Logger; 017 018 import antlr.RecognitionException; 019 import antlr.TokenStreamException; 020 import biz.hammurapi.antlr.AST; 021 import biz.hammurapi.antlr.Token; 022 import biz.hammurapi.config.BeanContext; 023 import biz.hammurapi.config.Context; 024 import biz.hammurapi.convert.Converter; 025 026 027 /** 028 * @deprecated Use EvaluatingContext instead. 029 * @author Pavel Vlasov 030 * @revision $Revision$ 031 */ 032 public class Evaluator { 033 private static final Logger logger=Logger.getLogger(Evaluator.class); 034 035 private AST[] nodes; 036 private Converter converter; 037 038 private Collection methods=new ArrayList(); 039 040 public Evaluator(String expression, Collection methods, Converter converter) throws EvaluationException { 041 logger.debug("New evaluator for expression: "+expression); 042 this.converter=converter; 043 ExpressionLexer lexer=new ExpressionLexer(new StringReader(expression)); 044 lexer.setTokenObjectClass(Token.class.getName()); 045 ExpressionRecognizer parser=new ExpressionRecognizer(lexer); 046 parser.setASTNodeClass(AST.class.getName()); 047 try { 048 parser.expressionList(); 049 } catch (RecognitionException e) { 050 throw new EvaluationException(e); 051 } catch (TokenStreamException e) { 052 throw new EvaluationException(e); 053 } 054 055 List nodeList=new ArrayList(); 056 for (AST ast=(AST) parser.getAST().getFirstChild(); ast!=null; ast=(AST) ast.getNextSibling()) { 057 nodeList.add(ast); 058 } 059 060 nodes=(AST[]) nodeList.toArray(new AST[nodeList.size()]); 061 062 if (methods!=null) { 063 this.methods.addAll(methods); 064 } 065 066 try { 067 this.methods.add(new MethodEntry(null, Evaluator.class.getMethod("forEach", new Class[] {Object.class}))); 068 } catch (SecurityException e) { 069 throw new EvaluationException(e); 070 } catch (NoSuchMethodException e) { 071 throw new EvaluationException(e); 072 } 073 } 074 075 public static MultiResult forEach(Object param) throws EvaluationException { 076 Collection values = new ArrayList(); 077 populate(((SingleResult) param).getValue(), values); 078 return new MultiResult(null, values, null); 079 } 080 081 void print() { 082 if (nodes.length!=0) { 083 nodes[0].print(ExpressionRecognizer._tokenNames, true); 084 } 085 } 086 087 public Collection evaluate(Context context) throws EvaluationException { 088 Collection ret=new ArrayList(); 089 for (int i=0; i<nodes.length; i++) { 090 Result result = evaluate(null, nodes[i], context); 091 if (result instanceof SingleResult) { 092 ret.add(((SingleResult) result).getValue()); 093 } else { 094 Object[] values=((MultiResult) result).getValues(); 095 for (int j=0; j<values.length; j++) { 096 ret.add(values[j]); 097 } 098 } 099 } 100 return ret; 101 } 102 103 104 /** 105 * @param o 106 * @param ast 107 * @param context 108 * @return evaluation result 109 * @throws EvaluationException 110 */ 111 private Result evaluate(Result res, AST ast, Context context) throws EvaluationException { 112 logAst(ast); 113 switch (ast.getType()) { 114 case ExpressionTokenTypes.MINUS: 115 return minus(res, ast, context); 116 case ExpressionTokenTypes.PLUS: 117 return plus(res, ast, context); 118 case ExpressionTokenTypes.LNOT: 119 return lnot(res, ast, context); 120 case ExpressionTokenTypes.TYPECAST: 121 return typecast(res, ast, context); 122 case ExpressionTokenTypes.METHOD_CALL: 123 return invoke(res, ast, context); 124 case ExpressionTokenTypes.ARRAY_DECLARATOR: 125 throw new EvaluationException("Handle it!"); 126 case ExpressionTokenTypes.INDEX_OP: 127 return index(res, ast, context); 128 case ExpressionTokenTypes.DOT: 129 return dot(res, ast, context); 130 case ExpressionTokenTypes.IDENT: 131 Object obj = context.get(ast.getText()); 132 return obj instanceof Result ? (Result) obj : new SingleResult(this.converter, null, obj); 133 case ExpressionTokenTypes.LITERAL_true: 134 return new SingleResult(this.converter, boolean.class, Boolean.TRUE); 135 case ExpressionTokenTypes.LITERAL_false: 136 return new SingleResult(this.converter, boolean.class, Boolean.FALSE); 137 case ExpressionTokenTypes.LITERAL_null: 138 return new SingleResult(this.converter, null, null); 139 case ExpressionTokenTypes.NUM_INT: 140 return new SingleResult(this.converter, int.class, new Integer(ast.getText())); 141 case ExpressionTokenTypes.CHAR_LITERAL: 142 return new SingleResult(this.converter, char.class, ast.getText().substring(0,1)); 143 case ExpressionTokenTypes.STRING_LITERAL: 144 String rawText=ast.getText(); 145 StringBuffer sb=new StringBuffer(rawText.substring(1, rawText.length()-1)); 146 for (int i=sb.indexOf("\\n"); i!=-1; i=sb.indexOf("\\n")) { 147 sb.replace(i, i+2, "\n"); 148 } 149 for (int i=sb.indexOf("\\r"); i!=-1; i=sb.indexOf("\\r")) { 150 sb.replace(i, i+2, "\r"); 151 } 152 for (int i=sb.indexOf("\\t"); i!=-1; i=sb.indexOf("\\t")) { 153 sb.replace(i, i+2, "\t"); 154 } 155 return new SingleResult(this.converter, String.class, sb.toString()); 156 case ExpressionTokenTypes.NUM_FLOAT: 157 return new SingleResult(this.converter, float.class, new Float(ast.getText())); 158 case ExpressionTokenTypes.NUM_LONG: 159 return new SingleResult(this.converter, long.class, new Long(ast.getText())); 160 case ExpressionTokenTypes.NUM_DOUBLE: 161 return new SingleResult(this.converter, double.class, new Double(ast.getText())); 162 case ExpressionTokenTypes.LITERAL_boolean: 163 return new SingleResult(this.converter, boolean.class, null); 164 case ExpressionTokenTypes.LITERAL_byte: 165 return new SingleResult(this.converter, byte.class, null); 166 case ExpressionTokenTypes.LITERAL_char: 167 return new SingleResult(this.converter, char.class, null); 168 case ExpressionTokenTypes.LITERAL_short: 169 return new SingleResult(this.converter, short.class, null); 170 case ExpressionTokenTypes.LITERAL_float: 171 return new SingleResult(this.converter, float.class, null); 172 case ExpressionTokenTypes.LITERAL_long: 173 return new SingleResult(this.converter, long.class, null); 174 case ExpressionTokenTypes.LITERAL_double: 175 return new SingleResult(this.converter, double.class, null); 176 default: 177 throw new EvaluationException(ast); 178 } 179 } 180 181 /** 182 * @param ast 183 */ 184 private static void logAst(AST ast) { 185 logger.debug("Evaluating: ["+ExpressionRecognizer._tokenNames[ast.getType()]+"] "+ast.getLine()+":"+ast.getColumn()+" "+ast.toString()); 186 } 187 188 /** 189 * @param res Current result 190 * @param ast Current node 191 * @param context Context 192 * @return evaluation result 193 * @throws EvaluationException 194 */ 195 private Result dot(Result res, AST ast, Context context) throws EvaluationException { 196 Result result=evaluate(res, (AST) ast.getFirstChild(), context); 197 String property = ast.getFirstChild().getNextSibling().getText(); 198 if (result instanceof SingleResult) { 199 Object value = ((SingleResult) result).getValue(); 200 if (value==null) { 201 throw new EvaluationException(ast.getFirstChild().getText()+" is null"); 202 } 203 204 Context ctx= value instanceof Context ? (Context) value : new BeanContext(value) { 205 protected String translate(String name) { 206 return Evaluator.this.translate(name); 207 } 208 }; 209 Object ret = ctx.get(property); 210 return ret instanceof Result ? (Result) ret : new SingleResult(this.converter, null, ret); 211 } 212 213 Object[] values = ((MultiResult) result).getValues(); 214 215 Collection cres=new ArrayList(); 216 for (int i=0; i<values.length; i++) { 217 Context ctx = values[i] instanceof Context ? (Context) values[i] : new BeanContext(values[i]) { 218 protected String translate(String name) { 219 return Evaluator.this.translate(name); 220 } 221 }; 222 Object ret = ctx.get(property); 223 if (ret instanceof SingleResult) { 224 cres.add(((SingleResult) ret).getValue()); 225 } else if (ret instanceof MultiResult) { 226 Object[] rets=((MultiResult) ret).getValues(); 227 for (int j=0; j<rets.length; j++) { 228 cres.add(rets[j]); 229 } 230 } else { 231 cres.add(ret); 232 } 233 } 234 return new MultiResult(null, cres, converter); 235 } 236 237 /** 238 * @param res 239 * @param ast 240 * @param context 241 * @param converter 242 * @return 243 * @throws EvaluationException 244 */ 245 private Result index(Result res, AST ast, Context context) throws EvaluationException { 246 AST objectNode = (AST) ast.getFirstChild(); 247 Result result = evaluate(null, objectNode, context); 248 if (result instanceof SingleResult) { 249 Object obj=((SingleResult) result).getValue(); 250 if (obj==null) { 251 throw new EvaluationException("Value "+objectNode.getText()+" is null at "+objectNode.getLine()+":"+objectNode.getColumn()); 252 } 253 254 AST indexNode = (AST) objectNode.getNextSibling(); 255 Result idr = evaluate(null, indexNode, context); 256 if (idr instanceof SingleResult) { 257 Object idx=((SingleResult) idr).getValue(); 258 return new SingleResult(this.converter, null, index(ast, obj, indexNode, idx)); 259 } 260 261 Collection values=new ArrayList(); 262 Object[] idxa=((MultiResult) idr).getValues(); 263 for (int i=0; i<idxa.length; i++) { 264 values.add(index(ast, obj, indexNode, idxa[i])); 265 } 266 267 return new MultiResult(null, values, converter); 268 } 269 270 Object[] objs=((MultiResult) result).getValues(); 271 272 AST indexNode = (AST) objectNode.getNextSibling(); 273 Collection values=new ArrayList(); 274 Result idr = evaluate(null, indexNode, context); 275 if (idr instanceof SingleResult) { 276 Object idx=((SingleResult) idr).getValue(); 277 for (int i=0; i<objs.length; i++) { 278 values.add(index(ast, objs[i], indexNode, idx)); 279 } 280 return new MultiResult(null, values, converter); 281 } 282 283 Object[] idxa=((MultiResult) idr).getValues(); 284 for (int j=0; j<objs.length; j++) { 285 for (int i=0; i<idxa.length; i++) { 286 values.add(index(ast, objs[j], indexNode, idxa[i])); 287 } 288 } 289 290 return new MultiResult(null, values, converter); 291 } 292 293 /** 294 * @param ast 295 * @param obj 296 * @param indexNode 297 * @param idx 298 * @throws EvaluationException 299 */ 300 private Object index(AST ast, Object obj, AST indexNode, Object idx) throws EvaluationException { 301 if (idx==null) { 302 throw new EvaluationException("Index "+indexNode.getText()+" is null at "+indexNode.getLine()+":"+indexNode.getColumn()); 303 } 304 305 if (obj.getClass().isArray()) { 306 return Array.get(obj, ((Number) converter.convert(idx, Number.class, null)).intValue()); 307 } 308 309 if (obj instanceof Collection) { 310 int index=((Number) converter.convert(idx, Number.class, null)).intValue(); 311 Iterator it=((Collection) obj).iterator(); 312 for (int i=0; it.hasNext(); i++) { 313 Object next = it.next(); 314 if (i==index) { 315 return next; 316 } 317 } 318 319 throw new EvaluationException("Index out of bounds: "+index+" at "+ast.getLine()+":"+ast.getColumn()); 320 } 321 322 if (obj instanceof Iterator) { 323 int index=((Number) converter.convert(idx, Number.class, null)).intValue(); 324 Iterator it=(Iterator) obj; 325 for (int i=0; it.hasNext(); i++) { 326 Object next = it.next(); 327 if (i==index) { 328 return next; 329 } 330 } 331 332 throw new EvaluationException("Index out of bounds: "+index+" at "+ast.getLine()+":"+ast.getColumn()); 333 } 334 335 if (obj instanceof Enumeration) { 336 int index=((Number) converter.convert(idx, Number.class, null)).intValue(); 337 Enumeration enm=(Enumeration) obj; 338 for (int i=0; enm.hasMoreElements(); i++) { 339 Object nextElement = enm.nextElement(); 340 if (i==index) { 341 return nextElement; 342 } 343 } 344 345 throw new EvaluationException("Index out of bounds: "+index+" at "+ast.getLine()+":"+ast.getColumn()); 346 } 347 348 if (obj instanceof Context) { 349 return ((Context) obj).get(idx.toString()); 350 } 351 352 if (obj instanceof Map) { 353 return ((Map) obj).get(idx); 354 } 355 356 throw new EvaluationException("Can't apply index operation to class "+obj.getClass().getName()); 357 } 358 359 /** 360 * @param res 361 * @param ast 362 * @param context 363 * @param converter 364 * @return 365 * @throws EvaluationException 366 */ 367 private Result invoke(Result res, AST ast, Context context) throws EvaluationException { 368 int paramCount = ast.getFirstChild().getNextSibling().getNumberOfChildren(); 369 AST nameNode = (AST) ast.getFirstChild(); 370 Object object; 371 String methodName; 372 switch (nameNode.getType()) { 373 case ExpressionRecognizer.DOT: 374 methodName = nameNode.getFirstChild().getNextSibling().getText(); 375 Result result = evaluate(res, (AST) nameNode.getFirstChild(), context); 376 if (result instanceof MultiResult) { 377 Collection ret=new ArrayList(); 378 Object[] values=((MultiResult) result).getValues(); 379 for (int i=0; i<values.length; i++) { 380 ArrayList vCandidates=new ArrayList(); 381 Method[] ma=values[i].getClass().getMethods(); 382 for (int j=0; j<ma.length; j++) { 383 vCandidates.add(new MethodEntry(values[i], ma[j])); 384 } 385 Result ir = invokeInternal(res, context, paramCount, nameNode, vCandidates, methodName); 386 if (ir instanceof SingleResult) { 387 ret.add(((SingleResult) ir).getValue()); 388 } else { 389 Object[] vv=((MultiResult) ir).getValues(); 390 for (int k=0; k<vv.length; k++) { 391 ret.add(vv[k]); 392 } 393 } 394 } 395 return new MultiResult(null, ret, converter); 396 } 397 object = ((SingleResult) result).getValue(); 398 break; 399 case ExpressionRecognizer.IDENT: 400 object=context; 401 methodName=nameNode.getText(); 402 break; 403 default: 404 throw new EvaluationException(nameNode); 405 } 406 407 ArrayList candidates=new ArrayList(); 408 if (object==null) { 409 candidates.addAll(methods); 410 } else { 411 Method[] ma=object.getClass().getMethods(); 412 for (int i=0; i<ma.length; i++) { 413 candidates.add(new MethodEntry(object, ma[i])); 414 } 415 } 416 417 return invokeInternal(res, context, paramCount, nameNode, candidates, methodName); 418 } 419 420 /** 421 * @param res 422 * @param context 423 * @param paramCount 424 * @param methods 425 * @param nameNode 426 * @param object 427 * @param methodName 428 * @return 429 * @throws EvaluationException 430 */ 431 private Result invokeInternal(Result res, Context context, int paramCount, AST nameNode, ArrayList methods, String methodName) throws EvaluationException { 432 Iterator it=methods.iterator(); 433 while (it.hasNext()) { 434 MethodEntry me=(MethodEntry) it.next(); 435 if (!me.name.equals(methodName) || me.method.getParameterTypes().length!=paramCount) { 436 it.remove(); 437 } 438 } 439 440 if (methods.isEmpty()) { 441 throw new EvaluationException("No appropriate method '"+methodName+"'"); 442 } 443 444 Result[] params=new Result[paramCount]; 445 int idx=0; 446 boolean multiResult=false; 447 for (AST paramNode=(AST) nameNode.getNextSibling().getFirstChild(); paramNode!=null; paramNode=(AST) paramNode.getNextSibling(), idx++) { 448 params[idx]=evaluate(res, paramNode, context); 449 if (params[idx] instanceof MultiResult) { 450 multiResult=true; 451 } 452 453 if (params[idx].getType()!=null) { 454 it=methods.iterator(); 455 while (it.hasNext()) { 456 MethodEntry me=(MethodEntry) it.next(); 457 if (!me.method.getParameterTypes()[idx].isAssignableFrom(params[idx].getType())) { 458 it.remove(); 459 } 460 } 461 } 462 } 463 464 it=methods.iterator(); 465 Z: while (it.hasNext()) { 466 MethodEntry me=(MethodEntry) it.next(); 467 Iterator jt=methods.iterator(); 468 while (jt.hasNext()) { 469 switch (((MethodEntry) jt.next()).isMoreSpecific(me)) { 470 case 1: 471 it.remove(); 472 break Z; 473 case -1: 474 jt.remove(); 475 } 476 } 477 } 478 479 // Finding proper method 480 if (methods.isEmpty()) { 481 throw new EvaluationException("No appropriate method '"+methodName+"'"); 482 } 483 484 if (methods.size()>1) { 485 StringBuffer msg=new StringBuffer("Ambiguous method '"+methodName+"': "); 486 it=methods.iterator(); 487 while (it.hasNext()) { 488 msg.append("\n\t"); 489 msg.append(((MethodEntry) it.next()).method); 490 } 491 492 throw new EvaluationException(msg.toString()); 493 } 494 495 final MethodEntry methodEntry=(MethodEntry) methods.get(0); 496 497 if (multiResult) { 498 Collection ret=new ArrayList(); 499 Collection args=new ArrayList(); 500 args.add(new Object[params.length]); 501 for (int i=0; i<params.length; i++) { 502 args=setArgs(args, i, params[i], methodEntry.method.getParameterTypes()[i]); 503 } 504 return new MultiResult(methodEntry.method.getReturnType(), ret, converter); 505 } 506 507 Object[] args=new Object[params.length]; 508 for (int i=0; i<params.length; i++) { 509 args[i]=converter.convert(((SingleResult) params[i]).getValue(), methodEntry.method.getParameterTypes()[i], null); 510 } 511 512 return new SingleResult(this.converter, methodEntry.method.getReturnType(), methodEntry.invoke(args)); 513 } 514 515 private Collection setArgs(Collection args, int idx, Result arg, Class paramType) { 516 if (arg instanceof SingleResult) { 517 Iterator it=args.iterator(); 518 while (it.hasNext()) { 519 ((Object[]) it.next())[idx]=converter.convert(((SingleResult) arg).getValue(), paramType, null); 520 } 521 return args; 522 } 523 524 Collection ret=new ArrayList(); 525 Object[] values=((MultiResult) arg).getValues(); 526 Iterator it=args.iterator(); 527 while (it.hasNext()) { 528 Object[] objs = (Object[]) it.next(); 529 for (int i=0; i<values.length; i++) { 530 Object[] cobjs=(Object[]) objs.clone(); 531 cobjs[idx]=converter.convert(values[i], paramType, null); 532 ret.add(cobjs); 533 } 534 } 535 return ret; 536 } 537 538 /** 539 * @param object 540 * @param values 541 * @throws EvaluationException 542 */ 543 private static void populate(Object object, Collection values) throws EvaluationException { 544 if (object.getClass().isArray()) { 545 for (int i=0, j=Array.getLength(object); i<j; i++) { 546 values.add(Array.get(object, i)); 547 } 548 } else if (object instanceof Collection) { 549 values.addAll((Collection) object); 550 } else if (object instanceof Map) { 551 values.addAll(((Map) object).entrySet()); 552 } else if (object instanceof Iterator) { 553 while (((Iterator) object).hasNext()) { 554 values.add(((Iterator) object).next()); 555 } 556 } else if (object instanceof Enumeration) { 557 while (((Enumeration) object).hasMoreElements()) { 558 values.add(((Enumeration) object).nextElement()); 559 } 560 } else { 561 throw new EvaluationException("forEach() is not applicable for "+object.getClass()); 562 } 563 } 564 565 /** 566 * @param res 567 * @param ast 568 * @param context 569 * @return 570 */ 571 private Result typecast(Result res, AST ast, Context context) { 572 ast.print(ExpressionRecognizer._tokenNames, false); 573 throw new UnsupportedOperationException("Not yet implemented"); 574 } 575 576 /** 577 * @param res 578 * @param ast 579 * @param context 580 * @return 581 */ 582 private Result lnot(Result res, AST ast, Context context) { 583 ast.print(ExpressionRecognizer._tokenNames, false); 584 throw new UnsupportedOperationException("Not yet implemented"); 585 } 586 587 /** 588 * @param res 589 * @param ast 590 * @param context 591 * @return 592 */ 593 private Result plus(Result res, AST ast, Context context) { 594 ast.print(ExpressionRecognizer._tokenNames, false); 595 throw new UnsupportedOperationException("Not yet implemented"); 596 } 597 598 /** 599 * @param res 600 * @param ast 601 * @param context 602 * @return 603 */ 604 private Result minus(Result res, AST ast, Context context) { 605 ast.print(ExpressionRecognizer._tokenNames, false); 606 throw new UnsupportedOperationException("Not yet implemented"); 607 } 608 609 private String identifier(AST ast) throws EvaluationException { 610 switch (ast.getType()) { 611 case ExpressionTokenTypes.IDENT: 612 return ast.getText(); 613 case ExpressionTokenTypes.DOT: 614 return identifier((AST) ast.getFirstChild())+"."+identifier((AST) ast.getFirstChild().getNextSibling()); 615 default: 616 throw new EvaluationException("Unexpected node type: "+ExpressionRecognizer._tokenNames[ast.getType()]); 617 } 618 } 619 620 /** 621 * Translates "indexed" property name. 622 * By default replaces '_' with ' ' 623 * @param name 624 * @return 625 */ 626 protected String translate(String name) { 627 return name.replace('_', ' '); 628 } 629 }