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