001: /*
002: * The contents of this file are subject to the terms
003: * of the Common Development and Distribution License
004: * (the "License"). You may not use this file except
005: * in compliance with the License.
006: *
007: * You can obtain a copy of the license at
008: * glassfish/bootstrap/legal/CDDLv1.0.txt or
009: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
010: * See the License for the specific language governing
011: * permissions and limitations under the License.
012: *
013: * When distributing Covered Code, include this CDDL
014: * HEADER in each file and include the License file at
015: * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
016: * add the following below this CDDL HEADER, with the
017: * fields enclosed by brackets "[]" replaced with your
018: * own identifying information: Portions Copyright [yyyy]
019: * [name of copyright owner]
020: *
021: * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
022: */
023:
024: package javax.el;
025:
026: import java.util.ArrayList;
027: import java.util.Iterator;
028: import java.beans.FeatureDescriptor;
029:
030: /**
031: * Maintains an ordered composite list of child <code>ELResolver</code>s.
032: *
033: * <p>Though only a single <code>ELResolver</code> is associated with an
034: * <code>ELContext</code>, there are usually multiple resolvers considered
035: * for any given variable or property resolution. <code>ELResolver</code>s
036: * are combined together using a <code>CompositeELResolver</code>, to define
037: * rich semantics for evaluating an expression.</p>
038: *
039: * <p>For the {@link #getValue}, {@link #getType}, {@link #setValue} and
040: * {@link #isReadOnly} methods, an <code>ELResolver</code> is not
041: * responsible for resolving all possible (base, property) pairs. In fact,
042: * most resolvers will only handle a <code>base</code> of a single type.
043: * To indicate that a resolver has successfully resolved a particular
044: * (base, property) pair, it must set the <code>propertyResolved</code>
045: * property of the <code>ELContext</code> to <code>true</code>. If it could
046: * not handle the given pair, it must leave this property alone. The caller
047: * must ignore the return value of the method if <code>propertyResolved</code>
048: * is <code>false</code>.</p>
049: *
050: * <p>The <code>CompositeELResolver</code> initializes the
051: * <code>ELContext.propertyResolved</code> flag to <code>false</code>, and uses
052: * it as a stop condition for iterating through its component resolvers.</p>
053: *
054: * <p>The <code>ELContext.propertyResolved</code> flag is not used for the
055: * design-time methods {@link #getFeatureDescriptors} and
056: * {@link #getCommonPropertyType}. Instead, results are collected and
057: * combined from all child <code>ELResolver</code>s for these methods.</p>
058: *
059: * @see ELContext
060: * @see ELResolver
061: * @since JSP 2.1
062: */
063: public class CompositeELResolver extends ELResolver {
064:
065: /**
066: * Adds the given resolver to the list of component resolvers.
067: *
068: * <p>Resolvers are consulted in the order in which they are added.</p>
069: *
070: * @param elResolver The component resolver to add.
071: * @throws NullPointerException If the provided resolver is
072: * <code>null</code>.
073: */
074: public void add(ELResolver elResolver) {
075:
076: if (elResolver == null) {
077: throw new NullPointerException();
078: }
079:
080: elResolvers.add(elResolver);
081: }
082:
083: /**
084: * Attempts to resolve the given <code>property</code> object on the given
085: * <code>base</code> object by querying all component resolvers.
086: *
087: * <p>If this resolver handles the given (base, property) pair,
088: * the <code>propertyResolved</code> property of the
089: * <code>ELContext</code> object must be set to <code>true</code>
090: * by the resolver, before returning. If this property is not
091: * <code>true</code> after this method is called, the caller should ignore
092: * the return value.</p>
093: *
094: * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
095: * the provided <code>ELContext</code>.</p>
096: *
097: * <p>Next, for each component resolver in this composite:
098: * <ol>
099: * <li>The <code>getValue()</code> method is called, passing in
100: * the provided <code>context</code>, <code>base</code> and
101: * <code>property</code>.</li>
102: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
103: * flag is <code>false</code> then iteration continues.</li>
104: * <li>Otherwise, iteration stops and no more component resolvers are
105: * considered. The value returned by <code>getValue()</code> is
106: * returned by this method.</li>
107: * </ol></p>
108: *
109: * <p>If none of the component resolvers were able to perform this
110: * operation, the value <code>null</code> is returned and the
111: * <code>propertyResolved</code> flag remains set to
112: * <code>false</code></p>.
113: *
114: * <p>Any exception thrown by component resolvers during the iteration
115: * is propagated to the caller of this method.</p>
116: *
117: * @param context The context of this evaluation.
118: * @param base The base object whose property value is to be returned,
119: * or <code>null</code> to resolve a top-level variable.
120: * @param property The property or variable to be resolved.
121: * @return If the <code>propertyResolved</code> property of
122: * <code>ELContext</code> was set to <code>true</code>, then
123: * the result of the variable or property resolution; otherwise
124: * undefined.
125: * @throws NullPointerException if context is <code>null</code>
126: * @throws PropertyNotFoundException if the given (base, property) pair
127: * is handled by this <code>ELResolver</code> but the specified
128: * variable or property does not exist or is not readable.
129: * @throws ELException if an exception was thrown while performing
130: * the property or variable resolution. The thrown exception
131: * must be included as the cause property of this exception, if
132: * available.
133: */
134: public Object getValue(ELContext context, Object base,
135: Object property) {
136: context.setPropertyResolved(false);
137: int i = 0, len = this .elResolvers.size();
138: ELResolver elResolver;
139: Object value;
140: while (i < len) {
141: elResolver = this .elResolvers.get(i);
142: value = elResolver.getValue(context, base, property);
143: if (context.isPropertyResolved()) {
144: return value;
145: }
146: i++;
147: }
148: return null;
149: }
150:
151: /**
152: * For a given <code>base</code> and <code>property</code>, attempts to
153: * identify the most general type that is acceptable for an object to be
154: * passed as the <code>value</code> parameter in a future call
155: * to the {@link #setValue} method. The result is obtained by
156: * querying all component resolvers.
157: *
158: * <p>If this resolver handles the given (base, property) pair,
159: * the <code>propertyResolved</code> property of the
160: * <code>ELContext</code> object must be set to <code>true</code>
161: * by the resolver, before returning. If this property is not
162: * <code>true</code> after this method is called, the caller should ignore
163: * the return value.</p>
164: *
165: * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
166: * the provided <code>ELContext</code>.</p>
167: *
168: * <p>Next, for each component resolver in this composite:
169: * <ol>
170: * <li>The <code>getType()</code> method is called, passing in
171: * the provided <code>context</code>, <code>base</code> and
172: * <code>property</code>.</li>
173: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
174: * flag is <code>false</code> then iteration continues.</li>
175: * <li>Otherwise, iteration stops and no more component resolvers are
176: * considered. The value returned by <code>getType()</code> is
177: * returned by this method.</li>
178: * </ol></p>
179: *
180: * <p>If none of the component resolvers were able to perform this
181: * operation, the value <code>null</code> is returned and the
182: * <code>propertyResolved</code> flag remains set to
183: * <code>false</code></p>.
184: *
185: * <p>Any exception thrown by component resolvers during the iteration
186: * is propagated to the caller of this method.</p>
187: *
188: * @param context The context of this evaluation.
189: * @param base The base object whose property value is to be analyzed,
190: * or <code>null</code> to analyze a top-level variable.
191: * @param property The property or variable to return the acceptable
192: * type for.
193: * @return If the <code>propertyResolved</code> property of
194: * <code>ELContext</code> was set to <code>true</code>, then
195: * the most general acceptable type; otherwise undefined.
196: * @throws NullPointerException if context is <code>null</code>
197: * @throws PropertyNotFoundException if the given (base, property) pair
198: * is handled by this <code>ELResolver</code> but the specified
199: * variable or property does not exist or is not readable.
200: * @throws ELException if an exception was thrown while performing
201: * the property or variable resolution. The thrown exception
202: * must be included as the cause property of this exception, if
203: * available.
204: */
205: public Class<?> getType(ELContext context, Object base,
206: Object property) {
207: context.setPropertyResolved(false);
208: int i = 0, len = this .elResolvers.size();
209: ELResolver elResolver;
210: Class<?> type;
211: while (i < len) {
212: elResolver = this .elResolvers.get(i);
213: type = elResolver.getType(context, base, property);
214: if (context.isPropertyResolved()) {
215: return type;
216: }
217: i++;
218: }
219: return null;
220: }
221:
222: /**
223: * Attempts to set the value of the given <code>property</code>
224: * object on the given <code>base</code> object. All component
225: * resolvers are asked to attempt to set the value.
226: *
227: * <p>If this resolver handles the given (base, property) pair,
228: * the <code>propertyResolved</code> property of the
229: * <code>ELContext</code> object must be set to <code>true</code>
230: * by the resolver, before returning. If this property is not
231: * <code>true</code> after this method is called, the caller can
232: * safely assume no value has been set.</p>
233: *
234: * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
235: * the provided <code>ELContext</code>.</p>
236: *
237: * <p>Next, for each component resolver in this composite:
238: * <ol>
239: * <li>The <code>setValue()</code> method is called, passing in
240: * the provided <code>context</code>, <code>base</code>,
241: * <code>property</code> and <code>value</code>.</li>
242: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
243: * flag is <code>false</code> then iteration continues.</li>
244: * <li>Otherwise, iteration stops and no more component resolvers are
245: * considered.</li>
246: * </ol></p>
247: *
248: * <p>If none of the component resolvers were able to perform this
249: * operation, the <code>propertyResolved</code> flag remains set to
250: * <code>false</code></p>.
251: *
252: * <p>Any exception thrown by component resolvers during the iteration
253: * is propagated to the caller of this method.</p>
254: *
255: * @param context The context of this evaluation.
256: * @param base The base object whose property value is to be set,
257: * or <code>null</code> to set a top-level variable.
258: * @param property The property or variable to be set.
259: * @param val The value to set the property or variable to.
260: * @throws NullPointerException if context is <code>null</code>
261: * @throws PropertyNotFoundException if the given (base, property) pair
262: * is handled by this <code>ELResolver</code> but the specified
263: * variable or property does not exist.
264: * @throws PropertyNotWritableException if the given (base, property)
265: * pair is handled by this <code>ELResolver</code> but the specified
266: * variable or property is not writable.
267: * @throws ELException if an exception was thrown while attempting to
268: * set the property or variable. The thrown exception
269: * must be included as the cause property of this exception, if
270: * available.
271: */
272: public void setValue(ELContext context, Object base,
273: Object property, Object val) {
274: context.setPropertyResolved(false);
275: int i = 0, len = this .elResolvers.size();
276: ELResolver elResolver;
277: while (i < len) {
278: elResolver = this .elResolvers.get(i);
279: elResolver.setValue(context, base, property, val);
280: if (context.isPropertyResolved()) {
281: return;
282: }
283: i++;
284: }
285: }
286:
287: /**
288: * For a given <code>base</code> and <code>property</code>, attempts to
289: * determine whether a call to {@link #setValue} will always fail. The
290: * result is obtained by querying all component resolvers.
291: *
292: * <p>If this resolver handles the given (base, property) pair,
293: * the <code>propertyResolved</code> property of the
294: * <code>ELContext</code> object must be set to <code>true</code>
295: * by the resolver, before returning. If this property is not
296: * <code>true</code> after this method is called, the caller should ignore
297: * the return value.</p>
298: *
299: * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
300: * the provided <code>ELContext</code>.</p>
301: *
302: * <p>Next, for each component resolver in this composite:
303: * <ol>
304: * <li>The <code>isReadOnly()</code> method is called, passing in
305: * the provided <code>context</code>, <code>base</code> and
306: * <code>property</code>.</li>
307: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
308: * flag is <code>false</code> then iteration continues.</li>
309: * <li>Otherwise, iteration stops and no more component resolvers are
310: * considered. The value returned by <code>isReadOnly()</code> is
311: * returned by this method.</li>
312: * </ol></p>
313: *
314: * <p>If none of the component resolvers were able to perform this
315: * operation, the value <code>false</code> is returned and the
316: * <code>propertyResolved</code> flag remains set to
317: * <code>false</code></p>.
318: *
319: * <p>Any exception thrown by component resolvers during the iteration
320: * is propagated to the caller of this method.</p>
321: *
322: * @param context The context of this evaluation.
323: * @param base The base object whose property value is to be analyzed,
324: * or <code>null</code> to analyze a top-level variable.
325: * @param property The property or variable to return the read-only status
326: * for.
327: * @return If the <code>propertyResolved</code> property of
328: * <code>ELContext</code> was set to <code>true</code>, then
329: * <code>true</code> if the property is read-only or
330: * <code>false</code> if not; otherwise undefined.
331: * @throws NullPointerException if context is <code>null</code>
332: * @throws PropertyNotFoundException if the given (base, property) pair
333: * is handled by this <code>ELResolver</code> but the specified
334: * variable or property does not exist.
335: * @throws ELException if an exception was thrown while performing
336: * the property or variable resolution. The thrown exception
337: * must be included as the cause property of this exception, if
338: * available.
339: */
340: public boolean isReadOnly(ELContext context, Object base,
341: Object property) {
342: context.setPropertyResolved(false);
343: int i = 0, len = this .elResolvers.size();
344: ELResolver elResolver;
345: boolean readOnly;
346: while (i < len) {
347: elResolver = this .elResolvers.get(i);
348: readOnly = elResolver.isReadOnly(context, base, property);
349: if (context.isPropertyResolved()) {
350: return readOnly;
351: }
352: i++;
353: }
354: return false; // Does not matter
355: }
356:
357: /**
358: * Returns information about the set of variables or properties that
359: * can be resolved for the given <code>base</code> object. One use for
360: * this method is to assist tools in auto-completion. The results are
361: * collected from all component resolvers.
362: *
363: * <p>The <code>propertyResolved</code> property of the
364: * <code>ELContext</code> is not relevant to this method.
365: * The results of all <code>ELResolver</code>s are concatenated.</p>
366: *
367: * <p>The <code>Iterator</code> returned is an iterator over the
368: * collection of <code>FeatureDescriptor</code> objects returned by
369: * the iterators returned by each component resolver's
370: * <code>getFeatureDescriptors</code> method. If <code>null</code> is
371: * returned by a resolver, it is skipped.</p>
372: *
373: * @param context The context of this evaluation.
374: * @param base The base object whose set of valid properties is to
375: * be enumerated, or <code>null</code> to enumerate the set of
376: * top-level variables that this resolver can evaluate.
377: * @return An <code>Iterator</code> containing zero or more (possibly
378: * infinitely more) <code>FeatureDescriptor</code> objects, or
379: * <code>null</code> if this resolver does not handle the given
380: * <code>base</code> object or that the results are too complex to
381: * represent with this method
382: */
383: public Iterator<FeatureDescriptor> getFeatureDescriptors(
384: ELContext context, Object base) {
385: return new CompositeIterator(elResolvers.iterator(), context,
386: base);
387: }
388:
389: /**
390: * Returns the most general type that this resolver accepts for the
391: * <code>property</code> argument, given a <code>base</code> object.
392: * One use for this method is to assist tools in auto-completion. The
393: * result is obtained by querying all component resolvers.
394: *
395: * <p>The <code>Class</code> returned is the most specific class that is
396: * a common superclass of all the classes returned by each component
397: * resolver's <code>getCommonPropertyType</code> method. If
398: * <code>null</code> is returned by a resolver, it is skipped.</p>
399: *
400: * @param context The context of this evaluation.
401: * @param base The base object to return the most general property
402: * type for, or <code>null</code> to enumerate the set of
403: * top-level variables that this resolver can evaluate.
404: * @return <code>null</code> if this <code>ELResolver</code> does not
405: * know how to handle the given <code>base</code> object; otherwise
406: * <code>Object.class</code> if any type of <code>property</code>
407: * is accepted; otherwise the most general <code>property</code>
408: * type accepted for the given <code>base</code>.
409: */
410: public Class<?> getCommonPropertyType(ELContext context, Object base) {
411: Class<?> commonPropertyType = null;
412: Iterator<ELResolver> iter = elResolvers.iterator();
413: while (iter.hasNext()) {
414: ELResolver elResolver = iter.next();
415: Class<?> type = elResolver.getCommonPropertyType(context,
416: base);
417: if (type == null) {
418: // skip this EL Resolver
419: continue;
420: } else if (commonPropertyType == null) {
421: commonPropertyType = type;
422: } else if (commonPropertyType.isAssignableFrom(type)) {
423: continue;
424: } else if (type.isAssignableFrom(commonPropertyType)) {
425: commonPropertyType = type;
426: } else {
427: // Don't have a commonPropertyType
428: return null;
429: }
430: }
431: return commonPropertyType;
432: }
433:
434: private final ArrayList<ELResolver> elResolvers = new ArrayList<ELResolver>();
435:
436: private static class CompositeIterator implements
437: Iterator<FeatureDescriptor> {
438:
439: Iterator<ELResolver> compositeIter;
440: Iterator<FeatureDescriptor> propertyIter;
441: ELContext context;
442: Object base;
443:
444: CompositeIterator(Iterator<ELResolver> iter, ELContext context,
445: Object base) {
446: compositeIter = iter;
447: this .context = context;
448: this .base = base;
449: }
450:
451: public boolean hasNext() {
452: if (propertyIter == null || !propertyIter.hasNext()) {
453: while (compositeIter.hasNext()) {
454: ELResolver elResolver = compositeIter.next();
455: propertyIter = elResolver.getFeatureDescriptors(
456: context, base);
457: if (propertyIter != null) {
458: return propertyIter.hasNext();
459: }
460: }
461: return false;
462: }
463: return propertyIter.hasNext();
464: }
465:
466: public FeatureDescriptor next() {
467: if (propertyIter == null || !propertyIter.hasNext()) {
468: while (compositeIter.hasNext()) {
469: ELResolver elResolver = compositeIter.next();
470: propertyIter = elResolver.getFeatureDescriptors(
471: context, base);
472: if (propertyIter != null) {
473: return propertyIter.next();
474: }
475: }
476: return null;
477: }
478: return propertyIter.next();
479: }
480:
481: public void remove() {
482: throw new UnsupportedOperationException();
483: }
484: }
485: }
|