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  package org.apache.commons.jexl.parser;
    17
    18  import java.lang.reflect.InvocationTargetException;
    19  import java.math.BigDecimal;
    20  import java.math.BigInteger;
    21
    22  import org.apache.commons.jexl.JexlContext;
    23  import org.apache.commons.jexl.util.Introspector;
    24  import org.apache.commons.jexl.util.introspection.VelMethod;
    25  import org.apache.commons.jexl.util.introspection.Info;
    26
    27  /***
    28   * Method execution.
    29   */
    30  public class ASTMethod extends SimpleNode {
    31      /*** dummy velocity info. */
    32      private static final Info DUMMY = new Info("", 1, 1);
    33
    34      /***
    35       * Create the node given an id.
    36       *
    37       * @param id node id.
    38       */
    39      public ASTMethod(int id) {
    40          super(id);
    41      }
    42
    43      /***
    44       * Create a node with the given parser and id.
    45       *
    46       * @param p a parser.
    47       * @param id node id.
    48       */
    49      public ASTMethod(Parser p, int id) {
    50          super(p, id);
    51      }
    52
    53      /*** {@inheritDoc} */
    54      public Object jjtAccept(ParserVisitor visitor, Object data) {
    55          return visitor.visit(this, data);
    56      }
    57
    58      /***
    59       * evaluate a method invocation upon a base object.
    60       *
    61       * foo.bar(2)
    62       *
    63       * @param jc the {@link JexlContext} to evaluate against.
    64       * @param obj The object to have the method invoked.
    65       * @return the value of the method invocation.
    66       * @throws Exception on any error
    67       */
    68      public Object execute(Object obj, JexlContext jc) throws Exception {
    69          String methodName = ((ASTIdentifier) jjtGetChild(0)).val;
    70
    71          int paramCount = jjtGetNumChildren() - 1;
    72
    73          /*
    74           * get our params
    75           */
    76
    77          Object[] params = new Object[paramCount];
    78
    79          try {
    80              for (int i = 0; i < paramCount; i++) {
    81                  params[i] = ((SimpleNode) jjtGetChild(i + 1)).value(jc);
    82              }
    83
    84              VelMethod vm = Introspector.getUberspect().getMethod(obj, methodName, params, DUMMY);
    85              /*
    86               * DG: If we can't find an exact match, narrow the parameters and
    87               * try again!
    88               */
    89              if (vm == null) {
    90
    91                  // replace all numbers with the smallest type that will fit
    92                  for (int i = 0; i < params.length; i++) {
    93                      Object param = params[i];
    94                      if (param instanceof Number) {
    95                          params[i] = narrow((Number) param);
    96                      }
    97                  }
    98                  vm = Introspector.getUberspect().getMethod(obj, methodName, params, DUMMY);
    99                  if (vm == null) {
    100                     return null;
    101                 }
    102             }
    103
    104             return vm.invoke(obj, params);
    105         } catch (InvocationTargetException e) {
    106             Throwable t = e.getTargetException();
    107
    108             if (t instanceof Exception) {
    109                 throw (Exception) t;
    110             }
    111
    112             throw e;
    113         }
    114     }
    115
    116     /***
    117      * Given a Number, return back the value using the smallest type the result
    118      * will fit into. This works hand in hand with parameter 'widening' in java
    119      * method calls, e.g. a call to substring(int,int) with an int and a long
    120      * will fail, but a call to substring(int,int) with an int and a short will
    121      * succeed.
    122      *
    123      * @param original the original number.
    124      * @return a value of the smallest type the original number will fit into.
    125      * @since 1.1
    126      */
    127     private Number narrow(Number original) {
    128         if (original == null || original instanceof BigDecimal || original instanceof BigInteger) {
    129             return original;
    130         }
    131         Number result = original;
    132         if (original instanceof Double || original instanceof Float) {
    133             double value = original.doubleValue();
    134             if (value <= Float.MAX_VALUE && value >= Float.MIN_VALUE) {
    135                 result = new Float(result.floatValue());
    136             }
    137             // else it was already a double
    138         } else {
    139             long value = original.longValue();
    140             if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
    141                 // it will fit in a byte
    142                 result = new Byte((byte) value);
    143             } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
    144                 result = new Short((short) value);
    145             } else if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
    146                 result = new Integer((int) value);
    147             }
    148             // else it was already a long
    149         }
    150         return result;
    151     }
    152
    153 }