1 /*
2 * The contents of this file are subject to the terms
3 * of the Common Development and Distribution License
4 * (the "License"). You may not use this file except
5 * in compliance with the License.
6 *
7 * You can obtain a copy of the license at
8 * glassfish/bootstrap/legal/CDDLv1.0.txt or
9 * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10 * See the License for the specific language governing
11 * permissions and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL
14 * HEADER in each file and include the License file at
15 * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16 * add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your
18 * own identifying information: Portions Copyright [yyyy]
19 * [name of copyright owner]
20 *
21 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
22 */
23
24 package javax.el;
25
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.beans.FeatureDescriptor;
29
30 /**
31 * Maintains an ordered composite list of child <code>ELResolver</code>s.
32 *
33 * <p>Though only a single <code>ELResolver</code> is associated with an
34 * <code>ELContext</code>, there are usually multiple resolvers considered
35 * for any given variable or property resolution. <code>ELResolver</code>s
36 * are combined together using a <code>CompositeELResolver</code>, to define
37 * rich semantics for evaluating an expression.</p>
38 *
39 * <p>For the {@link #getValue}, {@link #getType}, {@link #setValue} and
40 * {@link #isReadOnly} methods, an <code>ELResolver</code> is not
41 * responsible for resolving all possible (base, property) pairs. In fact,
42 * most resolvers will only handle a <code>base</code> of a single type.
43 * To indicate that a resolver has successfully resolved a particular
44 * (base, property) pair, it must set the <code>propertyResolved</code>
45 * property of the <code>ELContext</code> to <code>true</code>. If it could
46 * not handle the given pair, it must leave this property alone. The caller
47 * must ignore the return value of the method if <code>propertyResolved</code>
48 * is <code>false</code>.</p>
49 *
50 * <p>The <code>CompositeELResolver</code> initializes the
51 * <code>ELContext.propertyResolved</code> flag to <code>false</code>, and uses
52 * it as a stop condition for iterating through its component resolvers.</p>
53 *
54 * <p>The <code>ELContext.propertyResolved</code> flag is not used for the
55 * design-time methods {@link #getFeatureDescriptors} and
56 * {@link #getCommonPropertyType}. Instead, results are collected and
57 * combined from all child <code>ELResolver</code>s for these methods.</p>
58 *
59 * @see ELContext
60 * @see ELResolver
61 * @since JSP 2.1
62 */
63 public class CompositeELResolver extends ELResolver {
64
65 /**
66 * Adds the given resolver to the list of component resolvers.
67 *
68 * <p>Resolvers are consulted in the order in which they are added.</p>
69 *
70 * @param elResolver The component resolver to add.
71 * @throws NullPointerException If the provided resolver is
72 * <code>null</code>.
73 */
74 public void add(ELResolver elResolver) {
75
76 if (elResolver == null) {
77 throw new NullPointerException();
78 }
79
80 elResolvers.add(elResolver);
81 }
82
83 /**
84 * Attempts to resolve the given <code>property</code> object on the given
85 * <code>base</code> object by querying all component resolvers.
86 *
87 * <p>If this resolver handles the given (base, property) pair,
88 * the <code>propertyResolved</code> property of the
89 * <code>ELContext</code> object must be set to <code>true</code>
90 * by the resolver, before returning. If this property is not
91 * <code>true</code> after this method is called, the caller should ignore
92 * the return value.</p>
93 *
94 * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
95 * the provided <code>ELContext</code>.</p>
96 *
97 * <p>Next, for each component resolver in this composite:
98 * <ol>
99 * <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,
135 Object base,
136 Object property) {
137 context.setPropertyResolved(false);
138 int i = 0, len = this.elResolvers.size();
139 ELResolver elResolver;
140 Object value;
141 while (i < len) {
142 elResolver = this.elResolvers.get(i);
143 value = elResolver.getValue(context, base, property);
144 if (context.isPropertyResolved()) {
145 return value;
146 }
147 i++;
148 }
149 return null;
150 }
151
152 /**
153 * For a given <code>base</code> and <code>property</code>, attempts to
154 * identify the most general type that is acceptable for an object to be
155 * passed as the <code>value</code> parameter in a future call
156 * to the {@link #setValue} method. The result is obtained by
157 * querying all component resolvers.
158 *
159 * <p>If this resolver handles the given (base, property) pair,
160 * the <code>propertyResolved</code> property of the
161 * <code>ELContext</code> object must be set to <code>true</code>
162 * by the resolver, before returning. If this property is not
163 * <code>true</code> after this method is called, the caller should ignore
164 * the return value.</p>
165 *
166 * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
167 * the provided <code>ELContext</code>.</p>
168 *
169 * <p>Next, for each component resolver in this composite:
170 * <ol>
171 * <li>The <code>getType()</code> method is called, passing in
172 * the provided <code>context</code>, <code>base</code> and
173 * <code>property</code>.</li>
174 * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
175 * flag is <code>false</code> then iteration continues.</li>
176 * <li>Otherwise, iteration stops and no more component resolvers are
177 * considered. The value returned by <code>getType()</code> is
178 * returned by this method.</li>
179 * </ol></p>
180 *
181 * <p>If none of the component resolvers were able to perform this
182 * operation, the value <code>null</code> is returned and the
183 * <code>propertyResolved</code> flag remains set to
184 * <code>false</code></p>.
185 *
186 * <p>Any exception thrown by component resolvers during the iteration
187 * is propagated to the caller of this method.</p>
188 *
189 * @param context The context of this evaluation.
190 * @param base The base object whose property value is to be analyzed,
191 * or <code>null</code> to analyze a top-level variable.
192 * @param property The property or variable to return the acceptable
193 * type for.
194 * @return If the <code>propertyResolved</code> property of
195 * <code>ELContext</code> was set to <code>true</code>, then
196 * the most general acceptable type; otherwise undefined.
197 * @throws NullPointerException if context is <code>null</code>
198 * @throws PropertyNotFoundException if the given (base, property) pair
199 * is handled by this <code>ELResolver</code> but the specified
200 * variable or property does not exist or is not readable.
201 * @throws ELException if an exception was thrown while performing
202 * the property or variable resolution. The thrown exception
203 * must be included as the cause property of this exception, if
204 * available.
205 */
206 public Class<?> getType(ELContext context,
207 Object base,
208 Object property) {
209 context.setPropertyResolved(false);
210 int i = 0, len = this.elResolvers.size();
211 ELResolver elResolver;
212 Class<?> type;
213 while (i < len) {
214 elResolver = this.elResolvers.get(i);
215 type = elResolver.getType(context, base, property);
216 if (context.isPropertyResolved()) {
217 return type;
218 }
219 i++;
220 }
221 return null;
222 }
223
224 /**
225 * Attempts to set the value of the given <code>property</code>
226 * object on the given <code>base</code> object. All component
227 * resolvers are asked to attempt to set the value.
228 *
229 * <p>If this resolver handles the given (base, property) pair,
230 * the <code>propertyResolved</code> property of the
231 * <code>ELContext</code> object must be set to <code>true</code>
232 * by the resolver, before returning. If this property is not
233 * <code>true</code> after this method is called, the caller can
234 * safely assume no value has been set.</p>
235 *
236 * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
237 * the provided <code>ELContext</code>.</p>
238 *
239 * <p>Next, for each component resolver in this composite:
240 * <ol>
241 * <li>The <code>setValue()</code> method is called, passing in
242 * the provided <code>context</code>, <code>base</code>,
243 * <code>property</code> and <code>value</code>.</li>
244 * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
245 * flag is <code>false</code> then iteration continues.</li>
246 * <li>Otherwise, iteration stops and no more component resolvers are
247 * considered.</li>
248 * </ol></p>
249 *
250 * <p>If none of the component resolvers were able to perform this
251 * operation, the <code>propertyResolved</code> flag remains set to
252 * <code>false</code></p>.
253 *
254 * <p>Any exception thrown by component resolvers during the iteration
255 * is propagated to the caller of this method.</p>
256 *
257 * @param context The context of this evaluation.
258 * @param base The base object whose property value is to be set,
259 * or <code>null</code> to set a top-level variable.
260 * @param property The property or variable to be set.
261 * @param val The value to set the property or variable to.
262 * @throws NullPointerException if context is <code>null</code>
263 * @throws PropertyNotFoundException if the given (base, property) pair
264 * is handled by this <code>ELResolver</code> but the specified
265 * variable or property does not exist.
266 * @throws PropertyNotWritableException if the given (base, property)
267 * pair is handled by this <code>ELResolver</code> but the specified
268 * variable or property is not writable.
269 * @throws ELException if an exception was thrown while attempting to
270 * set the property or variable. The thrown exception
271 * must be included as the cause property of this exception, if
272 * available.
273 */
274 public void setValue(ELContext context,
275 Object base,
276 Object property,
277 Object val) {
278 context.setPropertyResolved(false);
279 int i = 0, len = this.elResolvers.size();
280 ELResolver elResolver;
281 while (i < len) {
282 elResolver = this.elResolvers.get(i);
283 elResolver.setValue(context, base, property, val);
284 if (context.isPropertyResolved()) {
285 return;
286 }
287 i++;
288 }
289 }
290
291 /**
292 * For a given <code>base</code> and <code>property</code>, attempts to
293 * determine whether a call to {@link #setValue} will always fail. The
294 * result is obtained by querying all component resolvers.
295 *
296 * <p>If this resolver handles the given (base, property) pair,
297 * the <code>propertyResolved</code> property of the
298 * <code>ELContext</code> object must be set to <code>true</code>
299 * by the resolver, before returning. If this property is not
300 * <code>true</code> after this method is called, the caller should ignore
301 * the return value.</p>
302 *
303 * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
304 * the provided <code>ELContext</code>.</p>
305 *
306 * <p>Next, for each component resolver in this composite:
307 * <ol>
308 * <li>The <code>isReadOnly()</code> method is called, passing in
309 * the provided <code>context</code>, <code>base</code> and
310 * <code>property</code>.</li>
311 * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
312 * flag is <code>false</code> then iteration continues.</li>
313 * <li>Otherwise, iteration stops and no more component resolvers are
314 * considered. The value returned by <code>isReadOnly()</code> is
315 * returned by this method.</li>
316 * </ol></p>
317 *
318 * <p>If none of the component resolvers were able to perform this
319 * operation, the value <code>false</code> is returned and the
320 * <code>propertyResolved</code> flag remains set to
321 * <code>false</code></p>.
322 *
323 * <p>Any exception thrown by component resolvers during the iteration
324 * is propagated to the caller of this method.</p>
325 *
326 * @param context The context of this evaluation.
327 * @param base The base object whose property value is to be analyzed,
328 * or <code>null</code> to analyze a top-level variable.
329 * @param property The property or variable to return the read-only status
330 * for.
331 * @return If the <code>propertyResolved</code> property of
332 * <code>ELContext</code> was set to <code>true</code>, then
333 * <code>true</code> if the property is read-only or
334 * <code>false</code> if not; otherwise undefined.
335 * @throws NullPointerException if context is <code>null</code>
336 * @throws PropertyNotFoundException if the given (base, property) pair
337 * is handled by this <code>ELResolver</code> but the specified
338 * variable or property does not exist.
339 * @throws ELException if an exception was thrown while performing
340 * the property or variable resolution. The thrown exception
341 * must be included as the cause property of this exception, if
342 * available.
343 */
344 public boolean isReadOnly(ELContext context,
345 Object base,
346 Object property) {
347 context.setPropertyResolved(false);
348 int i = 0, len = this.elResolvers.size();
349 ELResolver elResolver;
350 boolean readOnly;
351 while (i < len) {
352 elResolver = this.elResolvers.get(i);
353 readOnly = elResolver.isReadOnly(context, base, property);
354 if (context.isPropertyResolved()) {
355 return readOnly;
356 }
357 i++;
358 }
359 return false; // Does not matter
360 }
361
362 /**
363 * Returns information about the set of variables or properties that
364 * can be resolved for the given <code>base</code> object. One use for
365 * this method is to assist tools in auto-completion. The results are
366 * collected from all component resolvers.
367 *
368 * <p>The <code>propertyResolved</code> property of the
369 * <code>ELContext</code> is not relevant to this method.
370 * The results of all <code>ELResolver</code>s are concatenated.</p>
371 *
372 * <p>The <code>Iterator</code> returned is an iterator over the
373 * collection of <code>FeatureDescriptor</code> objects returned by
374 * the iterators returned by each component resolver's
375 * <code>getFeatureDescriptors</code> method. If <code>null</code> is
376 * returned by a resolver, it is skipped.</p>
377 *
378 * @param context The context of this evaluation.
379 * @param base The base object whose set of valid properties is to
380 * be enumerated, or <code>null</code> to enumerate the set of
381 * top-level variables that this resolver can evaluate.
382 * @return An <code>Iterator</code> containing zero or more (possibly
383 * infinitely more) <code>FeatureDescriptor</code> objects, or
384 * <code>null</code> if this resolver does not handle the given
385 * <code>base</code> object or that the results are too complex to
386 * represent with this method
387 */
388 public Iterator<FeatureDescriptor> getFeatureDescriptors(
389 ELContext context,
390 Object base) {
391 return new CompositeIterator(elResolvers.iterator(), context, base);
392 }
393
394 /**
395 * Returns the most general type that this resolver accepts for the
396 * <code>property</code> argument, given a <code>base</code> object.
397 * One use for this method is to assist tools in auto-completion. The
398 * result is obtained by querying all component resolvers.
399 *
400 * <p>The <code>Class</code> returned is the most specific class that is
401 * a common superclass of all the classes returned by each component
402 * resolver's <code>getCommonPropertyType</code> method. If
403 * <code>null</code> is returned by a resolver, it is skipped.</p>
404 *
405 * @param context The context of this evaluation.
406 * @param base The base object to return the most general property
407 * type for, or <code>null</code> to enumerate the set of
408 * top-level variables that this resolver can evaluate.
409 * @return <code>null</code> if this <code>ELResolver</code> does not
410 * know how to handle the given <code>base</code> object; otherwise
411 * <code>Object.class</code> if any type of <code>property</code>
412 * is accepted; otherwise the most general <code>property</code>
413 * type accepted for the given <code>base</code>.
414 */
415 public Class<?> getCommonPropertyType(ELContext context,
416 Object base) {
417 Class<?> commonPropertyType = null;
418 Iterator<ELResolver> iter = elResolvers.iterator();
419 while (iter.hasNext()) {
420 ELResolver elResolver = iter.next();
421 Class<?> type = elResolver.getCommonPropertyType(context, base);
422 if (type == null) {
423 // skip this EL Resolver
424 continue;
425 } else if (commonPropertyType == null) {
426 commonPropertyType = type;
427 } else if (commonPropertyType.isAssignableFrom(type)) {
428 continue;
429 } else if (type.isAssignableFrom(commonPropertyType)) {
430 commonPropertyType = type;
431 } else {
432 // Don't have a commonPropertyType
433 return null;
434 }
435 }
436 return commonPropertyType;
437 }
438
439 private final ArrayList<ELResolver> elResolvers =
440 new ArrayList<ELResolver>();
441
442 private static class CompositeIterator
443 implements Iterator<FeatureDescriptor> {
444
445 Iterator<ELResolver> compositeIter;
446 Iterator<FeatureDescriptor> propertyIter;
447 ELContext context;
448 Object base;
449
450 CompositeIterator(Iterator<ELResolver> iter,
451 ELContext context,
452 Object base) {
453 compositeIter = iter;
454 this.context = context;
455 this.base = base;
456 }
457
458 public boolean hasNext() {
459 if (propertyIter == null || !propertyIter.hasNext()) {
460 while (compositeIter.hasNext()) {
461 ELResolver elResolver = compositeIter.next();
462 propertyIter = elResolver.getFeatureDescriptors(
463 context, base);
464 if (propertyIter != null) {
465 return propertyIter.hasNext();
466 }
467 }
468 return false;
469 }
470 return propertyIter.hasNext();
471 }
472
473 public FeatureDescriptor next() {
474 if (propertyIter == null || !propertyIter.hasNext()) {
475 while (compositeIter.hasNext()) {
476 ELResolver elResolver = compositeIter.next();
477 propertyIter = elResolver.getFeatureDescriptors(
478 context, base);
479 if (propertyIter != null) {
480 return propertyIter.next();
481 }
482 }
483 return null;
484 }
485 return propertyIter.next();
486 }
487
488 public void remove() {
489 throw new UnsupportedOperationException();
490 }
491 }
492 }
493