Skip to contentMethod: ArrayELResolver(boolean)
1: /*
2: * Copyright (c) 1997, 2022 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.lang.reflect.Array;
22:
23: /**
24: * Defines property resolution behavior on arrays.
25: *
26: * <p>
27: * This resolver handles base objects that are Java language arrays. It accepts any object as a property and coerces
28: * that object into an integer index into the array. The resulting value is the value in the array at that index.
29: * </p>
30: *
31: * <p>
32: * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return
33: * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>.
34: * </p>
35: *
36: * <p>
37: * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for
38: * evaluating an expression. See the javadocs for {@link ELResolver} for details.
39: * </p>
40: *
41: * @see CompositeELResolver
42: * @see ELResolver
43: *
44: * @since Jakarta Server Pages 2.1
45: */
46: public class ArrayELResolver extends ELResolver {
47:
48: /**
49: * Creates a new read/write <code>ArrayELResolver</code>.
50: */
51: public ArrayELResolver() {
52: this.isReadOnly = false;
53: }
54:
55: /**
56: * Creates a new <code>ArrayELResolver</code> whose read-only status is determined by the given parameter.
57: *
58: * @param isReadOnly <code>true</code> if this resolver cannot modify arrays; <code>false</code> otherwise.
59: */
60: public ArrayELResolver(boolean isReadOnly) {
61: this.isReadOnly = isReadOnly;
62: }
63:
64: /**
65: * If the base object is an array, returns the most general acceptable type for a value in this array.
66: *
67: * <p>
68: * If the base is a <code>array</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
69: * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
70: * this method is called, the caller should ignore the return value.
71: * </p>
72: *
73: * <p>
74: * Assuming the base is an <code>array</code> and that this resolver was not constructed in read-only mode, this
75: * method will return <code>base.getClass().getComponentType()</code>, which is the most general type of component
76: * that can be stored at any given index in the array.
77: * </p>
78: *
79: * @param context The context of this evaluation.
80: * @param base The array to analyze. Only bases that are Java language arrays are handled by this resolver.
81: * @param property The index of the element in the array to return the acceptable type for. Will be coerced into an
82: * integer, but otherwise ignored by this resolver.
83: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
84: * the most general acceptable type which must be {@code null} if the either the property or the resolver is
85: * read-only; otherwise undefined
86: * @throws PropertyNotFoundException if the given index is out of bounds for this array.
87: * @throws NullPointerException if context is <code>null</code>
88: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
89: * exception must be included as the cause property of this exception, if available.
90: */
91: @Override
92: public Class<?> getType(ELContext context, Object base, Object property) {
93:
94: if (context == null) {
95: throw new NullPointerException();
96: }
97:
98: if (base != null && base.getClass().isArray()) {
99: context.setPropertyResolved(true);
100: int index = toInteger(property);
101: if (index < 0 || index >= Array.getLength(base)) {
102: throw new PropertyNotFoundException();
103: }
104:
105: /*
106: * The resolver may have been created in read-only mode but the
107: * array and its elements will always be read-write.
108: */
109: if (isReadOnly) {
110: return null;
111: }
112:
113: return base.getClass().getComponentType();
114: }
115: return null;
116: }
117:
118: /**
119: * If the base object is a Java language array, returns the value at the given index. The index is specified by the
120: * <code>property</code> argument, and coerced into an integer. If the coercion could not be performed, an
121: * <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, <code>null</code> is returned.
122: *
123: * <p>
124: * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object
125: * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
126: * this method is called, the caller should ignore the return value.
127: * </p>
128: *
129: * @param context The context of this evaluation.
130: * @param base The array to analyze. Only bases that are Java language arrays are handled by this resolver.
131: * @param property The index of the value to be returned. Will be coerced into an integer.
132: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
133: * the value at the given index or <code>null</code> if the index was out of bounds. Otherwise, undefined.
134: * @throws IllegalArgumentException if the property could not be coerced into an integer.
135: * @throws NullPointerException if context is <code>null</code>.
136: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
137: * exception must be included as the cause property of this exception, if available.
138: */
139: @Override
140: public Object getValue(ELContext context, Object base, Object property) {
141:
142: if (context == null) {
143: throw new NullPointerException();
144: }
145:
146: if (base != null && base.getClass().isArray()) {
147: context.setPropertyResolved(base, property);
148: int index = toInteger(property);
149: if (index >= 0 && index < Array.getLength(base)) {
150: return Array.get(base, index);
151: }
152: }
153: return null;
154: }
155:
156: /**
157: * If the base object is a Java language array, attempts to set the value at the given index with the given value. The
158: * index is specified by the <code>property</code> argument, and coerced into an integer. If the coercion could not be
159: * performed, an <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, a
160: * <code>PropertyNotFoundException</code> is thrown.
161: *
162: * <p>
163: * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object
164: * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
165: * this method is called, the caller can safely assume no value was set.
166: * </p>
167: *
168: * <p>
169: * If this resolver was constructed in read-only mode, this method will always throw
170: * <code>PropertyNotWritableException</code>.
171: * </p>
172: *
173: * @param context The context of this evaluation.
174: * @param base The array to be modified. Only bases that are Java language arrays are handled by this resolver.
175: * @param property The index of the value to be set. Will be coerced into an integer.
176: * @param val The value to be set at the given index.
177: * @throws ClassCastException if the class of the specified element prevents it from being added to this array.
178: * @throws NullPointerException if context is <code>null</code>.
179: * @throws IllegalArgumentException if the property could not be coerced into an integer, or if some aspect of the
180: * specified element prevents it from being added to this array.
181: * @throws PropertyNotWritableException if this resolver was constructed in read-only mode.
182: * @throws PropertyNotFoundException if the given index is out of bounds for this array.
183: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
184: * exception must be included as the cause property of this exception, if available.
185: */
186: @Override
187: public void setValue(ELContext context, Object base, Object property, Object val) {
188:
189: if (context == null) {
190: throw new NullPointerException();
191: }
192:
193: if (base != null && base.getClass().isArray()) {
194: context.setPropertyResolved(base, property);
195: if (isReadOnly) {
196: throw new PropertyNotWritableException();
197: }
198: Class<?> type = base.getClass().getComponentType();
199: if (val != null && !type.isAssignableFrom(val.getClass())) {
200: throw new ClassCastException();
201: }
202: int index = toInteger(property);
203: if (index < 0 || index >= Array.getLength(base)) {
204: throw new PropertyNotFoundException();
205: }
206: Array.set(base, index, val);
207: }
208: }
209:
210: /**
211: * If the base object is a Java language array, returns whether a call to {@link #setValue} will always fail.
212: *
213: * <p>
214: * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object
215: * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
216: * this method is called, the caller should ignore the return value.
217: * </p>
218: *
219: * <p>
220: * If this resolver was constructed in read-only mode, this method will always return <code>true</code>. Otherwise, it
221: * returns <code>false</code>.
222: * </p>
223: *
224: * @param context The context of this evaluation.
225: * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver.
226: * @param property The index of the element in the array to return the acceptable type for. Will be coerced into an
227: * integer, but otherwise ignored by this resolver.
228: * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
229: * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is
230: * possible that such a call may succeed; otherwise undefined.
231: * @throws PropertyNotFoundException if the given index is out of bounds for this array.
232: * @throws NullPointerException if context is <code>null</code>
233: * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
234: * exception must be included as the cause property of this exception, if available.
235: */
236: @Override
237: public boolean isReadOnly(ELContext context, Object base, Object property) {
238:
239: if (context == null) {
240: throw new NullPointerException();
241: }
242:
243: if (base != null && base.getClass().isArray()) {
244: context.setPropertyResolved(true);
245: int index = toInteger(property);
246: if (index < 0 || index >= Array.getLength(base)) {
247: throw new PropertyNotFoundException();
248: }
249: }
250: return isReadOnly;
251: }
252:
253: /**
254: * If the base object is a Java language array, returns the most general type that this resolver accepts for the
255: * <code>property</code> argument. Otherwise, returns <code>null</code>.
256: *
257: * <p>
258: * Assuming the base is an array, this method will always return <code>Integer.class</code>. This is because arrays
259: * accept integers for their index.
260: * </p>
261: *
262: * @param context The context of this evaluation.
263: * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver.
264: * @return <code>null</code> if base is not a Java language array; otherwise <code>Integer.class</code>.
265: */
266: @Override
267: public Class<?> getCommonPropertyType(ELContext context, Object base) {
268:
269: if (base != null && base.getClass().isArray()) {
270: return Integer.class;
271: }
272: return null;
273: }
274:
275: private int toInteger(Object p) {
276:
277: if (p instanceof Integer) {
278: return ((Integer) p).intValue();
279: }
280: if (p instanceof Character) {
281: return ((Character) p).charValue();
282: }
283: if (p instanceof Boolean) {
284: return ((Boolean) p).booleanValue() ? 1 : 0;
285: }
286: if (p instanceof Number) {
287: return ((Number) p).intValue();
288: }
289: if (p instanceof String) {
290: return Integer.parseInt((String) p);
291: }
292: throw new IllegalArgumentException();
293: }
294:
295: private boolean isReadOnly;
296: }