Skip to contentMethod: add(ELResolver)
1: /*
2: * Copyright (c) 1997, 2021 Oracle and/or its affiliates and others.
3: * All rights reserved.
4: * Copyright 2004 The Apache Software Foundation
5: *
6: * Licensed under the Apache License, Version 2.0 (the "License");
7: * you may not use this file except in compliance with the License.
8: * You may obtain a copy of the License at
9: *
10: * http://www.apache.org/licenses/LICENSE-2.0
11: *
12: * Unless required by applicable law or agreed to in writing, software
13: * distributed under the License is distributed on an "AS IS" BASIS,
14: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15: * See the License for the specific language governing permissions and
16: * limitations under the License.
17: */
18:
19: package jakarta.el;
20:
21: import java.beans.FeatureDescriptor;
22: import java.util.Iterator;
23:
24: /**
25: * Maintains an ordered composite list of child <code>ELResolver</code>s.
26: *
27: * <p>
28: * Though only a single <code>ELResolver</code> is associated with an <code>ELContext</code>, there are usually multiple
29: * resolvers considered for any given variable or property resolution. <code>ELResolver</code>s are combined together
30: * using a <code>CompositeELResolver</code>, to define rich semantics for evaluating an expression.
31: * </p>
32: *
33: * <p>
34: * For the {@link #getValue}, {@link #getType}, {@link #setValue} and {@link #isReadOnly} methods, an
35: * <code>ELResolver</code> is not responsible for resolving all possible (base, property) pairs. In fact, most resolvers
36: * will only handle a <code>base</code> of a single type. To indicate that a resolver has successfully resolved a
37: * particular (base, property) pair, it must set the <code>propertyResolved</code> property of the
38: * <code>ELContext</code> to <code>true</code>. If it could not handle the given pair, it must leave this property
39: * alone. The caller must ignore the return value of the method if <code>propertyResolved</code> is <code>false</code>.
40: * </p>
41: *
42: * <p>
43: * The <code>CompositeELResolver</code> initializes the <code>ELContext.propertyResolved</code> flag to
44: * <code>false</code>, and uses it as a stop condition for iterating through its component resolvers.
45: * </p>
46: *
47: * <p>
48: * The <code>ELContext.propertyResolved</code> flag is not used for the design-time methods
49: * {@link #getFeatureDescriptors} and {@link #getCommonPropertyType}. Instead, results are collected and combined from
50: * all child <code>ELResolver</code>s for these methods.
51: * </p>
52: *
53: * @see ELContext
54: * @see ELResolver
55: * @since Jakarta Server Pages 2.1
56: */
57: public class CompositeELResolver extends ELResolver {
58:
59: public CompositeELResolver() {
60: this.size = 0;
61: this.elResolvers = new ELResolver[16];
62: }
63:
64: /**
65: * Adds the given resolver to the list of component resolvers.
66: *
67: * <p>
68: * Resolvers are consulted in the order in which they are added.
69: * </p>
70: *
71: * @param elResolver The component resolver to add.
72: * @throws NullPointerException If the provided resolver is <code>null</code>.
73: */
74: public void add(ELResolver elResolver) {
75:• if (elResolver == null) {
76: throw new NullPointerException();
77: }
78:
79:• if (size >= elResolvers.length) {
80: ELResolver[] newResolvers = new ELResolver[size * 2];
81: System.arraycopy(elResolvers, 0, newResolvers, 0, size);
82: elResolvers = newResolvers;
83: }
84:
85: elResolvers[size++] = elResolver;
86: }
87:
88: /**
89: * Attempts to resolve the given <code>property</code> object on the given <code>base</code> object by querying all
90: * component resolvers.
91: *
92: * <p>
93: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
94: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
95: * not <code>true</code> after this method is called, the caller should ignore the return value.
96: *
97: * <p>
98: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
99: *
100: * <p>
101: * Next, for each component resolver in this composite:
102: * <ol>
103: * <li>The <code>getValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and
104: * <code>property</code>.</li>
105: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
106: * continues.</li>
107: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
108: * <code>getValue()</code> is returned by this method.</li>
109: * </ol>
110: *
111: * <p>
112: * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
113: * the <code>propertyResolved</code> flag remains set to <code>false</code>.
114: *
115: * <p>
116: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
117: *
118: * @param context The context of this evaluation.
119: * @param base The base object whose property value is to be returned, or <code>null</code> to resolve a top-level
120: * variable.
121: * @param property The property or variable to be resolved.
122: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
123: * the result of the variable or property resolution; otherwise undefined.
124: * @throws NullPointerException if context is <code>null</code>
125: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
126: * the specified variable or property does not exist or is not readable.
127: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
128: * exception must be included as the cause property of this exception, if available.
129: */
130: @Override
131: public Object getValue(ELContext context, Object base, Object property) {
132: context.setPropertyResolved(false);
133:
134: Object value = null;
135: for (int i = 0; i < size; i++) {
136: value = elResolvers[i].getValue(context, base, property);
137: if (context.isPropertyResolved()) {
138: return value;
139: }
140: }
141:
142: return null;
143: }
144:
145: /**
146: * Attempts to resolve and invoke the given <code>method</code> on the given <code>base</code> object by querying all
147: * component resolvers.
148: *
149: * <p>
150: * If this resolver handles the given (base, method) pair, the <code>propertyResolved</code> property of the
151: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
152: * not <code>true</code> after this method is called, the caller should ignore the return value.
153: * </p>
154: *
155: * <p>
156: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
157: * </p>
158: *
159: * <p>
160: * Next, for each component resolver in this composite:
161: * <ol>
162: * <li>The <code>invoke()</code> method is called, passing in the provided <code>context</code>, <code>base</code>,
163: * <code>method</code>, <code>paramTypes</code>, and <code>params</code>.</li>
164: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
165: * continues.</li>
166: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
167: * <code>getValue()</code> is returned by this method.</li>
168: * </ol>
169: *
170: * <p>
171: * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
172: * the <code>propertyResolved</code> flag remains set to <code>false</code>
173: * </p>
174: * .
175: *
176: * <p>
177: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
178: * </p>
179: *
180: * @param context The context of this evaluation.
181: * @param base The bean on which to invoke the method
182: * @param method The simple name of the method to invoke. Will be coerced to a <code>String</code>.
183: * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use
184: * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter
185: * types are assumed to be unknown.
186: * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
187: *
188: * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
189: *
190: * @since Jakarta Expression Language 2.2
191: */
192: @Override
193: public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) {
194: context.setPropertyResolved(false);
195:
196: Object value;
197: for (int i = 0; i < size; i++) {
198: value = elResolvers[i].invoke(context, base, method, paramTypes, params);
199: if (context.isPropertyResolved()) {
200: return value;
201: }
202: }
203:
204: return null;
205: }
206:
207: /**
208: * For a given <code>base</code> and <code>property</code>, attempts to identify the most general type that is
209: * acceptable for an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue}
210: * method. The result is obtained by querying all component resolvers.
211: *
212: * <p>
213: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
214: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
215: * not <code>true</code> after this method is called, the caller should ignore the return value.
216: * </p>
217: *
218: * <p>
219: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
220: * </p>
221: *
222: * <p>
223: * Next, for each component resolver in this composite:
224: * <ol>
225: * <li>The <code>getType()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and
226: * <code>property</code>.</li>
227: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
228: * continues.</li>
229: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
230: * <code>getType()</code> is returned by this method.</li>
231: * </ol>
232: *
233: * <p>
234: * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
235: * the <code>propertyResolved</code> flag remains set to <code>false</code>.
236: * </p>
237: *
238: *
239: * <p>
240: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
241: * </p>
242: *
243: * @param context The context of this evaluation.
244: * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
245: * variable.
246: * @param property The property or variable to return the acceptable type for.
247: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
248: * the most general acceptable type which must be {@code null} if the either the property or the resolver is
249: * read-only; otherwise undefined
250: * @throws NullPointerException if context is <code>null</code>
251: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
252: * the specified variable or property does not exist or is not readable.
253: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
254: * exception must be included as the cause property of this exception, if available.
255: */
256: @Override
257: public Class<?> getType(ELContext context, Object base, Object property) {
258: context.setPropertyResolved(false);
259:
260: Class<?> type;
261: for (int i = 0; i < size; i++) {
262: type = elResolvers[i].getType(context, base, property);
263: if (context.isPropertyResolved()) {
264: return type;
265: }
266: }
267:
268: return null;
269: }
270:
271: /**
272: * Attempts to set the value of the given <code>property</code> object on the given <code>base</code> object. All
273: * component resolvers are asked to attempt to set the value.
274: *
275: * <p>
276: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
277: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
278: * not <code>true</code> after this method is called, the caller can safely assume no value has been set.
279: *
280: * <p>
281: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
282: *
283: * <p>
284: * Next, for each component resolver in this composite:
285: * <ol>
286: * <li>The <code>setValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code>,
287: * <code>property</code> and <code>value</code>.</li>
288: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
289: * continues.</li>
290: * <li>Otherwise, iteration stops and no more component resolvers are considered.</li>
291: * </ol>
292: *
293: * <p>
294: * If none of the component resolvers were able to perform this operation, the <code>propertyResolved</code> flag
295: * remains set to <code>false</code>.
296: *
297: * <p>
298: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
299: *
300: * @param context The context of this evaluation.
301: * @param base The base object whose property value is to be set, or <code>null</code> to set a top-level variable.
302: * @param property The property or variable to be set.
303: * @param val The value to set the property or variable to.
304: * @throws NullPointerException if context is <code>null</code>
305: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
306: * the specified variable or property does not exist.
307: * @throws PropertyNotWritableException if the given (base, property) pair is handled by this <code>ELResolver</code>
308: * but the specified variable or property is not writable.
309: * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
310: * must be included as the cause property of this exception, if available.
311: */
312: @Override
313: public void setValue(ELContext context, Object base, Object property, Object val) {
314: context.setPropertyResolved(false);
315:
316: for (int i = 0; i < size; i++) {
317: elResolvers[i].setValue(context, base, property, val);
318: if (context.isPropertyResolved()) {
319: return;
320: }
321: }
322: }
323:
324: /**
325: * For a given <code>base</code> and <code>property</code>, attempts to determine whether a call to {@link #setValue}
326: * will always fail. The result is obtained by querying all component resolvers.
327: *
328: * <p>
329: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
330: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
331: * not <code>true</code> after this method is called, the caller should ignore the return value.
332: * </p>
333: *
334: * <p>
335: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
336: * </p>
337: *
338: * <p>
339: * Next, for each component resolver in this composite:
340: * <ol>
341: * <li>The <code>isReadOnly()</code> method is called, passing in the provided <code>context</code>, <code>base</code>
342: * and <code>property</code>.</li>
343: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
344: * continues.</li>
345: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
346: * <code>isReadOnly()</code> is returned by this method.</li>
347: * </ol>
348: *
349: * <p>
350: * If none of the component resolvers were able to perform this operation, the value <code>false</code> is returned and
351: * the <code>propertyResolved</code> flag remains set to <code>false</code>
352: * </p>
353: * .
354: *
355: * <p>
356: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
357: * </p>
358: *
359: * @param context The context of this evaluation.
360: * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
361: * variable.
362: * @param property The property or variable to return the read-only status for.
363: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
364: * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined.
365: * @throws NullPointerException if context is <code>null</code>
366: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
367: * the specified variable or property does not exist.
368: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
369: * exception must be included as the cause property of this exception, if available.
370: */
371: @Override
372: public boolean isReadOnly(ELContext context, Object base, Object property) {
373: context.setPropertyResolved(false);
374:
375: boolean readOnly;
376: for (int i = 0; i < size; i++) {
377: readOnly = elResolvers[i].isReadOnly(context, base, property);
378: if (context.isPropertyResolved()) {
379: return readOnly;
380: }
381: }
382:
383: return false; // Does not matter
384: }
385:
386: /**
387: * Returns information about the set of variables or properties that can be resolved for the given <code>base</code>
388: * object. One use for this method is to assist tools in auto-completion. The results are collected from all component
389: * resolvers.
390: *
391: * <p>
392: * The <code>propertyResolved</code> property of the <code>ELContext</code> is not relevant to this method. The results
393: * of all <code>ELResolver</code>s are concatenated.
394: * </p>
395: *
396: * <p>
397: * The <code>Iterator</code> returned is an iterator over the collection of <code>FeatureDescriptor</code> objects
398: * returned by the iterators returned by each component resolver's <code>getFeatureDescriptors</code> method. If
399: * <code>null</code> is returned by a resolver, it is skipped.
400: * </p>
401: *
402: * @param context The context of this evaluation.
403: * @param base The base object whose set of valid properties is to be enumerated, or <code>null</code> to enumerate the
404: * set of top-level variables that this resolver can evaluate.
405: * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code>
406: * objects, or <code>null</code> if this resolver does not handle the given <code>base</code> object or that the results
407: * are too complex to represent with this method
408: *
409: * @deprecated This method will be removed with replacement in EL 6.0
410: */
411: @Deprecated(forRemoval = true, since = "5.0")
412: @Override
413: public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
414: return new CompositeIterator(elResolvers, size, context, base);
415: }
416:
417: /**
418: * Returns the most general type that this resolver accepts for the <code>property</code> argument, given a
419: * <code>base</code> object. One use for this method is to assist tools in auto-completion. The result is obtained by
420: * querying all component resolvers.
421: *
422: * <p>
423: * The <code>Class</code> returned is the most specific class that is a common superclass of all the classes returned by
424: * each component resolver's <code>getCommonPropertyType</code> method. If <code>null</code> is returned by a resolver,
425: * it is skipped.
426: *
427: * @param context The context of this evaluation.
428: * @param base The base object to return the most general property type for, or <code>null</code> to enumerate the set
429: * of top-level variables that this resolver can evaluate.
430: *
431: * @return <code>null</code> if this <code>ELResolver</code> does not know how to handle the given <code>base</code>
432: * object; otherwise <code>Object.class</code> if any type of <code>property</code> is accepted; otherwise the most
433: * general <code>property</code> type accepted for the given <code>base</code>.
434: */
435: @Override
436: public Class<?> getCommonPropertyType(ELContext context, Object base) {
437: Class<?> commonPropertyType = null;
438: for (int i = 0; i < size; i++) {
439:
440: Class<?> type = elResolvers[i].getCommonPropertyType(context, base);
441: if (type == null) {
442: // skip this Jakarta Expression Language Resolver
443: continue;
444: } else if (commonPropertyType == null) {
445: commonPropertyType = type;
446: } else if (commonPropertyType.isAssignableFrom(type)) {
447: continue;
448: } else if (type.isAssignableFrom(commonPropertyType)) {
449: commonPropertyType = type;
450: } else {
451: // Don't have a commonPropertyType
452: return null;
453: }
454: }
455:
456: return commonPropertyType;
457: }
458:
459: /**
460: * Converts an object to a specific type.
461: *
462: * <p>
463: * An <code>ELException</code> is thrown if an error occurs during the conversion.
464: * </p>
465: *
466: * @param context The context of this evaluation.
467: * @param obj The object to convert.
468: * @param targetType The target type for the convertion.
469: *
470: * @throws ELException thrown if errors occur.
471: *
472: * @since Jakarta Expression Language 3.0
473: */
474: @Override
475: public <T> T convertToType(ELContext context, Object obj, Class<T> targetType) {
476: context.setPropertyResolved(false);
477:
478: T value = null;
479: for (int i = 0; i < size; i++) {
480: value = elResolvers[i].convertToType(context, obj, targetType);
481: if (context.isPropertyResolved()) {
482: return value;
483: }
484: }
485:
486: return null;
487: }
488:
489: private ELResolver[] elResolvers;
490: private int size;
491:
492: /**
493: * @deprecated This method will be removed without replacement in EL 6.0
494: */
495: @Deprecated(forRemoval = true, since = "5.0")
496: private static class CompositeIterator implements Iterator<FeatureDescriptor> {
497:
498: ELResolver[] resolvers;
499: int size;
500: int index = 0;
501: Iterator<FeatureDescriptor> propertyIter = null;
502: ELContext context;
503: Object base;
504:
505: CompositeIterator(ELResolver[] resolvers, int size, ELContext context, Object base) {
506: this.resolvers = resolvers;
507: this.size = size;
508: this.context = context;
509: this.base = base;
510: }
511:
512: @Override
513: public boolean hasNext() {
514: if (propertyIter == null || !propertyIter.hasNext()) {
515: while (index < size) {
516: ELResolver elResolver = resolvers[index++];
517: propertyIter = elResolver.getFeatureDescriptors(context, base);
518: if (propertyIter != null) {
519: return propertyIter.hasNext();
520: }
521: }
522: return false;
523: }
524: return propertyIter.hasNext();
525: }
526:
527: @Override
528: public FeatureDescriptor next() {
529: if (propertyIter == null || !propertyIter.hasNext()) {
530: while (index < size) {
531: ELResolver elResolver = resolvers[index++];
532: propertyIter = elResolver.getFeatureDescriptors(context, base);
533: if (propertyIter != null) {
534: return propertyIter.next();
535: }
536: }
537: return null;
538: }
539: return propertyIter.next();
540: }
541:
542: @Override
543: public void remove() {
544: throw new UnsupportedOperationException();
545: }
546: }
547: }