Skip to content

Package: CompositeELResolver

CompositeELResolver

nameinstructionbranchcomplexitylinemethod
CompositeELResolver()
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
add(ELResolver)
M: 41 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
convertToType(ELContext, Object, Class)
M: 29 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
getCommonPropertyType(ELContext, Object)
M: 42 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 13 C: 0
0%
M: 1 C: 0
0%
getFeatureDescriptors(ELContext, Object)
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getType(ELContext, Object, Object)
M: 27 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
getValue(ELContext, Object, Object)
M: 29 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
invoke(ELContext, Object, Object, Class[], Object[])
M: 29 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
isReadOnly(ELContext, Object, Object)
M: 27 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
setValue(ELContext, Object, Object, Object)
M: 25 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%

Coverage

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; otherwise undefined.
249: * @throws NullPointerException if context is <code>null</code>
250: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
251: * the specified variable or property does not exist or is not readable.
252: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
253: * exception must be included as the cause property of this exception, if available.
254: */
255: @Override
256: public Class<?> getType(ELContext context, Object base, Object property) {
257: context.setPropertyResolved(false);
258:
259: Class<?> type;
260:• for (int i = 0; i < size; i++) {
261: type = elResolvers[i].getType(context, base, property);
262:• if (context.isPropertyResolved()) {
263: return type;
264: }
265: }
266:
267: return null;
268: }
269:
270: /**
271: * Attempts to set the value of the given <code>property</code> object on the given <code>base</code> object. All
272: * component resolvers are asked to attempt to set the value.
273: *
274: * <p>
275: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
276: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
277: * not <code>true</code> after this method is called, the caller can safely assume no value has been set.
278: *
279: * <p>
280: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
281: *
282: * <p>
283: * Next, for each component resolver in this composite:
284: * <ol>
285: * <li>The <code>setValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code>,
286: * <code>property</code> and <code>value</code>.</li>
287: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
288: * continues.</li>
289: * <li>Otherwise, iteration stops and no more component resolvers are considered.</li>
290: * </ol>
291: *
292: * <p>
293: * If none of the component resolvers were able to perform this operation, the <code>propertyResolved</code> flag
294: * remains set to <code>false</code>.
295: *
296: * <p>
297: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
298: *
299: * @param context The context of this evaluation.
300: * @param base The base object whose property value is to be set, or <code>null</code> to set a top-level variable.
301: * @param property The property or variable to be set.
302: * @param val The value to set the property or variable to.
303: * @throws NullPointerException if context is <code>null</code>
304: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
305: * the specified variable or property does not exist.
306: * @throws PropertyNotWritableException if the given (base, property) pair is handled by this <code>ELResolver</code>
307: * but the specified variable or property is not writable.
308: * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
309: * must be included as the cause property of this exception, if available.
310: */
311: @Override
312: public void setValue(ELContext context, Object base, Object property, Object val) {
313: context.setPropertyResolved(false);
314:
315:• for (int i = 0; i < size; i++) {
316: elResolvers[i].setValue(context, base, property, val);
317:• if (context.isPropertyResolved()) {
318: return;
319: }
320: }
321: }
322:
323: /**
324: * For a given <code>base</code> and <code>property</code>, attempts to determine whether a call to {@link #setValue}
325: * will always fail. The result is obtained by querying all component resolvers.
326: *
327: * <p>
328: * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
329: * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
330: * not <code>true</code> after this method is called, the caller should ignore the return value.
331: * </p>
332: *
333: * <p>
334: * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
335: * </p>
336: *
337: * <p>
338: * Next, for each component resolver in this composite:
339: * <ol>
340: * <li>The <code>isReadOnly()</code> method is called, passing in the provided <code>context</code>, <code>base</code>
341: * and <code>property</code>.</li>
342: * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
343: * continues.</li>
344: * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
345: * <code>isReadOnly()</code> is returned by this method.</li>
346: * </ol>
347: *
348: * <p>
349: * If none of the component resolvers were able to perform this operation, the value <code>false</code> is returned and
350: * the <code>propertyResolved</code> flag remains set to <code>false</code>
351: * </p>
352: * .
353: *
354: * <p>
355: * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
356: * </p>
357: *
358: * @param context The context of this evaluation.
359: * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
360: * variable.
361: * @param property The property or variable to return the read-only status for.
362: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
363: * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined.
364: * @throws NullPointerException if context is <code>null</code>
365: * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
366: * the specified variable or property does not exist.
367: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
368: * exception must be included as the cause property of this exception, if available.
369: */
370: @Override
371: public boolean isReadOnly(ELContext context, Object base, Object property) {
372: context.setPropertyResolved(false);
373:
374: boolean readOnly;
375:• for (int i = 0; i < size; i++) {
376: readOnly = elResolvers[i].isReadOnly(context, base, property);
377:• if (context.isPropertyResolved()) {
378: return readOnly;
379: }
380: }
381:
382: return false; // Does not matter
383: }
384:
385: /**
386: * Returns information about the set of variables or properties that can be resolved for the given <code>base</code>
387: * object. One use for this method is to assist tools in auto-completion. The results are collected from all component
388: * resolvers.
389: *
390: * <p>
391: * The <code>propertyResolved</code> property of the <code>ELContext</code> is not relevant to this method. The results
392: * of all <code>ELResolver</code>s are concatenated.
393: * </p>
394: *
395: * <p>
396: * The <code>Iterator</code> returned is an iterator over the collection of <code>FeatureDescriptor</code> objects
397: * returned by the iterators returned by each component resolver's <code>getFeatureDescriptors</code> method. If
398: * <code>null</code> is returned by a resolver, it is skipped.
399: * </p>
400: *
401: * @param context The context of this evaluation.
402: * @param base The base object whose set of valid properties is to be enumerated, or <code>null</code> to enumerate the
403: * set of top-level variables that this resolver can evaluate.
404: * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code>
405: * objects, or <code>null</code> if this resolver does not handle the given <code>base</code> object or that the results
406: * are too complex to represent with this method
407: *
408: * @deprecated This method will be removed with replacement in EL 6.0
409: */
410: @Deprecated(forRemoval = true, since = "5.0")
411: @Override
412: public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
413: return new CompositeIterator(elResolvers, size, context, base);
414: }
415:
416: /**
417: * Returns the most general type that this resolver accepts for the <code>property</code> argument, given a
418: * <code>base</code> object. One use for this method is to assist tools in auto-completion. The result is obtained by
419: * querying all component resolvers.
420: *
421: * <p>
422: * The <code>Class</code> returned is the most specific class that is a common superclass of all the classes returned by
423: * each component resolver's <code>getCommonPropertyType</code> method. If <code>null</code> is returned by a resolver,
424: * it is skipped.
425: *
426: * @param context The context of this evaluation.
427: * @param base The base object to return the most general property type for, or <code>null</code> to enumerate the set
428: * of top-level variables that this resolver can evaluate.
429: *
430: * @return <code>null</code> if this <code>ELResolver</code> does not know how to handle the given <code>base</code>
431: * object; otherwise <code>Object.class</code> if any type of <code>property</code> is accepted; otherwise the most
432: * general <code>property</code> type accepted for the given <code>base</code>.
433: */
434: @Override
435: public Class<?> getCommonPropertyType(ELContext context, Object base) {
436: Class<?> commonPropertyType = null;
437:• for (int i = 0; i < size; i++) {
438:
439: Class<?> type = elResolvers[i].getCommonPropertyType(context, base);
440:• if (type == null) {
441: // skip this Jakarta Expression Language Resolver
442: continue;
443:• } else if (commonPropertyType == null) {
444: commonPropertyType = type;
445:• } else if (commonPropertyType.isAssignableFrom(type)) {
446: continue;
447:• } else if (type.isAssignableFrom(commonPropertyType)) {
448: commonPropertyType = type;
449: } else {
450: // Don't have a commonPropertyType
451: return null;
452: }
453: }
454:
455: return commonPropertyType;
456: }
457:
458: /**
459: * Converts an object to a specific type.
460: *
461: * <p>
462: * An <code>ELException</code> is thrown if an error occurs during the conversion.
463: * </p>
464: *
465: * @param context The context of this evaluation.
466: * @param obj The object to convert.
467: * @param targetType The target type for the convertion.
468: *
469: * @throws ELException thrown if errors occur.
470: *
471: * @since Jakarta Expression Language 3.0
472: */
473: @Override
474: public <T> T convertToType(ELContext context, Object obj, Class<T> targetType) {
475: context.setPropertyResolved(false);
476:
477: T value = null;
478:• for (int i = 0; i < size; i++) {
479: value = elResolvers[i].convertToType(context, obj, targetType);
480:• if (context.isPropertyResolved()) {
481: return value;
482: }
483: }
484:
485: return null;
486: }
487:
488: private ELResolver[] elResolvers;
489: private int size;
490:
491: /**
492: * @deprecated This method will be removed without replacement in EL 6.0
493: */
494: @Deprecated(forRemoval = true, since = "5.0")
495: private static class CompositeIterator implements Iterator<FeatureDescriptor> {
496:
497: ELResolver[] resolvers;
498: int size;
499: int index = 0;
500: Iterator<FeatureDescriptor> propertyIter = null;
501: ELContext context;
502: Object base;
503:
504: CompositeIterator(ELResolver[] resolvers, int size, ELContext context, Object base) {
505: this.resolvers = resolvers;
506: this.size = size;
507: this.context = context;
508: this.base = base;
509: }
510:
511: @Override
512: public boolean hasNext() {
513: if (propertyIter == null || !propertyIter.hasNext()) {
514: while (index < size) {
515: ELResolver elResolver = resolvers[index++];
516: propertyIter = elResolver.getFeatureDescriptors(context, base);
517: if (propertyIter != null) {
518: return propertyIter.hasNext();
519: }
520: }
521: return false;
522: }
523: return propertyIter.hasNext();
524: }
525:
526: @Override
527: public FeatureDescriptor next() {
528: if (propertyIter == null || !propertyIter.hasNext()) {
529: while (index < size) {
530: ELResolver elResolver = resolvers[index++];
531: propertyIter = elResolver.getFeatureDescriptors(context, base);
532: if (propertyIter != null) {
533: return propertyIter.next();
534: }
535: }
536: return null;
537: }
538: return propertyIter.next();
539: }
540:
541: @Override
542: public void remove() {
543: throw new UnsupportedOperationException();
544: }
545: }
546: }