View Javadoc

    1   /*
    2    * Copyright 2002,2004 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.util.introspection;
    18
    19  import org.apache.commons.jexl.util.ArrayIterator;
    20  import org.apache.commons.jexl.util.EnumerationIterator;
    21  import org.apache.commons.jexl.util.AbstractExecutor;
    22  import org.apache.commons.jexl.util.GetExecutor;
    23  import org.apache.commons.jexl.util.BooleanPropertyExecutor;
    24  import org.apache.commons.jexl.util.PropertyExecutor;
    25  import org.apache.commons.logging.Log;
    26
    27  import java.lang.reflect.Method;
    28  import java.lang.reflect.InvocationTargetException;
    29  import java.util.Iterator;
    30  import java.util.Collection;
    31  import java.util.Map;
    32  import java.util.Enumeration;
    33  import java.util.ArrayList;
    34
    35  /***
    36   * Implementation of Uberspect to provide the default introspective
    37   * functionality of Velocity.
    38   *
    39   * @since 1.0
    40   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
    41   * @version $Id: UberspectImpl.java 398509 2006-05-01 03:34:35Z dion $
    42   */
    43  public class UberspectImpl implements Uberspect, UberspectLoggable {
    44      /*** index of the first character of the property. */
    45      private static final int PROPERTY_START_INDEX = 3;
    46
    47      /***
    48       * Our runtime logger.
    49       */
    50      private Log rlog;
    51
    52      /***
    53       * the default Velocity introspector.
    54       */
    55      private static Introspector introspector;
    56
    57      /***
    58       * init - does nothing - we need to have setRuntimeLogger called before
    59       * getting our introspector, as the default vel introspector depends upon
    60       * it.
    61       * @throws Exception on any error.
    62       */
    63      public void init() throws Exception {
    64      }
    65
    66      /***
    67       * Sets the runtime logger - this must be called before anything else
    68       * besides init() as to get the logger. Makes the pull model appealing...
    69       * @param runtimeLogger service to use for logging.
    70       */
    71      public void setRuntimeLogger(Log runtimeLogger) {
    72          rlog = runtimeLogger;
    73          introspector = new Introspector(rlog);
    74      }
    75
    76      /***
    77       * {@inheritDoc}
    78       */
    79      public Iterator getIterator(Object obj, Info i) throws Exception {
    80          if (obj.getClass().isArray()) {
    81              return new ArrayIterator(obj);
    82          } else if (obj instanceof Collection) {
    83              return ((Collection) obj).iterator();
    84          } else if (obj instanceof Map) {
    85              return ((Map) obj).values().iterator();
    86          } else if (obj instanceof Iterator) {
    87              rlog.warn("Warning! The iterative " + " is an Iterator in the #foreach() loop at [" + i.getLine() + ","
    88                  + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
    89                  + " if used in more than once, this may lead to" + " unexpected results.");
    90
    91              return ((Iterator) obj);
    92          } else if (obj instanceof Enumeration) {
    93              rlog.warn("Warning! The iterative " + " is an Enumeration in the #foreach() loop at [" + i.getLine() + ","
    94                  + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
    95                  + " if used in more than once, this may lead to" + " unexpected results.");
    96
    97              return new EnumerationIterator((Enumeration) obj);
    98          }
    99
    100         /* we have no clue what this is */
    101         rlog.warn("Could not determine type of iterator in " + "#foreach loop " + " at [" + i.getLine() + ","
    102             + i.getColumn() + "]" + " in template " + i.getTemplateName());
    103
    104         return null;
    105     }
    106
    107     /***
    108      * {@inheritDoc}
    109      */
    110     public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception {
    111         if (obj == null) {
    112             return null;
    113         }
    114
    115         Method m = introspector.getMethod(obj.getClass(), methodName, args);
    116         if (m == null && obj instanceof Class) {
    117             m = introspector.getMethod((Class) obj, methodName, args);
    118         }
    119
    120         return (m == null) ? null : new VelMethodImpl(m);
    121     }
    122
    123     /***
    124      * {@inheritDoc}
    125      */
    126     public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception {
    127         AbstractExecutor executor;
    128
    129         Class claz = obj.getClass();
    130
    131         /*
    132          * first try for a getFoo() type of property (also getfoo() )
    133          */
    134
    135         executor = new PropertyExecutor(rlog, introspector, claz, identifier);
    136
    137         /*
    138          * look for boolean isFoo()
    139          */
    140
    141         if (!executor.isAlive()) {
    142             executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier);
    143         }
    144
    145         /*
    146          * if that didn't work, look for get("foo")
    147          */
    148
    149         if (!executor.isAlive()) {
    150             executor = new GetExecutor(rlog, introspector, claz, identifier);
    151         }
    152
    153         return (executor == null) ? null : new VelGetterImpl(executor);
    154     }
    155
    156     /***
    157      * {@inheritDoc}
    158      */
    159     public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) throws Exception {
    160         Class claz = obj.getClass();
    161
    162         VelMethod vm = null;
    163         try {
    164             /*
    165              * first, we introspect for the set<identifier> setter method
    166              */
    167
    168             Object[] params = {arg};
    169
    170             try {
    171                 vm = getMethod(obj, "set" + identifier, params, i);
    172
    173                 if (vm == null) {
    174                     throw new NoSuchMethodException();
    175                 }
    176             } catch (NoSuchMethodException nsme2) {
    177                 StringBuffer sb = new StringBuffer("set");
    178                 sb.append(identifier);
    179
    180                 if (Character.isLowerCase(sb.charAt(PROPERTY_START_INDEX))) {
    181                     sb.setCharAt(PROPERTY_START_INDEX, Character.toUpperCase(sb.charAt(PROPERTY_START_INDEX)));
    182                 } else {
    183                     sb.setCharAt(PROPERTY_START_INDEX, Character.toLowerCase(sb.charAt(PROPERTY_START_INDEX)));
    184                 }
    185
    186                 vm = getMethod(obj, sb.toString(), params, i);
    187
    188                 if (vm == null) {
    189                     throw new NoSuchMethodException();
    190                 }
    191             }
    192         } catch (NoSuchMethodException nsme) {
    193             /*
    194              * right now, we only support the Map interface
    195              */
    196
    197             if (Map.class.isAssignableFrom(claz)) {
    198                 Object[] params = {new Object(), new Object()};
    199
    200                 vm = getMethod(obj, "put", params, i);
    201
    202                 if (vm != null) {
    203                     return new VelSetterImpl(vm, identifier);
    204                 }
    205             }
    206         }
    207
    208         return (vm == null) ? null : new VelSetterImpl(vm);
    209     }
    210
    211     /***
    212      * An implementation of {@link VelMethod}.
    213      */
    214     public class VelMethodImpl implements VelMethod {
    215         /*** the method. */
    216         protected Method method = null;
    217         /***
    218          * Create a new instance.
    219          *
    220          * @param m the method.
    221          */
    222         public VelMethodImpl(Method m) {
    223             method = m;
    224         }
    225
    226         /***
    227          * {@inheritDoc}
    228          */
    229         public Object invoke(Object o, Object[] params) throws Exception {
    230             try {
    231                 return method.invoke(o, params);
    232             } catch (InvocationTargetException e) {
    233                 final Throwable t = e.getTargetException();
    234
    235                 if (t instanceof Exception) {
    236                     throw (Exception) t;
    237                 } else if (t instanceof Error) {
    238                     throw (Error) t;
    239                 } else {
    240                     throw e;
    241                 }
    242             }
    243         }
    244
    245         /***
    246          * {@inheritDoc}
    247          */
    248         public boolean isCacheable() {
    249             return true;
    250         }
    251
    252         /***
    253          * {@inheritDoc}
    254          */
    255         public String getMethodName() {
    256             return method.getName();
    257         }
    258
    259         /***
    260          * {@inheritDoc}
    261          */
    262         public Class getReturnType() {
    263             return method.getReturnType();
    264         }
    265     }
    266
    267     /***
    268      * {@inheritDoc}
    269      */
    270     public class VelGetterImpl implements VelPropertyGet {
    271         /*** executor for performing the get. */
    272         protected AbstractExecutor ae = null;
    273
    274         /***
    275          * Create the getter using an {@link AbstractExecutor} to
    276          * do the work.
    277          * @param exec the executor.
    278          */
    279         public VelGetterImpl(AbstractExecutor exec) {
    280             ae = exec;
    281         }
    282
    283         /***
    284          * {@inheritDoc}
    285          */
    286         public Object invoke(Object o) throws Exception {
    287             return ae.execute(o);
    288         }
    289
    290         /***
    291          * {@inheritDoc}
    292          */
    293         public boolean isCacheable() {
    294             return true;
    295         }
    296
    297         /***
    298          * {@inheritDoc}
    299          */
    300         public String getMethodName() {
    301             return ae.getMethod().getName();
    302         }
    303     }
    304
    305     /***
    306      * {@inheritDoc}
    307      */
    308     public class VelSetterImpl implements VelPropertySet {
    309         /*** the method to call. */
    310         protected VelMethod vm = null;
    311         /*** the key for indexed and other properties. */
    312         protected String putKey = null;
    313
    314         /***
    315          * Create an instance.
    316          * @param velmethod the method to call on set.
    317          */
    318         public VelSetterImpl(VelMethod velmethod) {
    319             this.vm = velmethod;
    320         }
    321
    322         /***
    323          * Create an instance.
    324          * @param velmethod the method to call on set.
    325          * @param key the index or other value passed to a
    326          *      setProperty(xxx, value) method.
    327          */
    328         public VelSetterImpl(VelMethod velmethod, String key) {
    329             this.vm = velmethod;
    330             putKey = key;
    331         }
    332
    333         /*** {@inheritDoc} */
    334         public Object invoke(Object o, Object value) throws Exception {
    335             ArrayList al = new ArrayList();
    336
    337             if (putKey == null) {
    338                 al.add(value);
    339             } else {
    340                 al.add(putKey);
    341                 al.add(value);
    342             }
    343
    344             return vm.invoke(o, al.toArray());
    345         }
    346
    347         /*** {@inheritDoc} */
    348         public boolean isCacheable() {
    349             return true;
    350         }
    351
    352         /*** {@inheritDoc} */
    353         public String getMethodName() {
    354             return vm.getMethodName();
    355         }
    356
    357     }
    358 }