001: //--------------------------------------------------------------------------
002: // Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
003: // All rights reserved.
004: //
005: // Redistribution and use in source and binary forms, with or without
006: // modification, are permitted provided that the following conditions are
007: // met:
008: //
009: // Redistributions of source code must retain the above copyright notice,
010: // this list of conditions and the following disclaimer.
011: // Redistributions in binary form must reproduce the above copyright
012: // notice, this list of conditions and the following disclaimer in the
013: // documentation and/or other materials provided with the distribution.
014: // Neither the name of the Drew Davidson nor the names of its contributors
015: // may be used to endorse or promote products derived from this software
016: // without specific prior written permission.
017: //
018: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
019: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
020: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
021: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
022: // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
023: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
024: // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
025: // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
027: // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
028: // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
029: // DAMAGE.
030: //--------------------------------------------------------------------------
031: package ognl;
032:
033: import java.io.StringReader;
034: import java.util.Map;
035:
036: /**
037: * <P>This class provides static methods for parsing and interpreting OGNL expressions.</P>
038: *
039: * <P>The simplest use of the Ognl class is to get the value of an expression from
040: * an object, without extra context or pre-parsing.</P>
041: *
042: * <PRE>
043: * import ognl.Ognl;
044: * import ognl.OgnlException;
045: *
046: * try {
047: * result = Ognl.getValue(expression, root);
048: * } catch (OgnlException ex) {
049: * // Report error or recover
050: * }
051: * </PRE>
052: *
053: * <P>This will parse the expression given and evaluate it against the root object
054: * given, returning the result. If there is an error in the expression, such
055: * as the property is not found, the exception is encapsulated into an
056: * {@link ognl.OgnlException OgnlException}.</P>
057: *
058: * <P>Other more sophisticated uses of Ognl can pre-parse expressions. This
059: * provides two advantages: in the case of user-supplied expressions it
060: * allows you to catch parse errors before evaluation and it allows you to
061: * cache parsed expressions into an AST for better speed during repeated use.
062: * The pre-parsed expression is always returned as an <CODE>Object</CODE>
063: * to simplify use for programs that just wish to store the value for
064: * repeated use and do not care that it is an AST. If it does care
065: * it can always safely cast the value to an <CODE>AST</CODE> type.</P>
066: *
067: * <P>The Ognl class also takes a <I>context map</I> as one of the parameters
068: * to the set and get methods. This allows you to put your own variables
069: * into the available namespace for OGNL expressions. The default context
070: * contains only the <CODE>#root</CODE> and <CODE>#context</CODE> keys,
071: * which are required to be present. The <CODE>addDefaultContext(Object, Map)</CODE>
072: * method will alter an existing <CODE>Map</CODE> to put the defaults in.
073: * Here is an example that shows how to extract the <CODE>documentName</CODE>
074: * property out of the root object and append a string with the current user
075: * name in parens:</P>
076: *
077: * <PRE>
078: * private Map context = new HashMap();
079: *
080: * public void setUserName(String value)
081: * {
082: * context.put("userName", value);
083: * }
084: *
085: * try {
086: * // get value using our own custom context map
087: * result = Ognl.getValue("documentName + \" (\" + ((#userName == null) ? \"<nobody>\" : #userName) + \")\"", context, root);
088: * } catch (OgnlException ex) {
089: * // Report error or recover
090: * }
091: *
092: * </PRE>
093: *
094: * @author Luke Blanshard (blanshlu@netscape.net)
095: * @author Drew Davidson (drew@ognl.org)
096: * @version 27 June 1999
097: */
098: public abstract class Ognl {
099: /**
100: * Parses the given OGNL expression and returns a tree representation of the
101: * expression that can be used by <CODE>Ognl</CODE> static methods.
102: *
103: * @param expression the OGNL expression to be parsed
104: * @return a tree representation of the expression
105: * @throws ExpressionSyntaxException if the expression is malformed
106: * @throws OgnlException if there is a pathological environmental problem
107: */
108: public static Object parseExpression(String expression)
109: throws OgnlException {
110: try {
111: OgnlParser parser = new OgnlParser(new StringReader(
112: expression));
113: return parser.topLevelExpression();
114: } catch (ParseException e) {
115: throw new ExpressionSyntaxException(expression, e);
116: } catch (TokenMgrError e) {
117: throw new ExpressionSyntaxException(expression, e);
118: }
119: }
120:
121: /**
122: * Creates and returns a new standard naming context for evaluating an OGNL
123: * expression.
124: *
125: * @param root the root of the object graph
126: * @return a new Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
127: * set appropriately
128: */
129: public static Map createDefaultContext(Object root) {
130: return addDefaultContext(root, null, null, null,
131: new OgnlContext());
132: }
133:
134: /**
135: * Creates and returns a new standard naming context for evaluating an OGNL
136: * expression.
137: *
138: * @param root the root of the object graph
139: * @return a new OgnlContext with the keys <CODE>root</CODE> and <CODE>context</CODE>
140: * set appropriately
141: */
142: public static Map createDefaultContext(Object root,
143: ClassResolver classResolver) {
144: return addDefaultContext(root, classResolver, null, null,
145: new OgnlContext());
146: }
147:
148: /**
149: * Creates and returns a new standard naming context for evaluating an OGNL
150: * expression.
151: *
152: * @param root the root of the object graph
153: * @return a new Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
154: * set appropriately
155: */
156: public static Map createDefaultContext(Object root,
157: ClassResolver classResolver, TypeConverter converter) {
158: return addDefaultContext(root, classResolver, converter, null,
159: new OgnlContext());
160: }
161:
162: /**
163: * Creates and returns a new standard naming context for evaluating an OGNL
164: * expression.
165: *
166: * @param root the root of the object graph
167: * @return a new Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
168: * set appropriately
169: */
170: public static Map createDefaultContext(Object root,
171: ClassResolver classResolver, TypeConverter converter,
172: MemberAccess memberAccess) {
173: return addDefaultContext(root, classResolver, converter,
174: memberAccess, new OgnlContext());
175: }
176:
177: /**
178: * Appends the standard naming context for evaluating an OGNL expression
179: * into the context given so that cached maps can be used as a context.
180: *
181: * @param root the root of the object graph
182: * @param context the context to which OGNL context will be added.
183: * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
184: * set appropriately
185: */
186: public static Map addDefaultContext(Object root, Map context) {
187: return addDefaultContext(root, null, null, null, context);
188: }
189:
190: /**
191: * Appends the standard naming context for evaluating an OGNL expression
192: * into the context given so that cached maps can be used as a context.
193: *
194: * @param root the root of the object graph
195: * @param context the context to which OGNL context will be added.
196: * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
197: * set appropriately
198: */
199: public static Map addDefaultContext(Object root,
200: ClassResolver classResolver, Map context) {
201: return addDefaultContext(root, classResolver, null, null,
202: context);
203: }
204:
205: /**
206: * Appends the standard naming context for evaluating an OGNL expression
207: * into the context given so that cached maps can be used as a context.
208: *
209: * @param root the root of the object graph
210: * @param context the context to which OGNL context will be added.
211: * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
212: * set appropriately
213: */
214: public static Map addDefaultContext(Object root,
215: ClassResolver classResolver, TypeConverter converter,
216: Map context) {
217: return addDefaultContext(root, classResolver, converter, null,
218: context);
219: }
220:
221: /**
222: * Appends the standard naming context for evaluating an OGNL expression
223: * into the context given so that cached maps can be used as a context.
224: *
225: * @param root the root of the object graph
226: * @param context the context to which OGNL context will be added.
227: * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
228: * set appropriately
229: */
230: public static Map addDefaultContext(Object root,
231: ClassResolver classResolver, TypeConverter converter,
232: MemberAccess memberAccess, Map context) {
233: OgnlContext result;
234:
235: if (!(context instanceof OgnlContext)) {
236: result = new OgnlContext();
237: result.setValues(context);
238: } else {
239: result = (OgnlContext) context;
240: }
241: if (classResolver != null) {
242: result.setClassResolver(classResolver);
243: }
244: if (converter != null) {
245: result.setTypeConverter(converter);
246: }
247: if (memberAccess != null) {
248: result.setMemberAccess(memberAccess);
249: }
250: result.setRoot(root);
251: return result;
252: }
253:
254: public static void setClassResolver(Map context,
255: ClassResolver classResolver) {
256: context.put(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY,
257: classResolver);
258: }
259:
260: public static ClassResolver getClassResolver(Map context) {
261: return (ClassResolver) context
262: .get(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY);
263: }
264:
265: public static void setTypeConverter(Map context,
266: TypeConverter converter) {
267: context.put(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY, converter);
268: }
269:
270: public static TypeConverter getTypeConverter(Map context) {
271: return (TypeConverter) context
272: .get(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY);
273: }
274:
275: public static void setMemberAccess(Map context,
276: MemberAccess memberAccess) {
277: context
278: .put(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY,
279: memberAccess);
280: }
281:
282: public static MemberAccess getMemberAccess(Map context) {
283: return (MemberAccess) context
284: .get(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY);
285: }
286:
287: public static void setRoot(Map context, Object root) {
288: context.put(OgnlContext.ROOT_CONTEXT_KEY, root);
289: }
290:
291: public static Object getRoot(Map context) {
292: return context.get(OgnlContext.ROOT_CONTEXT_KEY);
293: }
294:
295: public static Evaluation getLastEvaluation(Map context) {
296: return (Evaluation) context
297: .get(OgnlContext.LAST_EVALUATION_CONTEXT_KEY);
298: }
299:
300: /**
301: * Evaluates the given OGNL expression tree to extract a value from the given root
302: * object. The default context is set for the given context and root via
303: * <CODE>addDefaultContext()</CODE>.
304: *
305: * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
306: * @param context the naming context for the evaluation
307: * @param root the root object for the OGNL expression
308: * @return the result of evaluating the expression
309: * @throws MethodFailedException if the expression called a method which failed
310: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
311: * @throws InappropriateExpressionException if the expression can't be used in this context
312: * @throws OgnlException if there is a pathological environmental problem
313: */
314: public static Object getValue(Object tree, Map context, Object root)
315: throws OgnlException {
316: return getValue(tree, context, root, null);
317: }
318:
319: /**
320: * Evaluates the given OGNL expression tree to extract a value from the given root
321: * object. The default context is set for the given context and root via
322: * <CODE>addDefaultContext()</CODE>.
323: *
324: * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
325: * @param context the naming context for the evaluation
326: * @param root the root object for the OGNL expression
327: * @param resultType the converted type of the resultant object, using the context's type converter
328: * @return the result of evaluating the expression
329: * @throws MethodFailedException if the expression called a method which failed
330: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
331: * @throws InappropriateExpressionException if the expression can't be used in this context
332: * @throws OgnlException if there is a pathological environmental problem
333: */
334: public static Object getValue(Object tree, Map context,
335: Object root, Class resultType) throws OgnlException {
336: Object result;
337: OgnlContext ognlContext = (OgnlContext) addDefaultContext(root,
338: context);
339:
340: result = ((Node) tree).getValue(ognlContext, root);
341: if (resultType != null) {
342: result = getTypeConverter(context).convertValue(context,
343: root, null, null, result, resultType);
344: }
345: return result;
346: }
347:
348: /**
349: * Evaluates the given OGNL expression to extract a value from the given root
350: * object in a given context
351: *
352: * @see #parseExpression(String)
353: * @see #getValue(Object,Object)
354: * @param expression the OGNL expression to be parsed
355: * @param context the naming context for the evaluation
356: * @param root the root object for the OGNL expression
357: * @return the result of evaluating the expression
358: * @throws MethodFailedException if the expression called a method which failed
359: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
360: * @throws InappropriateExpressionException if the expression can't be used in this context
361: * @throws OgnlException if there is a pathological environmental problem
362: */
363: public static Object getValue(String expression, Map context,
364: Object root) throws OgnlException {
365: return getValue(expression, context, root, null);
366: }
367:
368: /**
369: * Evaluates the given OGNL expression to extract a value from the given root
370: * object in a given context
371: *
372: * @see #parseExpression(String)
373: * @see #getValue(Object,Object)
374: * @param expression the OGNL expression to be parsed
375: * @param context the naming context for the evaluation
376: * @param root the root object for the OGNL expression
377: * @param resultType the converted type of the resultant object, using the context's type converter
378: * @return the result of evaluating the expression
379: * @throws MethodFailedException if the expression called a method which failed
380: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
381: * @throws InappropriateExpressionException if the expression can't be used in this context
382: * @throws OgnlException if there is a pathological environmental problem
383: */
384: public static Object getValue(String expression, Map context,
385: Object root, Class resultType) throws OgnlException {
386: return getValue(parseExpression(expression), context, root,
387: resultType);
388: }
389:
390: /**
391: * Evaluates the given OGNL expression tree to extract a value from the given root
392: * object.
393: *
394: * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
395: * @param root the root object for the OGNL expression
396: * @return the result of evaluating the expression
397: * @throws MethodFailedException if the expression called a method which failed
398: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
399: * @throws InappropriateExpressionException if the expression can't be used in this context
400: * @throws OgnlException if there is a pathological environmental problem
401: */
402: public static Object getValue(Object tree, Object root)
403: throws OgnlException {
404: return getValue(tree, root, null);
405: }
406:
407: /**
408: * Evaluates the given OGNL expression tree to extract a value from the given root
409: * object.
410: *
411: * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
412: * @param root the root object for the OGNL expression
413: * @param resultType the converted type of the resultant object, using the context's type converter
414: * @return the result of evaluating the expression
415: * @throws MethodFailedException if the expression called a method which failed
416: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
417: * @throws InappropriateExpressionException if the expression can't be used in this context
418: * @throws OgnlException if there is a pathological environmental problem
419: */
420: public static Object getValue(Object tree, Object root,
421: Class resultType) throws OgnlException {
422: return getValue(tree, createDefaultContext(root), root,
423: resultType);
424: }
425:
426: /**
427: * Convenience method that combines calls to <code> parseExpression </code> and
428: * <code> getValue</code>.
429: *
430: * @see #parseExpression(String)
431: * @see #getValue(Object,Object)
432: * @param expression the OGNL expression to be parsed
433: * @param root the root object for the OGNL expression
434: * @return the result of evaluating the expression
435: * @throws ExpressionSyntaxException if the expression is malformed
436: * @throws MethodFailedException if the expression called a method which failed
437: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
438: * @throws InappropriateExpressionException if the expression can't be used in this context
439: * @throws OgnlException if there is a pathological environmental problem
440: */
441: public static Object getValue(String expression, Object root)
442: throws OgnlException {
443: return getValue(expression, root, null);
444: }
445:
446: /**
447: * Convenience method that combines calls to <code> parseExpression </code> and
448: * <code> getValue</code>.
449: *
450: * @see #parseExpression(String)
451: * @see #getValue(Object,Object)
452: * @param expression the OGNL expression to be parsed
453: * @param root the root object for the OGNL expression
454: * @param resultType the converted type of the resultant object, using the context's type converter
455: * @return the result of evaluating the expression
456: * @throws ExpressionSyntaxException if the expression is malformed
457: * @throws MethodFailedException if the expression called a method which failed
458: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
459: * @throws InappropriateExpressionException if the expression can't be used in this context
460: * @throws OgnlException if there is a pathological environmental problem
461: */
462: public static Object getValue(String expression, Object root,
463: Class resultType) throws OgnlException {
464: return getValue(parseExpression(expression), root, resultType);
465: }
466:
467: /**
468: * Evaluates the given OGNL expression tree to insert a value into the object graph
469: * rooted at the given root object. The default context is set for the given
470: * context and root via <CODE>addDefaultContext()</CODE>.
471: *
472: * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
473: * @param context the naming context for the evaluation
474: * @param root the root object for the OGNL expression
475: * @param value the value to insert into the object graph
476: * @throws MethodFailedException if the expression called a method which failed
477: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
478: * @throws InappropriateExpressionException if the expression can't be used in this context
479: * @throws OgnlException if there is a pathological environmental problem
480: */
481: public static void setValue(Object tree, Map context, Object root,
482: Object value) throws OgnlException {
483: OgnlContext ognlContext = (OgnlContext) addDefaultContext(root,
484: context);
485: Node n = (Node) tree;
486:
487: n.setValue(ognlContext, root, value);
488: }
489:
490: /**
491: * Evaluates the given OGNL expression to insert a value into the object graph
492: * rooted at the given root object given the context.
493: *
494: * @param expression the OGNL expression to be parsed
495: * @param root the root object for the OGNL expression
496: * @param context the naming context for the evaluation
497: * @param value the value to insert into the object graph
498: * @throws MethodFailedException if the expression called a method which failed
499: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
500: * @throws InappropriateExpressionException if the expression can't be used in this context
501: * @throws OgnlException if there is a pathological environmental problem
502: */
503: public static void setValue(String expression, Map context,
504: Object root, Object value) throws OgnlException {
505: setValue(parseExpression(expression), context, root, value);
506: }
507:
508: /**
509: * Evaluates the given OGNL expression tree to insert a value into the object graph
510: * rooted at the given root object.
511: *
512: * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
513: * @param root the root object for the OGNL expression
514: * @param value the value to insert into the object graph
515: * @throws MethodFailedException if the expression called a method which failed
516: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
517: * @throws InappropriateExpressionException if the expression can't be used in this context
518: * @throws OgnlException if there is a pathological environmental problem
519: */
520: public static void setValue(Object tree, Object root, Object value)
521: throws OgnlException {
522: setValue(tree, createDefaultContext(root), root, value);
523: }
524:
525: /**
526: * Convenience method that combines calls to <code> parseExpression </code> and
527: * <code> setValue</code>.
528: *
529: * @see #parseExpression(String)
530: * @see #setValue(Object,Object,Object)
531: * @param expression the OGNL expression to be parsed
532: * @param root the root object for the OGNL expression
533: * @param value the value to insert into the object graph
534: * @throws ExpressionSyntaxException if the expression is malformed
535: * @throws MethodFailedException if the expression called a method which failed
536: * @throws NoSuchPropertyException if the expression referred to a nonexistent property
537: * @throws InappropriateExpressionException if the expression can't be used in this context
538: * @throws OgnlException if there is a pathological environmental problem
539: */
540: public static void setValue(String expression, Object root,
541: Object value) throws OgnlException {
542: setValue(parseExpression(expression), root, value);
543: }
544:
545: public static boolean isConstant(Object tree, Map context)
546: throws OgnlException {
547: return ((SimpleNode) tree)
548: .isConstant((OgnlContext) addDefaultContext(null,
549: context));
550: }
551:
552: public static boolean isConstant(String expression, Map context)
553: throws OgnlException {
554: return isConstant(parseExpression(expression), context);
555: }
556:
557: public static boolean isConstant(Object tree) throws OgnlException {
558: return isConstant(tree, createDefaultContext(null));
559: }
560:
561: public static boolean isConstant(String expression)
562: throws OgnlException {
563: return isConstant(parseExpression(expression),
564: createDefaultContext(null));
565: }
566:
567: public static boolean isSimpleProperty(Object tree, Map context)
568: throws OgnlException {
569: return ((SimpleNode) tree)
570: .isSimpleProperty((OgnlContext) addDefaultContext(null,
571: context));
572: }
573:
574: public static boolean isSimpleProperty(String expression,
575: Map context) throws OgnlException {
576: return isSimpleProperty(parseExpression(expression), context);
577: }
578:
579: public static boolean isSimpleProperty(Object tree)
580: throws OgnlException {
581: return isSimpleProperty(tree, createDefaultContext(null));
582: }
583:
584: public static boolean isSimpleProperty(String expression)
585: throws OgnlException {
586: return isSimpleProperty(parseExpression(expression),
587: createDefaultContext(null));
588: }
589:
590: public static boolean isSimpleNavigationChain(Object tree,
591: Map context) throws OgnlException {
592: return ((SimpleNode) tree)
593: .isSimpleNavigationChain((OgnlContext) addDefaultContext(
594: null, context));
595: }
596:
597: public static boolean isSimpleNavigationChain(String expression,
598: Map context) throws OgnlException {
599: return isSimpleNavigationChain(parseExpression(expression),
600: context);
601: }
602:
603: public static boolean isSimpleNavigationChain(Object tree)
604: throws OgnlException {
605: return isSimpleNavigationChain(tree, createDefaultContext(null));
606: }
607:
608: public static boolean isSimpleNavigationChain(String expression)
609: throws OgnlException {
610: return isSimpleNavigationChain(parseExpression(expression),
611: createDefaultContext(null));
612: }
613:
614: /** You can't make one of these. */
615: private Ognl() {
616: }
617: }
|