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    }