Source code: ognl/Ognl.java
1 //--------------------------------------------------------------------------
2 // Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // Neither the name of the Drew Davidson nor the names of its contributors
15 // may be used to endorse or promote products derived from this software
16 // without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 // DAMAGE.
30 //--------------------------------------------------------------------------
31 package ognl;
32
33 import java.io.StringReader;
34 import java.util.Map;
35
36 /**
37 * <P>This class provides static methods for parsing and interpreting OGNL expressions.</P>
38 *
39 * <P>The simplest use of the Ognl class is to get the value of an expression from
40 * an object, without extra context or pre-parsing.</P>
41 *
42 * <PRE>
43 * import ognl.Ognl;
44 * import ognl.OgnlException;
45 *
46 * try {
47 * result = Ognl.getValue(expression, root);
48 * } catch (OgnlException ex) {
49 * // Report error or recover
50 * }
51 * </PRE>
52 *
53 * <P>This will parse the expression given and evaluate it against the root object
54 * given, returning the result. If there is an error in the expression, such
55 * as the property is not found, the exception is encapsulated into an
56 * {@link ognl.OgnlException OgnlException}.</P>
57 *
58 * <P>Other more sophisticated uses of Ognl can pre-parse expressions. This
59 * provides two advantages: in the case of user-supplied expressions it
60 * allows you to catch parse errors before evaluation and it allows you to
61 * cache parsed expressions into an AST for better speed during repeated use.
62 * The pre-parsed expression is always returned as an <CODE>Object</CODE>
63 * to simplify use for programs that just wish to store the value for
64 * repeated use and do not care that it is an AST. If it does care
65 * it can always safely cast the value to an <CODE>AST</CODE> type.</P>
66 *
67 * <P>The Ognl class also takes a <I>context map</I> as one of the parameters
68 * to the set and get methods. This allows you to put your own variables
69 * into the available namespace for OGNL expressions. The default context
70 * contains only the <CODE>#root</CODE> and <CODE>#context</CODE> keys,
71 * which are required to be present. The <CODE>addDefaultContext(Object, Map)</CODE>
72 * method will alter an existing <CODE>Map</CODE> to put the defaults in.
73 * Here is an example that shows how to extract the <CODE>documentName</CODE>
74 * property out of the root object and append a string with the current user
75 * name in parens:</P>
76 *
77 * <PRE>
78 * private Map context = new HashMap();
79 *
80 * public void setUserName(String value)
81 * {
82 * context.put("userName", value);
83 * }
84 *
85 * try {
86 * // get value using our own custom context map
87 * result = Ognl.getValue("documentName + \" (\" + ((#userName == null) ? \"<nobody>\" : #userName) + \")\"", context, root);
88 * } catch (OgnlException ex) {
89 * // Report error or recover
90 * }
91 *
92 * </PRE>
93 *
94 * @author Luke Blanshard (blanshlu@netscape.net)
95 * @author Drew Davidson (drew@ognl.org)
96 * @version 27 June 1999
97 */
98 public abstract class Ognl
99 {
100 /**
101 * Parses the given OGNL expression and returns a tree representation of the
102 * expression that can be used by <CODE>Ognl</CODE> static methods.
103 *
104 * @param expression the OGNL expression to be parsed
105 * @return a tree representation of the expression
106 * @throws ExpressionSyntaxException if the expression is malformed
107 * @throws OgnlException if there is a pathological environmental problem
108 */
109 public static Object parseExpression( String expression ) throws OgnlException
110 {
111 try {
112 OgnlParser parser = new OgnlParser( new StringReader(expression) );
113 return parser.topLevelExpression();
114 }
115 catch (ParseException e) {
116 throw new ExpressionSyntaxException( expression, e );
117 }
118 catch (TokenMgrError e) {
119 throw new ExpressionSyntaxException( expression, e );
120 }
121 }
122
123 /**
124 * Creates and returns a new standard naming context for evaluating an OGNL
125 * expression.
126 *
127 * @param root the root of the object graph
128 * @return a new Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
129 * set appropriately
130 */
131 public static Map createDefaultContext( Object root )
132 {
133 return addDefaultContext( root, null, null, null, new OgnlContext() );
134 }
135
136 /**
137 * Creates and returns a new standard naming context for evaluating an OGNL
138 * expression.
139 *
140 * @param root the root of the object graph
141 * @return a new OgnlContext with the keys <CODE>root</CODE> and <CODE>context</CODE>
142 * set appropriately
143 */
144 public static Map createDefaultContext( Object root, ClassResolver classResolver )
145 {
146 return addDefaultContext( root, classResolver, null, null, new OgnlContext() );
147 }
148
149 /**
150 * Creates and returns a new standard naming context for evaluating an OGNL
151 * expression.
152 *
153 * @param root the root of the object graph
154 * @return a new Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
155 * set appropriately
156 */
157 public static Map createDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter )
158 {
159 return addDefaultContext( root, classResolver, converter, null, 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, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess )
171 {
172 return addDefaultContext( root, classResolver, converter, memberAccess, new OgnlContext() );
173 }
174
175 /**
176 * Appends the standard naming context for evaluating an OGNL expression
177 * into the context given so that cached maps can be used as a context.
178 *
179 * @param root the root of the object graph
180 * @param context the context to which OGNL context will be added.
181 * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
182 * set appropriately
183 */
184 public static Map addDefaultContext( Object root, Map context )
185 {
186 return addDefaultContext( root, null, null, null, context );
187 }
188
189 /**
190 * Appends the standard naming context for evaluating an OGNL expression
191 * into the context given so that cached maps can be used as a context.
192 *
193 * @param root the root of the object graph
194 * @param context the context to which OGNL context will be added.
195 * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
196 * set appropriately
197 */
198 public static Map addDefaultContext( Object root, ClassResolver classResolver, Map context )
199 {
200 return addDefaultContext(root, classResolver, null, null, context);
201 }
202
203 /**
204 * Appends the standard naming context for evaluating an OGNL expression
205 * into the context given so that cached maps can be used as a context.
206 *
207 * @param root the root of the object graph
208 * @param context the context to which OGNL context will be added.
209 * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
210 * set appropriately
211 */
212 public static Map addDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter, Map context )
213 {
214 return addDefaultContext(root, classResolver, converter, null, context);
215 }
216
217 /**
218 * Appends the standard naming context for evaluating an OGNL expression
219 * into the context given so that cached maps can be used as a context.
220 *
221 * @param root the root of the object graph
222 * @param context the context to which OGNL context will be added.
223 * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
224 * set appropriately
225 */
226 public static Map addDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context )
227 {
228 OgnlContext result;
229
230 if (!(context instanceof OgnlContext)) {
231 result = new OgnlContext();
232 result.setValues(context);
233 } else {
234 result = (OgnlContext)context;
235 }
236 if (classResolver != null) {
237 result.setClassResolver(classResolver);
238 }
239 if (converter != null) {
240 result.setTypeConverter(converter);
241 }
242 if (memberAccess != null) {
243 result.setMemberAccess(memberAccess);
244 }
245 result.setRoot(root);
246 return result;
247 }
248
249 public static void setClassResolver( Map context, ClassResolver classResolver)
250 {
251 context.put(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY, classResolver);
252 }
253
254 public static ClassResolver getClassResolver( Map context )
255 {
256 return (ClassResolver)context.get(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY);
257 }
258
259 public static void setTypeConverter( Map context, TypeConverter converter)
260 {
261 context.put(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY, converter);
262 }
263
264 public static TypeConverter getTypeConverter( Map context )
265 {
266 return (TypeConverter)context.get(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY);
267 }
268
269 public static void setMemberAccess(Map context, MemberAccess memberAccess)
270 {
271 context.put(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY, memberAccess);
272 }
273
274 public static MemberAccess getMemberAccess(Map context)
275 {
276 return (MemberAccess)context.get(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY);
277 }
278
279 public static void setRoot( Map context, Object root)
280 {
281 context.put(OgnlContext.ROOT_CONTEXT_KEY, root);
282 }
283
284 public static Object getRoot( Map context )
285 {
286 return context.get(OgnlContext.ROOT_CONTEXT_KEY);
287 }
288
289 public static Evaluation getLastEvaluation( Map context )
290 {
291 return (Evaluation)context.get(OgnlContext.LAST_EVALUATION_CONTEXT_KEY);
292 }
293
294 /**
295 * Evaluates the given OGNL expression tree to extract a value from the given root
296 * object. The default context is set for the given context and root via
297 * <CODE>addDefaultContext()</CODE>.
298 *
299 * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
300 * @param context the naming context for the evaluation
301 * @param root the root object for the OGNL expression
302 * @return the result of evaluating the expression
303 * @throws MethodFailedException if the expression called a method which failed
304 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
305 * @throws InappropriateExpressionException if the expression can't be used in this context
306 * @throws OgnlException if there is a pathological environmental problem
307 */
308 public static Object getValue( Object tree, Map context, Object root ) throws OgnlException
309 {
310 return getValue( tree, context, root, null );
311 }
312
313 /**
314 * Evaluates the given OGNL expression tree to extract a value from the given root
315 * object. The default context is set for the given context and root via
316 * <CODE>addDefaultContext()</CODE>.
317 *
318 * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
319 * @param context the naming context for the evaluation
320 * @param root the root object for the OGNL expression
321 * @param resultType the converted type of the resultant object, using the context's type converter
322 * @return the result of evaluating the expression
323 * @throws MethodFailedException if the expression called a method which failed
324 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
325 * @throws InappropriateExpressionException if the expression can't be used in this context
326 * @throws OgnlException if there is a pathological environmental problem
327 */
328 public static Object getValue( Object tree, Map context, Object root, Class resultType ) throws OgnlException
329 {
330 Object result;
331 OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context);
332
333 result = ((Node)tree).getValue( ognlContext, root );
334 if (resultType != null) {
335 result = getTypeConverter( context ).convertValue( context, root, null, null, result, resultType);
336 }
337 return result;
338 }
339
340 /**
341 * Evaluates the given OGNL expression to extract a value from the given root
342 * object in a given context
343 *
344 * @see #parseExpression(String)
345 * @see #getValue(Object,Object)
346 * @param expression the OGNL expression to be parsed
347 * @param context the naming context for the evaluation
348 * @param root the root object for the OGNL expression
349 * @return the result of evaluating the expression
350 * @throws MethodFailedException if the expression called a method which failed
351 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
352 * @throws InappropriateExpressionException if the expression can't be used in this context
353 * @throws OgnlException if there is a pathological environmental problem
354 */
355 public static Object getValue( String expression, Map context, Object root ) throws OgnlException
356 {
357 return getValue( expression, context, root, null );
358 }
359
360 /**
361 * Evaluates the given OGNL expression to extract a value from the given root
362 * object in a given context
363 *
364 * @see #parseExpression(String)
365 * @see #getValue(Object,Object)
366 * @param expression the OGNL expression to be parsed
367 * @param context the naming context for the evaluation
368 * @param root the root object for the OGNL expression
369 * @param resultType the converted type of the resultant object, using the context's type converter
370 * @return the result of evaluating the expression
371 * @throws MethodFailedException if the expression called a method which failed
372 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
373 * @throws InappropriateExpressionException if the expression can't be used in this context
374 * @throws OgnlException if there is a pathological environmental problem
375 */
376 public static Object getValue( String expression, Map context, Object root, Class resultType ) throws OgnlException
377 {
378 return getValue( parseExpression(expression), context, root, resultType );
379 }
380
381 /**
382 * Evaluates the given OGNL expression tree to extract a value from the given root
383 * object.
384 *
385 * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
386 * @param root the root object for the OGNL expression
387 * @return the result of evaluating the expression
388 * @throws MethodFailedException if the expression called a method which failed
389 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
390 * @throws InappropriateExpressionException if the expression can't be used in this context
391 * @throws OgnlException if there is a pathological environmental problem
392 */
393 public static Object getValue( Object tree, Object root ) throws OgnlException
394 {
395 return getValue( tree, root, null );
396 }
397
398 /**
399 * Evaluates the given OGNL expression tree to extract a value from the given root
400 * object.
401 *
402 * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
403 * @param root the root object for the OGNL expression
404 * @param resultType the converted type of the resultant object, using the context's type converter
405 * @return the result of evaluating the expression
406 * @throws MethodFailedException if the expression called a method which failed
407 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
408 * @throws InappropriateExpressionException if the expression can't be used in this context
409 * @throws OgnlException if there is a pathological environmental problem
410 */
411 public static Object getValue( Object tree, Object root, Class resultType ) throws OgnlException
412 {
413 return getValue( tree, createDefaultContext(root), root, resultType );
414 }
415
416 /**
417 * Convenience method that combines calls to <code> parseExpression </code> and
418 * <code> getValue</code>.
419 *
420 * @see #parseExpression(String)
421 * @see #getValue(Object,Object)
422 * @param expression the OGNL expression to be parsed
423 * @param root the root object for the OGNL expression
424 * @return the result of evaluating the expression
425 * @throws ExpressionSyntaxException if the expression is malformed
426 * @throws MethodFailedException if the expression called a method which failed
427 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
428 * @throws InappropriateExpressionException if the expression can't be used in this context
429 * @throws OgnlException if there is a pathological environmental problem
430 */
431 public static Object getValue( String expression, Object root ) throws OgnlException
432 {
433 return getValue( expression, root, null );
434 }
435
436 /**
437 * Convenience method that combines calls to <code> parseExpression </code> and
438 * <code> getValue</code>.
439 *
440 * @see #parseExpression(String)
441 * @see #getValue(Object,Object)
442 * @param expression the OGNL expression to be parsed
443 * @param root the root object for the OGNL expression
444 * @param resultType the converted type of the resultant object, using the context's type converter
445 * @return the result of evaluating the expression
446 * @throws ExpressionSyntaxException if the expression is malformed
447 * @throws MethodFailedException if the expression called a method which failed
448 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
449 * @throws InappropriateExpressionException if the expression can't be used in this context
450 * @throws OgnlException if there is a pathological environmental problem
451 */
452 public static Object getValue( String expression, Object root, Class resultType ) throws OgnlException
453 {
454 return getValue( parseExpression(expression), root, resultType );
455 }
456
457 /**
458 * Evaluates the given OGNL expression tree to insert a value into the object graph
459 * rooted at the given root object. The default context is set for the given
460 * context and root via <CODE>addDefaultContext()</CODE>.
461 *
462 * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
463 * @param context the naming context for the evaluation
464 * @param root the root object for the OGNL expression
465 * @param value the value to insert into the object graph
466 * @throws MethodFailedException if the expression called a method which failed
467 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
468 * @throws InappropriateExpressionException if the expression can't be used in this context
469 * @throws OgnlException if there is a pathological environmental problem
470 */
471 public static void setValue( Object tree, Map context, Object root, Object value ) throws OgnlException
472 {
473 OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context);
474 Node n = (Node) tree;
475
476 n.setValue( ognlContext, root, value );
477 }
478
479 /**
480 * Evaluates the given OGNL expression to insert a value into the object graph
481 * rooted at the given root object given the context.
482 *
483 * @param expression the OGNL expression to be parsed
484 * @param root the root object for the OGNL expression
485 * @param context the naming context for the evaluation
486 * @param value the value to insert into the object graph
487 * @throws MethodFailedException if the expression called a method which failed
488 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
489 * @throws InappropriateExpressionException if the expression can't be used in this context
490 * @throws OgnlException if there is a pathological environmental problem
491 */
492 public static void setValue( String expression, Map context, Object root, Object value ) throws OgnlException
493 {
494 setValue( parseExpression(expression), context, root, value );
495 }
496
497 /**
498 * Evaluates the given OGNL expression tree to insert a value into the object graph
499 * rooted at the given root object.
500 *
501 * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
502 * @param root the root object for the OGNL expression
503 * @param value the value to insert into the object graph
504 * @throws MethodFailedException if the expression called a method which failed
505 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
506 * @throws InappropriateExpressionException if the expression can't be used in this context
507 * @throws OgnlException if there is a pathological environmental problem
508 */
509 public static void setValue( Object tree, Object root, Object value ) throws OgnlException
510 {
511 setValue( tree, createDefaultContext(root), root, value );
512 }
513
514 /**
515 * Convenience method that combines calls to <code> parseExpression </code> and
516 * <code> setValue</code>.
517 *
518 * @see #parseExpression(String)
519 * @see #setValue(Object,Object,Object)
520 * @param expression the OGNL expression to be parsed
521 * @param root the root object for the OGNL expression
522 * @param value the value to insert into the object graph
523 * @throws ExpressionSyntaxException if the expression is malformed
524 * @throws MethodFailedException if the expression called a method which failed
525 * @throws NoSuchPropertyException if the expression referred to a nonexistent property
526 * @throws InappropriateExpressionException if the expression can't be used in this context
527 * @throws OgnlException if there is a pathological environmental problem
528 */
529 public static void setValue( String expression, Object root, Object value ) throws OgnlException
530 {
531 setValue( parseExpression(expression), root, value );
532 }
533
534 public static boolean isConstant( Object tree, Map context ) throws OgnlException
535 {
536 return ((SimpleNode)tree).isConstant( (OgnlContext)addDefaultContext( null, context ) );
537 }
538
539 public static boolean isConstant( String expression, Map context ) throws OgnlException
540 {
541 return isConstant(parseExpression(expression), context);
542 }
543
544 public static boolean isConstant( Object tree ) throws OgnlException
545 {
546 return isConstant(tree, createDefaultContext(null));
547 }
548
549 public static boolean isConstant( String expression ) throws OgnlException
550 {
551 return isConstant(parseExpression(expression), createDefaultContext(null));
552 }
553
554 public static boolean isSimpleProperty( Object tree, Map context ) throws OgnlException
555 {
556 return ((SimpleNode)tree).isSimpleProperty( (OgnlContext)addDefaultContext( null, context ) );
557 }
558
559 public static boolean isSimpleProperty( String expression, Map context ) throws OgnlException
560 {
561 return isSimpleProperty(parseExpression(expression), context);
562 }
563
564 public static boolean isSimpleProperty( Object tree ) throws OgnlException
565 {
566 return isSimpleProperty(tree, createDefaultContext(null));
567 }
568
569 public static boolean isSimpleProperty( String expression ) throws OgnlException
570 {
571 return isSimpleProperty(parseExpression(expression), createDefaultContext(null));
572 }
573
574 public static boolean isSimpleNavigationChain( Object tree, Map context ) throws OgnlException
575 {
576 return ((SimpleNode)tree).isSimpleNavigationChain( (OgnlContext)addDefaultContext( null, context ) );
577 }
578
579 public static boolean isSimpleNavigationChain( String expression, Map context ) throws OgnlException
580 {
581 return isSimpleNavigationChain(parseExpression(expression), context);
582 }
583
584 public static boolean isSimpleNavigationChain( Object tree ) throws OgnlException
585 {
586 return isSimpleNavigationChain(tree, createDefaultContext(null));
587 }
588
589 public static boolean isSimpleNavigationChain( String expression ) throws OgnlException
590 {
591 return isSimpleNavigationChain(parseExpression(expression), createDefaultContext(null));
592 }
593
594 /** You can't make one of these. */
595 private Ognl()
596 {
597 }
598 }