View Javadoc

    1   /*
    2    * Copyright 2002-2006 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16
    17  package org.apache.commons.jexl.parser;
    18
    19  import org.apache.commons.jexl.JexlContext;
    20  import org.apache.commons.jexl.util.Coercion;
    21  import org.apache.commons.jexl.util.Introspector;
    22  import org.apache.commons.jexl.util.introspection.Info;
    23  import org.apache.commons.jexl.util.introspection.VelPropertyGet;
    24
    25  import java.util.List;
    26  import java.util.Map;
    27  import java.lang.reflect.Array;
    28
    29  /***
    30   * Like an ASTIdentifier, but with array access allowed.
    31   *
    32   * $foo[2]
    33   *
    34   * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
    35   * @version $Id: ASTArrayAccess.java 398180 2006-04-29 15:40:35Z dion $
    36   */
    37  public class ASTArrayAccess extends SimpleNode {
    38      /*** dummy velocity info. */
    39      private static final Info DUMMY = new Info("", 1, 1);
    40
    41      /***
    42       * Create the node given an id.
    43       *
    44       * @param id node id.
    45       */
    46      public ASTArrayAccess(int id) {
    47          super(id);
    48      }
    49
    50      /***
    51       * Create a node with the given parser and id.
    52       *
    53       * @param p a parser.
    54       * @param id node id.
    55       */
    56      public ASTArrayAccess(Parser p, int id) {
    57          super(p, id);
    58      }
    59
    60      /*** {@inheritDoc} */
    61      public Object jjtAccept(ParserVisitor visitor, Object data) {
    62          return visitor.visit(this, data);
    63      }
    64
    65      /***
    66       * evaluate array access upon a base object.
    67       *
    68       * foo.bar[2]
    69       *
    70       * makes me rethink the array operator :)
    71       * @param jc the {@link JexlContext} to evaluate against.
    72       * @param obj not used.
    73       * @return the value of the array expression.
    74       * @throws Exception on any error
    75       */
    76      public Object execute(Object obj, JexlContext jc) throws Exception {
    77          ASTIdentifier base = (ASTIdentifier) jjtGetChild(0);
    78
    79          Object result = base.execute(obj, jc);
    80
    81          /*
    82           * ignore the first child - it's our identifier
    83           */
    84          for (int i = 1; i < jjtGetNumChildren(); i++) {
    85              Object loc = ((SimpleNode) jjtGetChild(i)).value(jc);
    86
    87              if (loc == null) {
    88                  return null;
    89              }
    90
    91              result = evaluateExpr(result, loc);
    92          }
    93
    94          return result;
    95      }
    96
    97      /*** {@inheritDoc} */
    98      public Object value(JexlContext jc) throws Exception {
    99          /*
    100          * get the base ASTIdentifier
    101          */
    102
    103         ASTIdentifier base = (ASTIdentifier) jjtGetChild(0);
    104
    105         Object o = base.value(jc);
    106
    107         /*
    108          * ignore the first child - it's our identifier
    109          */
    110         for (int i = 1; i < jjtGetNumChildren(); i++) {
    111             Object loc = ((SimpleNode) jjtGetChild(i)).value(jc);
    112
    113             if (loc == null) {
    114                 return null;
    115             }
    116
    117             o = evaluateExpr(o, loc);
    118         }
    119
    120         return o;
    121     }
    122
    123     /***
    124      * Evaluate the Array expression 'loc' on the given object, o.
    125      * e.g. in 'a[2]', <code>2</code> is 'loc' and <code>a</code> is 'o'.
    126      *
    127      * If o or loc are null, null is returned.
    128      * If o is a Map, o.get(loc) is returned.
    129      * If o is a List, o.get(loc) is returned. loc must resolve to an int value.
    130      * If o is an Array, o[loc] is returned. loc must resolve to an int value.
    131      * Otherwise loc is treated as a bean property of o.
    132      *
    133      * @param o an object to be accessed using the array operator or '.' operator.
    134      * @param loc the index of the object to be returned.
    135      * @return the resulting value.
    136      * @throws Exception on any error.
    137      */
    138     public static Object evaluateExpr(Object o, Object loc) throws Exception {
    139         /*
    140          * following the JSTL EL rules
    141          */
    142
    143         if (o == null) {
    144             return null;
    145         }
    146
    147         if (loc == null) {
    148             return null;
    149         }
    150
    151         if (o instanceof Map) {
    152             if (!((Map) o).containsKey(loc)) {
    153                 return null;
    154             }
    155
    156             return ((Map) o).get(loc);
    157         } else if (o instanceof List) {
    158             int idx = Coercion.coerceInteger(loc).intValue();
    159
    160             try {
    161                 return ((List) o).get(idx);
    162             } catch (IndexOutOfBoundsException iobe) {
    163                 return null;
    164             }
    165         } else if (o.getClass().isArray()) {
    166             int idx = Coercion.coerceInteger(loc).intValue();
    167
    168             try {
    169                 return Array.get(o, idx);
    170             } catch (ArrayIndexOutOfBoundsException aiobe) {
    171                 return null;
    172             }
    173         } else {
    174             /*
    175              * "Otherwise (a JavaBean object)..." huh? :)
    176              */
    177
    178             String s = loc.toString();
    179
    180             VelPropertyGet vg = Introspector.getUberspect().getPropertyGet(o, s, DUMMY);
    181
    182             if (vg != null) {
    183                 return vg.invoke(o);
    184             }
    185         }
    186
    187         throw new Exception("Unsupported object type for array [] accessor");
    188     }
    189
    190     /***
    191      * Gets the variable name piece of the expression.
    192      * @return a String of the identifer.
    193      * @see ASTIdentifier#getIdentifierString().
    194      */
    195     public String getIdentifierString() {
    196         return ((ASTIdentifier) jjtGetChild(0)).getIdentifierString();
    197     }
    198 }