1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.el;
18
19 import java.lang.reflect.Array;
20 import java.lang.reflect.InvocationTargetException;
21 import java.util.List;
22 import java.util.Map;
23
24 import javax.servlet.jsp.el.ELException;
25 import javax.servlet.jsp.el.FunctionMapper;
26 import javax.servlet.jsp.el.VariableResolver;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 /**
32 *
33 * <p>Represents an operator that obtains a Map entry, an indexed
34 * value, a property value, or an indexed property value of an object.
35 * The following are the rules for evaluating this operator:
36 *
37 * <ul><pre>
38 * Evaluating a[b] (assuming a.b == a["b"])
39 * a is null
40 * return null
41 * b is null
42 * return null
43 * a is Map
44 * !a.containsKey (b)
45 * return null
46 * a.get(b) == null
47 * return null
48 * otherwise
49 * return a.get(b)
50 * a is List or array
51 * coerce b to int (using coercion rules)
52 * coercion couldn't be performed
53 * error
54 * a.get(b) or Array.get(a, b) throws ArrayIndexOutOfBoundsException or IndexOutOfBoundsException
55 * return null
56 * a.get(b) or Array.get(a, b) throws other exception
57 * error
58 * return a.get(b) or Array.get(a, b)
59 *
60 * coerce b to String
61 * b is a readable property of a
62 * getter throws an exception
63 * error
64 * otherwise
65 * return result of getter call
66 *
67 * otherwise
68 * error
69 * </pre></ul>
70 *
71 * @author Nathan Abramson - Art Technology Group
72 * @author Shawn Bayern
73 * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: bayard $
74 **/
75
76 public class ArraySuffix
77 extends ValueSuffix {
78
79 //-------------------------------------
80 // Constants
81 //-------------------------------------
82 private static Log log = LogFactory.getLog(ArraySuffix.class);
83
84 // Zero-argument array
85 static Object[] sNoArgs = new Object[0];
86
87 //-------------------------------------
88 // Properties
89 //-------------------------------------
90 // property index
91
92 Expression mIndex;
93
94 public Expression getIndex() {
95 return mIndex;
96 }
97
98 public void setIndex(Expression pIndex) {
99 mIndex = pIndex;
100 }
101
102 //-------------------------------------
103 /**
104 *
105 * Constructor
106 **/
107 public ArraySuffix(Expression pIndex) {
108 mIndex = pIndex;
109 }
110
111 //-------------------------------------
112 /**
113 *
114 * Gets the value of the index
115 **/
116 Object evaluateIndex(
117 VariableResolver pResolver,
118 FunctionMapper functions)
119 throws ELException {
120 return mIndex.evaluate(pResolver, functions);
121 }
122
123 //-------------------------------------
124 /**
125 *
126 * Returns the operator symbol
127 **/
128 String getOperatorSymbol() {
129 return "[]";
130 }
131
132 //-------------------------------------
133 // ValueSuffix methods
134 //-------------------------------------
135 /**
136 *
137 * Returns the expression in the expression language syntax
138 **/
139 public String getExpressionString() {
140 return "[" + mIndex.getExpressionString() + "]";
141 }
142
143 //-------------------------------------
144 /**
145 *
146 * Evaluates the expression in the given context, operating on the
147 * given value.
148 **/
149 public Object evaluate(Object pValue, VariableResolver pResolver, FunctionMapper functions)
150 throws ELException {
151 Object indexVal;
152 String indexStr;
153 BeanInfoProperty property;
154 BeanInfoIndexedProperty ixproperty;
155
156 // Check for null value
157 if (pValue == null) {
158 if (log.isWarnEnabled()) {
159 log.warn(
160 MessageUtil.getMessageWithArgs(
161 Constants.CANT_GET_INDEXED_VALUE_OF_NULL, getOperatorSymbol()));
162 return null;
163 }
164 }
165
166 // Evaluate the index
167 else if ((indexVal = evaluateIndex(pResolver, functions))
168 == null) {
169 if (log.isWarnEnabled()) {
170 log.warn(
171 MessageUtil.getMessageWithArgs(
172 Constants.CANT_GET_NULL_INDEX, getOperatorSymbol()));
173 return null;
174 }
175 }
176
177 // See if it's a Map
178 else if (pValue instanceof Map) {
179 Map val = (Map) pValue;
180 return val.get(indexVal);
181 }
182
183 // See if it's a List or array
184 else if (pValue instanceof List ||
185 pValue.getClass().isArray()) {
186 Integer indexObj = Coercions.coerceToInteger(indexVal);
187 if (indexObj == null) {
188 if (log.isErrorEnabled()) {
189 String message = MessageUtil.getMessageWithArgs(
190 Constants.BAD_INDEX_VALUE,
191 getOperatorSymbol(), indexVal.getClass().getName());
192 log.error(message);
193 throw new ELException(message);
194 }
195 return null;
196 } else if (pValue instanceof List) {
197 try {
198 return ((List) pValue).get(indexObj.intValue());
199 } catch (ArrayIndexOutOfBoundsException aob) {
200 if (log.isWarnEnabled()) {
201 log.warn(
202 MessageUtil.getMessageWithArgs(
203 Constants.EXCEPTION_ACCESSING_LIST, indexObj), aob);
204 }
205 return null;
206 } catch (IndexOutOfBoundsException iob) {
207 if (log.isWarnEnabled()) {
208 log.warn(
209 MessageUtil.getMessageWithArgs(
210 Constants.EXCEPTION_ACCESSING_LIST, indexObj), iob);
211 }
212 return null;
213 } catch (Throwable t) {
214 if (log.isErrorEnabled()) {
215 String message = MessageUtil.getMessageWithArgs(
216 Constants.EXCEPTION_ACCESSING_LIST,
217 indexObj);
218 log.error(message, t);
219 throw new ELException(message, t);
220 }
221 return null;
222 }
223 } else {
224 try {
225 return Array.get(pValue, indexObj.intValue());
226 } catch (ArrayIndexOutOfBoundsException aob) {
227 if (log.isWarnEnabled()) {
228 log.warn(
229 MessageUtil.getMessageWithArgs(
230 Constants.EXCEPTION_ACCESSING_ARRAY, indexObj), aob);
231 }
232 return null;
233 } catch (IndexOutOfBoundsException iob) {
234 if (log.isWarnEnabled()) {
235 log.warn(
236 MessageUtil.getMessageWithArgs(
237 Constants.EXCEPTION_ACCESSING_ARRAY, indexObj), iob);
238 }
239 return null;
240 } catch (Throwable t) {
241 if (log.isErrorEnabled()) {
242 String message = MessageUtil.getMessageWithArgs(
243 Constants.EXCEPTION_ACCESSING_ARRAY,
244 indexObj);
245 log.error(message, t);
246 throw new ELException(message, t);
247 }
248 return null;
249 }
250 }
251 }
252
253 // Coerce to a String for property access
254
255 else if ((indexStr = Coercions.coerceToString(indexVal)) ==
256 null) {
257 return null;
258 }
259
260 // Look for a JavaBean property
261 else if ((property = BeanInfoManager.getBeanInfoProperty
262 (pValue.getClass(), indexStr)) != null &&
263 property.getReadMethod() != null) {
264 try {
265 return property.getReadMethod().invoke(pValue, sNoArgs);
266 } catch (InvocationTargetException exc) {
267 if (log.isErrorEnabled()) {
268 String message = MessageUtil.getMessageWithArgs(
269 Constants.ERROR_GETTING_PROPERTY, indexStr, pValue.getClass().getName());
270 Throwable t = exc.getTargetException();
271 log.warn(message, t);
272 throw new ELException(message, t);
273 }
274 return null;
275 } catch (Throwable t) {
276 if (log.isErrorEnabled()) {
277 String message = MessageUtil.getMessageWithArgs(
278 Constants.ERROR_GETTING_PROPERTY, indexStr, pValue.getClass().getName());
279 log.warn(message, t);
280 throw new ELException(message, t);
281 }
282 return null;
283 }
284 } else {
285 if (log.isErrorEnabled()) {
286 String message = MessageUtil.getMessageWithArgs(
287 Constants.CANT_FIND_INDEX,
288 indexVal,
289 pValue.getClass().getName(),
290 getOperatorSymbol());
291 log.error(message);
292 throw new ELException(message);
293 }
294 return null;
295 }
296 return null;
297 }
298
299 public ValueSuffix bindFunctions(final FunctionMapper functions) throws ELException {
300 return new ArraySuffix(mIndex.bindFunctions(functions));
301 }
302 //-------------------------------------
303 }