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