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.util.*;
034:
035: /**
036: * This class defines the execution context for an OGNL expression
037: * @author Luke Blanshard (blanshlu@netscape.net)
038: * @author Drew Davidson (drew@ognl.org)
039: */
040: public class OgnlContext extends Object implements Map {
041: public static final String CONTEXT_CONTEXT_KEY = "context";
042: public static final String ROOT_CONTEXT_KEY = "root";
043: public static final String THIS_CONTEXT_KEY = "this";
044: public static final String TRACE_EVALUATIONS_CONTEXT_KEY = "_traceEvaluations";
045: public static final String LAST_EVALUATION_CONTEXT_KEY = "_lastEvaluation";
046: public static final String KEEP_LAST_EVALUATION_CONTEXT_KEY = "_keepLastEvaluation";
047: public static final String CLASS_RESOLVER_CONTEXT_KEY = "_classResolver";
048: public static final String TYPE_CONVERTER_CONTEXT_KEY = "_typeConverter";
049: public static final String MEMBER_ACCESS_CONTEXT_KEY = "_memberAccess";
050:
051: private static final String PROPERTY_KEY_PREFIX = "ognl";
052: private static boolean DEFAULT_TRACE_EVALUATIONS = false;
053: private static boolean DEFAULT_KEEP_LAST_EVALUATION = false;
054:
055: public static final ClassResolver DEFAULT_CLASS_RESOLVER = new DefaultClassResolver();
056: public static final TypeConverter DEFAULT_TYPE_CONVERTER = new DefaultTypeConverter();
057: public static final MemberAccess DEFAULT_MEMBER_ACCESS = new DefaultMemberAccess(
058: false);
059:
060: private static Map RESERVED_KEYS = new HashMap(11);
061:
062: private Object root;
063: private Object currentObject;
064: private Node currentNode;
065: private boolean traceEvaluations = DEFAULT_TRACE_EVALUATIONS;
066: private Evaluation rootEvaluation;
067: private Evaluation currentEvaluation;
068: private Evaluation lastEvaluation;
069: private boolean keepLastEvaluation = DEFAULT_KEEP_LAST_EVALUATION;
070: private Map values = new HashMap(23);
071: private ClassResolver classResolver = DEFAULT_CLASS_RESOLVER;
072: private TypeConverter typeConverter = DEFAULT_TYPE_CONVERTER;
073: private MemberAccess memberAccess = DEFAULT_MEMBER_ACCESS;
074:
075: static {
076: String s;
077:
078: RESERVED_KEYS.put(CONTEXT_CONTEXT_KEY, null);
079: RESERVED_KEYS.put(ROOT_CONTEXT_KEY, null);
080: RESERVED_KEYS.put(THIS_CONTEXT_KEY, null);
081: RESERVED_KEYS.put(TRACE_EVALUATIONS_CONTEXT_KEY, null);
082: RESERVED_KEYS.put(LAST_EVALUATION_CONTEXT_KEY, null);
083: RESERVED_KEYS.put(KEEP_LAST_EVALUATION_CONTEXT_KEY, null);
084: RESERVED_KEYS.put(CLASS_RESOLVER_CONTEXT_KEY, null);
085: RESERVED_KEYS.put(TYPE_CONVERTER_CONTEXT_KEY, null);
086: RESERVED_KEYS.put(MEMBER_ACCESS_CONTEXT_KEY, null);
087:
088: try {
089: if ((s = System.getProperty(PROPERTY_KEY_PREFIX
090: + ".traceEvaluations")) != null) {
091: DEFAULT_TRACE_EVALUATIONS = Boolean.valueOf(s.trim())
092: .booleanValue();
093: }
094: if ((s = System.getProperty(PROPERTY_KEY_PREFIX
095: + ".keepLastEvaluation")) != null) {
096: DEFAULT_KEEP_LAST_EVALUATION = Boolean
097: .valueOf(s.trim()).booleanValue();
098: }
099: } catch (SecurityException ex) {
100: // restricted access environment, just keep defaults
101: }
102: }
103:
104: /**
105: Constructs a new OgnlContext with the default class resolver, type converter and
106: member access.
107: */
108: public OgnlContext() {
109: super ();
110: }
111:
112: /**
113: Constructs a new OgnlContext with the given class resolver, type converter and
114: member access. If any of these parameters is null the default will be used.
115: */
116: public OgnlContext(ClassResolver classResolver,
117: TypeConverter typeConverter, MemberAccess memberAccess) {
118: this ();
119: if (classResolver != null) {
120: this .classResolver = classResolver;
121: }
122: if (typeConverter != null) {
123: this .typeConverter = typeConverter;
124: }
125: if (memberAccess != null) {
126: this .memberAccess = memberAccess;
127: }
128: }
129:
130: public OgnlContext(Map values) {
131: super ();
132: this .values = values;
133: }
134:
135: public OgnlContext(ClassResolver classResolver,
136: TypeConverter typeConverter, MemberAccess memberAccess,
137: Map values) {
138: this (classResolver, typeConverter, memberAccess);
139: this .values = values;
140: }
141:
142: public void setValues(Map value) {
143: for (Iterator it = value.keySet().iterator(); it.hasNext();) {
144: Object k = it.next();
145:
146: values.put(k, value.get(k));
147: }
148: }
149:
150: public Map getValues() {
151: return values;
152: }
153:
154: public void setClassResolver(ClassResolver value) {
155: if (value == null) {
156: throw new IllegalArgumentException(
157: "cannot set ClassResolver to null");
158: }
159: classResolver = value;
160: }
161:
162: public ClassResolver getClassResolver() {
163: return classResolver;
164: }
165:
166: public void setTypeConverter(TypeConverter value) {
167: if (value == null) {
168: throw new IllegalArgumentException(
169: "cannot set TypeConverter to null");
170: }
171: typeConverter = value;
172: }
173:
174: public TypeConverter getTypeConverter() {
175: return typeConverter;
176: }
177:
178: public void setMemberAccess(MemberAccess value) {
179: if (value == null) {
180: throw new IllegalArgumentException(
181: "cannot set MemberAccess to null");
182: }
183: memberAccess = value;
184: }
185:
186: public MemberAccess getMemberAccess() {
187: return memberAccess;
188: }
189:
190: public void setRoot(Object value) {
191: root = value;
192: }
193:
194: public Object getRoot() {
195: return root;
196: }
197:
198: public boolean getTraceEvaluations() {
199: return traceEvaluations;
200: }
201:
202: public void setTraceEvaluations(boolean value) {
203: traceEvaluations = value;
204: }
205:
206: public Evaluation getLastEvaluation() {
207: return lastEvaluation;
208: }
209:
210: public void setLastEvaluation(Evaluation value) {
211: lastEvaluation = value;
212: }
213:
214: /**
215: This method can be called when the last evaluation has been used
216: and can be returned for reuse in the free pool maintained by the
217: runtime. This is not a necessary step, but is useful for keeping
218: memory usage down. This will recycle the last evaluation and then
219: set the last evaluation to null.
220: */
221: public void recycleLastEvaluation() {
222: OgnlRuntime.getEvaluationPool().recycleAll(lastEvaluation);
223: lastEvaluation = null;
224: }
225:
226: /**
227: Returns true if the last evaluation that was done on this
228: context is retained and available through <code>getLastEvaluation()</code>.
229: The default is true.
230: */
231: public boolean getKeepLastEvaluation() {
232: return keepLastEvaluation;
233: }
234:
235: /**
236: Sets whether the last evaluation that was done on this
237: context is retained and available through <code>getLastEvaluation()</code>.
238: The default is true.
239: */
240: public void setKeepLastEvaluation(boolean value) {
241: keepLastEvaluation = value;
242: }
243:
244: public void setCurrentObject(Object value) {
245: currentObject = value;
246: }
247:
248: public Object getCurrentObject() {
249: return currentObject;
250: }
251:
252: public void setCurrentNode(Node value) {
253: currentNode = value;
254: }
255:
256: public Node getCurrentNode() {
257: return currentNode;
258: }
259:
260: /**
261: Gets the current Evaluation from the top of the stack.
262: This is the Evaluation that is in process of evaluating.
263: */
264: public Evaluation getCurrentEvaluation() {
265: return currentEvaluation;
266: }
267:
268: public void setCurrentEvaluation(Evaluation value) {
269: currentEvaluation = value;
270: }
271:
272: /**
273: Gets the root of the evaluation stack.
274: This Evaluation contains the node representing
275: the root expression and the source is the root
276: source object.
277: */
278: public Evaluation getRootEvaluation() {
279: return rootEvaluation;
280: }
281:
282: public void setRootEvaluation(Evaluation value) {
283: rootEvaluation = value;
284: }
285:
286: /**
287: Returns the Evaluation at the relative index given. This should be
288: zero or a negative number as a relative reference back up the evaluation
289: stack. Therefore getEvaluation(0) returns the current Evaluation.
290: */
291: public Evaluation getEvaluation(int relativeIndex) {
292: Evaluation result = null;
293:
294: if (relativeIndex <= 0) {
295: result = currentEvaluation;
296: while ((++relativeIndex < 0) && (result != null)) {
297: result = result.getParent();
298: }
299: }
300: return result;
301: }
302:
303: /**
304: Pushes a new Evaluation onto the stack. This is done
305: before a node evaluates. When evaluation is complete
306: it should be popped from the stack via <code>popEvaluation()</code>.
307: */
308: public void pushEvaluation(Evaluation value) {
309: if (currentEvaluation != null) {
310: currentEvaluation.addChild(value);
311: } else {
312: setRootEvaluation(value);
313: }
314: setCurrentEvaluation(value);
315: }
316:
317: /**
318: Pops the current Evaluation off of the top of the stack.
319: This is done after a node has completed its evaluation.
320: */
321: public Evaluation popEvaluation() {
322: Evaluation result;
323:
324: result = currentEvaluation;
325: setCurrentEvaluation(result.getParent());
326: if (currentEvaluation == null) {
327: setLastEvaluation(getKeepLastEvaluation() ? result : null);
328: setRootEvaluation(null);
329: setCurrentNode(null);
330: }
331: return result;
332: }
333:
334: /*================= Map interface =================*/
335: public int size() {
336: return values.size();
337: }
338:
339: public boolean isEmpty() {
340: return values.isEmpty();
341: }
342:
343: public boolean containsKey(Object key) {
344: return values.containsKey(key);
345: }
346:
347: public boolean containsValue(Object value) {
348: return values.containsValue(value);
349: }
350:
351: public Object get(Object key) {
352: Object result;
353:
354: if (RESERVED_KEYS.containsKey(key)) {
355: if (key.equals(OgnlContext.THIS_CONTEXT_KEY)) {
356: result = getCurrentObject();
357: } else {
358: if (key.equals(OgnlContext.ROOT_CONTEXT_KEY)) {
359: result = getRoot();
360: } else {
361: if (key.equals(OgnlContext.CONTEXT_CONTEXT_KEY)) {
362: result = this ;
363: } else {
364: if (key
365: .equals(OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY)) {
366: result = getTraceEvaluations() ? Boolean.TRUE
367: : Boolean.FALSE;
368: } else {
369: if (key
370: .equals(OgnlContext.LAST_EVALUATION_CONTEXT_KEY)) {
371: result = getLastEvaluation();
372: } else {
373: if (key
374: .equals(OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY)) {
375: result = getKeepLastEvaluation() ? Boolean.TRUE
376: : Boolean.FALSE;
377: } else {
378: if (key
379: .equals(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY)) {
380: result = getClassResolver();
381: } else {
382: if (key
383: .equals(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY)) {
384: result = getTypeConverter();
385: } else {
386: if (key
387: .equals(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY)) {
388: result = getMemberAccess();
389: } else {
390: throw new IllegalArgumentException(
391: "unknown reserved key '"
392: + key
393: + "'");
394: }
395: }
396: }
397: }
398: }
399: }
400: }
401: }
402: }
403: } else {
404: result = values.get(key);
405: }
406: return result;
407: }
408:
409: public Object put(Object key, Object value) {
410: Object result;
411:
412: if (RESERVED_KEYS.containsKey(key)) {
413: if (key.equals(OgnlContext.THIS_CONTEXT_KEY)) {
414: result = getCurrentObject();
415: setCurrentObject(value);
416: } else {
417: if (key.equals(OgnlContext.ROOT_CONTEXT_KEY)) {
418: result = getRoot();
419: setRoot(value);
420: } else {
421: if (key.equals(OgnlContext.CONTEXT_CONTEXT_KEY)) {
422: throw new IllegalArgumentException(
423: "can't change "
424: + OgnlContext.CONTEXT_CONTEXT_KEY
425: + " in context");
426: } else {
427: if (key
428: .equals(OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY)) {
429: result = getTraceEvaluations() ? Boolean.TRUE
430: : Boolean.FALSE;
431: setTraceEvaluations(OgnlOps
432: .booleanValue(value));
433: } else {
434: if (key
435: .equals(OgnlContext.LAST_EVALUATION_CONTEXT_KEY)) {
436: result = getLastEvaluation();
437: lastEvaluation = (Evaluation) value;
438: } else {
439: if (key
440: .equals(OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY)) {
441: result = getKeepLastEvaluation() ? Boolean.TRUE
442: : Boolean.FALSE;
443: setKeepLastEvaluation(OgnlOps
444: .booleanValue(value));
445: } else {
446: if (key
447: .equals(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY)) {
448: result = getClassResolver();
449: setClassResolver((ClassResolver) value);
450: } else {
451: if (key
452: .equals(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY)) {
453: result = getTypeConverter();
454: setTypeConverter((TypeConverter) value);
455: } else {
456: if (key
457: .equals(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY)) {
458: result = getMemberAccess();
459: setMemberAccess((MemberAccess) value);
460: } else {
461: throw new IllegalArgumentException(
462: "unknown reserved key '"
463: + key
464: + "'");
465: }
466: }
467: }
468: }
469: }
470: }
471: }
472: }
473: }
474: } else {
475: result = values.put(key, value);
476: }
477: return result;
478: }
479:
480: public Object remove(Object key) {
481: Object result;
482:
483: if (RESERVED_KEYS.containsKey(key)) {
484: if (key.equals(OgnlContext.THIS_CONTEXT_KEY)) {
485: result = getCurrentObject();
486: setCurrentObject(null);
487: } else {
488: if (key.equals(OgnlContext.ROOT_CONTEXT_KEY)) {
489: result = getRoot();
490: setRoot(null);
491: } else {
492: if (key.equals(OgnlContext.CONTEXT_CONTEXT_KEY)) {
493: throw new IllegalArgumentException(
494: "can't remove "
495: + OgnlContext.CONTEXT_CONTEXT_KEY
496: + " from context");
497: } else {
498: if (key
499: .equals(OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY)) {
500: throw new IllegalArgumentException(
501: "can't remove "
502: + OgnlContext.TRACE_EVALUATIONS_CONTEXT_KEY
503: + " from context");
504: } else {
505: if (key
506: .equals(OgnlContext.LAST_EVALUATION_CONTEXT_KEY)) {
507: result = lastEvaluation;
508: setLastEvaluation(null);
509: } else {
510: if (key
511: .equals(OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY)) {
512: throw new IllegalArgumentException(
513: "can't remove "
514: + OgnlContext.KEEP_LAST_EVALUATION_CONTEXT_KEY
515: + " from context");
516: } else {
517: if (key
518: .equals(OgnlContext.CLASS_RESOLVER_CONTEXT_KEY)) {
519: result = getClassResolver();
520: setClassResolver(null);
521: } else {
522: if (key
523: .equals(OgnlContext.TYPE_CONVERTER_CONTEXT_KEY)) {
524: result = getTypeConverter();
525: setTypeConverter(null);
526: } else {
527: if (key
528: .equals(OgnlContext.MEMBER_ACCESS_CONTEXT_KEY)) {
529: result = getMemberAccess();
530: setMemberAccess(null);
531: } else {
532: throw new IllegalArgumentException(
533: "unknown reserved key '"
534: + key
535: + "'");
536: }
537: }
538: }
539: }
540: }
541: }
542: }
543: }
544: }
545: } else {
546: result = values.remove(key);
547: }
548: return result;
549: }
550:
551: public void putAll(Map t) {
552: for (Iterator it = t.keySet().iterator(); it.hasNext();) {
553: Object k = it.next();
554:
555: put(k, t.get(k));
556: }
557: }
558:
559: public void clear() {
560: values.clear();
561: setRoot(null);
562: setCurrentObject(null);
563: setRootEvaluation(null);
564: setCurrentEvaluation(null);
565: setLastEvaluation(null);
566: setCurrentNode(null);
567: setClassResolver(DEFAULT_CLASS_RESOLVER);
568: setTypeConverter(DEFAULT_TYPE_CONVERTER);
569: setMemberAccess(DEFAULT_MEMBER_ACCESS);
570: }
571:
572: public Set keySet() {
573: /* Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
574: return values.keySet();
575: }
576:
577: public Collection values() {
578: /* Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
579: return values.values();
580: }
581:
582: public Set entrySet() {
583: /* Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
584: return values.entrySet();
585: }
586:
587: public boolean equals(Object o) {
588: return values.equals(o);
589: }
590:
591: public int hashCode() {
592: return values.hashCode();
593: }
594: }
|