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.lang.reflect.Array;
027: import java.util.List;
028: import java.util.Iterator;
029: import java.beans.FeatureDescriptor;
030:
031: /**
032: * Defines property resolution behavior on arrays.
033: *
034: * <p>This resolver handles base objects that are Java language arrays.
035: * It accepts any object as a property and coerces that object into an
036: * integer index into the array. The resulting value is the value in the array
037: * at that index.</p>
038: *
039: * <p>This resolver can be constructed in read-only mode, which means that
040: * {@link #isReadOnly} will always return <code>true</code> and
041: * {@link #setValue} will always throw
042: * <code>PropertyNotWritableException</code>.</p>
043: *
044: * <p><code>ELResolver</code>s are combined together using
045: * {@link CompositeELResolver}s, to define rich semantics for evaluating
046: * an expression. See the javadocs for {@link ELResolver} for details.</p>
047: *
048: * @see CompositeELResolver
049: * @see ELResolver
050: * @since JSP 2.1
051: */
052: public class ArrayELResolver extends ELResolver {
053:
054: /**
055: * Creates a new read/write <code>ArrayELResolver</code>.
056: */
057: public ArrayELResolver() {
058: this .isReadOnly = false;
059: }
060:
061: /**
062: * Creates a new <code>ArrayELResolver</code> whose read-only status is
063: * determined by the given parameter.
064: *
065: * @param isReadOnly <code>true</code> if this resolver cannot modify
066: * arrays; <code>false</code> otherwise.
067: */
068: public ArrayELResolver(boolean isReadOnly) {
069: this .isReadOnly = isReadOnly;
070: }
071:
072: /**
073: * If the base object is an array, returns the most general acceptable type
074: * for a value in this array.
075: *
076: * <p>If the base is a <code>array</code>, the
077: * <code>propertyResolved</code> property of the <code>ELContext</code>
078: * object must be set to <code>true</code> by this resolver, before
079: * returning. If this property is not <code>true</code> after this method
080: * is called, the caller should ignore the return value.</p>
081: *
082: * <p>Assuming the base is an <code>array</code>, this method will always
083: * return <code>base.getClass().getComponentType()</code>, which is
084: * the most general type of component that can be stored at any given
085: * index in the array.</p>
086: *
087: * @param context The context of this evaluation.
088: * @param base The array to analyze. Only bases that are Java language
089: * arrays are handled by this resolver.
090: * @param property The index of the element in the array to return the
091: * acceptable type for. Will be coerced into an integer, but
092: * otherwise ignored by this resolver.
093: * @return If the <code>propertyResolved</code> property of
094: * <code>ELContext</code> was set to <code>true</code>, then
095: * the most general acceptable type; otherwise undefined.
096: * @throws PropertyNotFoundException if the given index is out of
097: * bounds for this array.
098: * @throws NullPointerException if context is <code>null</code>
099: * @throws ELException if an exception was thrown while performing
100: * the property or variable resolution. The thrown exception
101: * must be included as the cause property of this exception, if
102: * available.
103: */
104: public Class<?> getType(ELContext context, Object base,
105: Object property) {
106:
107: if (context == null) {
108: throw new NullPointerException();
109: }
110:
111: if (base != null && base.getClass().isArray()) {
112: context.setPropertyResolved(true);
113: int index = toInteger(property);
114: if (index < 0 || index >= Array.getLength(base)) {
115: throw new PropertyNotFoundException();
116: }
117: return base.getClass().getComponentType();
118: }
119: return null;
120: }
121:
122: /**
123: * If the base object is a Java language array, returns the value at the
124: * given index. The index is specified by the <code>property</code>
125: * argument, and coerced into an integer. If the coercion could not be
126: * performed, an <code>IllegalArgumentException</code> is thrown. If the
127: * index is out of bounds, <code>null</code> is returned.
128: *
129: * <p>If the base is a Java language array, the
130: * <code>propertyResolved</code> property of the <code>ELContext</code>
131: * object must be set to <code>true</code> by this resolver, before
132: * returning. If this property is not <code>true</code> after this
133: * method is called, the caller should ignore the return value.</p>
134: *
135: * @param context The context of this evaluation.
136: * @param base The array to analyze. Only bases that are Java language
137: * arrays are handled by this resolver.
138: * @param property The index of the value to be returned. Will be coerced
139: * into an integer.
140: * @return If the <code>propertyResolved</code> property of
141: * <code>ELContext</code> was set to <code>true</code>, then
142: * the value at the given index or <code>null</code>
143: * if the index was out of bounds. Otherwise, undefined.
144: * @throws IllegalArgumentException if the property could not be coerced
145: * into an integer.
146: * @throws NullPointerException if context is <code>null</code>.
147: * @throws ELException if an exception was thrown while performing
148: * the property or variable resolution. The thrown exception
149: * must be included as the cause property of this exception, if
150: * available.
151: */
152: public Object getValue(ELContext context, Object base,
153: Object property) {
154:
155: if (context == null) {
156: throw new NullPointerException();
157: }
158:
159: if (base != null && base.getClass().isArray()) {
160: context.setPropertyResolved(true);
161: int index = toInteger(property);
162: if (index >= 0 && index < Array.getLength(base)) {
163: return Array.get(base, index);
164: }
165: }
166: return null;
167: }
168:
169: /**
170: * If the base object is a Java language array, attempts to set the
171: * value at the given index with the given value. The index is specified
172: * by the <code>property</code> argument, and coerced into an integer.
173: * If the coercion could not be performed, an
174: * <code>IllegalArgumentException</code> is thrown. If the index is
175: * out of bounds, a <code>PropertyNotFoundException</code> is thrown.
176: *
177: * <p>If the base is a Java language array, the
178: * <code>propertyResolved</code> property of the <code>ELContext</code>
179: * object must be set to <code>true</code> by this resolver, before
180: * returning. If this property is not <code>true</code> after this method
181: * is called, the caller can safely assume no value was set.</p>
182: *
183: * <p>If this resolver was constructed in read-only mode, this method will
184: * always throw <code>PropertyNotWritableException</code>.</p>
185: *
186: * @param context The context of this evaluation.
187: * @param base The array to be modified. Only bases that are Java language
188: * arrays are handled by this resolver.
189: * @param property The index of the value to be set. Will be coerced
190: * into an integer.
191: * @param val The value to be set at the given index.
192: * @throws ClassCastException if the class of the specified element
193: * prevents it from being added to this array.
194: * @throws NullPointerException if context is <code>null</code>.
195: * @throws IllegalArgumentException if the property could not be coerced
196: * into an integer, or if some aspect of the specified element
197: * prevents it from being added to this array.
198: * @throws PropertyNotWritableException if this resolver was constructed
199: * in read-only mode.
200: * @throws PropertyNotFoundException if the given index is out of
201: * bounds for this array.
202: * @throws ELException if an exception was thrown while performing
203: * the property or variable resolution. The thrown exception
204: * must be included as the cause property of this exception, if
205: * available.
206: */
207: public void setValue(ELContext context, Object base,
208: Object property, Object val) {
209:
210: if (context == null) {
211: throw new NullPointerException();
212: }
213:
214: if (base != null && base.getClass().isArray()) {
215: context.setPropertyResolved(true);
216: if (isReadOnly) {
217: throw new PropertyNotWritableException();
218: }
219: Class<?> type = base.getClass().getComponentType();
220: if (val != null && !type.isAssignableFrom(val.getClass())) {
221: throw new ClassCastException();
222: }
223: int index = toInteger(property);
224: if (index < 0 || index >= Array.getLength(base)) {
225: throw new PropertyNotFoundException();
226: }
227: Array.set(base, index, val);
228: }
229: }
230:
231: /**
232: * If the base object is a Java language array, returns whether a call to
233: * {@link #setValue} will always fail.
234: *
235: * <p>If the base is a Java language array, the
236: * <code>propertyResolved</code> property of the <code>ELContext</code>
237: * object must be set to <code>true</code> by this resolver, before
238: * returning. If this property is not <code>true</code> after this method
239: * is called, the caller should ignore the return value.</p>
240: *
241: * <p>If this resolver was constructed in read-only mode, this method will
242: * always return <code>true</code>. Otherwise, it returns
243: * <code>false</code>.</p>
244: *
245: * @param context The context of this evaluation.
246: * @param base The array to analyze. Only bases that are a Java language
247: * array are handled by this resolver.
248: * @param property The index of the element in the array to return the
249: * acceptable type for. Will be coerced into an integer, but
250: * otherwise ignored by this resolver.
251: * @return If the <code>propertyResolved</code> property of
252: * <code>ELContext</code> was set to <code>true</code>, then
253: * <code>true</code> if calling the <code>setValue</code> method
254: * will always fail or <code>false</code> if it is possible that
255: * such a call may succeed; otherwise undefined.
256: * @throws PropertyNotFoundException if the given index is out of
257: * bounds for this array.
258: * @throws NullPointerException if context is <code>null</code>
259: * @throws ELException if an exception was thrown while performing
260: * the property or variable resolution. The thrown exception
261: * must be included as the cause property of this exception, if
262: * available.
263: */
264: public boolean isReadOnly(ELContext context, Object base,
265: Object property) {
266:
267: if (context == null) {
268: throw new NullPointerException();
269: }
270:
271: if (base != null && base.getClass().isArray()) {
272: context.setPropertyResolved(true);
273: int index = toInteger(property);
274: if (index < 0 || index >= Array.getLength(base)) {
275: throw new PropertyNotFoundException();
276: }
277: }
278: return isReadOnly;
279: }
280:
281: /**
282: * Always returns <code>null</code>, since there is no reason to
283: * iterate through set set of all integers.
284: *
285: * <p>The {@link #getCommonPropertyType} method returns sufficient
286: * information about what properties this resolver accepts.</p>
287: *
288: * @param context The context of this evaluation.
289: * @param base The array to analyze. Only bases that are a Java language
290: * array are handled by this resolver.
291: * @return <code>null</code>.
292: */
293: public Iterator<FeatureDescriptor> getFeatureDescriptors(
294: ELContext context, Object base) {
295: return null;
296: }
297:
298: /**
299: * If the base object is a Java language array, returns the most general
300: * type that this resolver accepts for the <code>property</code> argument.
301: * Otherwise, returns <code>null</code>.
302: *
303: * <p>Assuming the base is an array, this method will always return
304: * <code>Integer.class</code>. This is because arrays accept integers
305: * for their index.</p>
306: *
307: * @param context The context of this evaluation.
308: * @param base The array to analyze. Only bases that are a Java language
309: * array are handled by this resolver.
310: * @return <code>null</code> if base is not a Java language array;
311: * otherwise <code>Integer.class</code>.
312: */
313: public Class<?> getCommonPropertyType(ELContext context, Object base) {
314:
315: if (base != null && base.getClass().isArray()) {
316: return Integer.class;
317: }
318: return null;
319: }
320:
321: private int toInteger(Object p) {
322:
323: if (p instanceof Integer) {
324: return ((Integer) p).intValue();
325: }
326: if (p instanceof Character) {
327: return ((Character) p).charValue();
328: }
329: if (p instanceof Boolean) {
330: return ((Boolean) p).booleanValue() ? 1 : 0;
331: }
332: if (p instanceof Number) {
333: return ((Number) p).intValue();
334: }
335: if (p instanceof String) {
336: return Integer.parseInt((String) p);
337: }
338: throw new IllegalArgumentException();
339: }
340:
341: private boolean isReadOnly;
342: }
|