001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.el;
018
019 import java.lang.reflect.Array;
020 import java.lang.reflect.InvocationTargetException;
021 import java.util.List;
022 import java.util.Map;
023
024 import javax.servlet.jsp.el.ELException;
025 import javax.servlet.jsp.el.FunctionMapper;
026 import javax.servlet.jsp.el.VariableResolver;
027
028 import org.apache.commons.logging.Log;
029 import org.apache.commons.logging.LogFactory;
030
031 /**
032 *
033 * <p>Represents an operator that obtains a Map entry, an indexed
034 * value, a property value, or an indexed property value of an object.
035 * The following are the rules for evaluating this operator:
036 *
037 * <ul><pre>
038 * Evaluating a[b] (assuming a.b == a["b"])
039 * a is null
040 * return null
041 * b is null
042 * return null
043 * a is Map
044 * !a.containsKey (b)
045 * return null
046 * a.get(b) == null
047 * return null
048 * otherwise
049 * return a.get(b)
050 * a is List or array
051 * coerce b to int (using coercion rules)
052 * coercion couldn't be performed
053 * error
054 * a.get(b) or Array.get(a, b) throws ArrayIndexOutOfBoundsException or IndexOutOfBoundsException
055 * return null
056 * a.get(b) or Array.get(a, b) throws other exception
057 * error
058 * return a.get(b) or Array.get(a, b)
059 *
060 * coerce b to String
061 * b is a readable property of a
062 * getter throws an exception
063 * error
064 * otherwise
065 * return result of getter call
066 *
067 * otherwise
068 * error
069 * </pre></ul>
070 *
071 * @author Nathan Abramson - Art Technology Group
072 * @author Shawn Bayern
073 * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: bayard $
074 **/
075
076 public class ArraySuffix
077 extends ValueSuffix {
078
079 //-------------------------------------
080 // Constants
081 //-------------------------------------
082 private static Log log = LogFactory.getLog(ArraySuffix.class);
083
084 // Zero-argument array
085 static Object[] sNoArgs = new Object[0];
086
087 //-------------------------------------
088 // Properties
089 //-------------------------------------
090 // property index
091
092 Expression mIndex;
093
094 public Expression getIndex() {
095 return mIndex;
096 }
097
098 public void setIndex(Expression pIndex) {
099 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 }