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 */
017package org.apache.commons.el;
018
019import java.lang.reflect.Array;
020import java.lang.reflect.InvocationTargetException;
021import java.util.List;
022import java.util.Map;
023
024import javax.servlet.jsp.el.ELException;
025import javax.servlet.jsp.el.FunctionMapper;
026import javax.servlet.jsp.el.VariableResolver;
027
028import org.apache.commons.logging.Log;
029import 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
076public 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}