    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.jexl2.internal;
    018    import org.apache.commons.jexl2.internal.introspection.MethodKey;
    019    import org.apache.commons.jexl2.introspection.JexlMethod;
    020    import org.apache.commons.jexl2.introspection.JexlPropertySet;
    021    import org.apache.commons.jexl2.introspection.JexlPropertyGet;
    022    import java.lang.reflect.InvocationTargetException;
    023
    024    /**
    025     * Abstract class that is used to execute an arbitrary
    026     * method that is introspected. This is the superclass
    027     * for all other AbstractExecutor classes.
    028     *
    029     * @since 1.0
    030     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
    031     * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
    032     * @version $Id: AbstractExecutor.java 889760 2009-12-11 18:42:49Z sebb $
    033     */
    034    public abstract class AbstractExecutor {
    035        /** A marker for invocation failures in tryInvoke. */
    036        public static final Object TRY_FAILED = new Object() {
    037            @Override
    038            public String toString() {
    039                return "tryExecute failed";
    040            }
    041        };
    042
    043        /**
    044         * A helper to initialize the marker methods (array.get, list.get, etc...).
    045         * @param clazz the class to introspect
    046         * @param name the name of the method
    047         * @param parms the parameters
    048         * @return the method
    049         */
    050        static java.lang.reflect.Method initMarker(Class<?> clazz, String name, Class<?>... parms) {
    051            try {
    052                return clazz.getMethod(name, parms);
    053            } catch (Exception xnever) {
    054                throw new Error(xnever);
    055            }
    056        }
    057
    058        /**
    059         * Creates an arguments array.
    060         * @param args the list of arguments
    061         * @return the arguments array
    062         */
    063        static Object[] makeArgs(Object... args) {
    064            return args;
    065        }
    066
    067        /** The class this executor applies to. */
    068        protected final Class<?> objectClass;
    069        /** Method to be executed. */
    070        protected final java.lang.reflect.Method method;
    071
    072        /**
    073         * Default and sole constructor.
    074         * @param theClass the class this executor applies to
    075         * @param theMethod the method held by this executor
    076         */
    077        protected AbstractExecutor(Class<?> theClass, java.lang.reflect.Method theMethod) {
    078            objectClass = theClass;
    079            method = theMethod;
    080        }
    081
    082        /** {@inheritDoc} */
    083        @Override
    084        public boolean equals(Object obj) {
    085            return this == obj || (obj instanceof AbstractExecutor && equals((AbstractExecutor) obj));
    086        }
    087
    088        /** {@inheritDoc} */
    089        @Override
    090        public int hashCode() {
    091            return method.hashCode();
    092        }
    093
    094        /**
    095         *  Indicates whether some other executor is equivalent to this one.
    096         * @param arg the other executor to check
    097         * @return true if both executors are equivalent, false otherwise
    098         */
    099        public boolean equals(AbstractExecutor arg) {
    100            // common equality check
    101            if (!this.getClass().equals(arg.getClass())) {
    102                return false;
    103            }
    104            if (!this.getMethod().equals(arg.getMethod())) {
    105                return false;
    106            }
    107            if (!this.getTargetClass().equals(arg.getTargetClass())) {
    108                return false;
    109            }
    110            // specific equality check
    111            Object lhsp = this.getTargetProperty();
    112            Object rhsp = arg.getTargetProperty();
    113            if (lhsp == null && rhsp == null) {
    114                return true;
    115            }
    116            if (lhsp != null && rhsp != null) {
    117                return lhsp.equals(rhsp);
    118            }
    119            return false;
    120        }
    121
    122        /**
    123         * Tell whether the executor is alive by looking
    124         * at the value of the method.
    125         *
    126         * @return boolean Whether the executor is alive.
    127         */
    128        public final boolean isAlive() {
    129            return (method != null);
    130        }
    131
    132        /**
    133         * Specifies if this executor is cacheable and able to be reused for this
    134         * class of object it was returned for.
    135         *
    136         * @return true if can be reused for this class, false if not
    137         */
    138        public boolean isCacheable() {
    139            return method != null;
    140        }
    141
    142        /**
    143         * Gets the method to be executed or used as a marker.
    144         * @return Method The method used by execute in derived classes.
    145         */
    146        public final java.lang.reflect.Method getMethod() {
    147            return method;
    148        }
    149
    150        /**
    151         * Gets the object class targeted by this executor.
    152         * @return the target object class
    153         */
    154        public final Class<?> getTargetClass() {
    155            return objectClass;
    156        }
    157
    158        /**
    159         * Gets the property targeted by this executor.
    160         * @return the target property
    161         */
    162        public Object getTargetProperty() {
    163            return null;
    164        }
    165
    166        /**
    167         * Gets the method name used.
    168         * @return method name
    169         */
    170        public final String getMethodName() {
    171            return method.getName();
    172        }
    173
    174
    175        /**
    176         * Checks whether a tryExecute failed or not.
    177         * @param exec the value returned by tryExecute
    178         * @return true if tryExecute failed, false otherwise
    179         */
    180        public final boolean tryFailed(Object exec) {
    181            return exec == TRY_FAILED;
    182        }
    183
    184        /**
    185         * Abstract class that is used to execute an arbitrary 'get' method.
    186         */
    187        public abstract static class Get extends AbstractExecutor implements JexlPropertyGet {
    188            /**
    189             * Default and sole constructor.
    190             * @param theClass the class this executor applies to
    191             * @param theMethod the method held by this executor
    192             */
    193            protected Get(Class<?> theClass, java.lang.reflect.Method theMethod) {
    194                super(theClass, theMethod);
    195            }
    196
    197            /** {@inheritDoc} */
    198            public final Object invoke(Object obj) throws Exception {
    199                return execute(obj);
    200            }
    201
    202            /** {@inheritDoc} */
    203            public final Object tryInvoke(Object obj, Object key) {
    204                return tryExecute(obj, key);
    205            }
    206
    207            /**
    208             * Gets the property value from an object.
    209             *
    210             * @param obj The object to get the property from.
    211             * @return The property value.
    212             * @throws IllegalAccessException Method is inaccessible.
    213             * @throws InvocationTargetException Method body throws an exception.
    214             */
    215            public abstract Object execute(Object obj)
    216                    throws IllegalAccessException, InvocationTargetException;
    217
    218            /**
    219             * Tries to reuse this executor, checking that it is compatible with
    220             * the actual set of arguments.
    221             * <p>Compatibility means that:
    222             * <code>o</code> must be of the same class as this executor's
    223             * target class and
    224             * <code>property</code> must be of the same class as this
    225             * executor's target property (for list and map based executors) and have the same
    226             * value (for other types).</p>
    227             * @param obj The object to get the property from.
    228             * @param key The property to get from the object.
    229             * @return The property value or TRY_FAILED if checking failed.
    230             */
    231            public Object tryExecute(Object obj, Object key) {
    232                return TRY_FAILED;
    233            }
    234        }
    235
    236        /**
    237         * Abstract class that is used to execute an arbitrary 'set' method.
    238         */
    239        public abstract static class Set extends AbstractExecutor implements JexlPropertySet {
    240            /**
    241             * Default and sole constructor.
    242             * @param theClass the class this executor applies to
    243             * @param theMethod the method held by this executor
    244             */
    245            protected Set(Class<?> theClass, java.lang.reflect.Method theMethod) {
    246                super(theClass, theMethod);
    247            }
    248
    249            /** {@inheritDoc} */
    250            public final Object invoke(Object obj, Object arg) throws Exception {
    251                return execute(obj, arg);
    252            }
    253
    254            /** {@inheritDoc} */
    255            public final Object tryInvoke(Object obj, Object key, Object value) {
    256                return tryExecute(obj, key, value);
    257            }
    258
    259            /**
    260             * Sets the property value of an object.
    261             *
    262             * @param obj The object to set the property in.
    263             * @param value The value.
    264             * @return The return value.
    265             * @throws IllegalAccessException Method is inaccessible.
    266             * @throws InvocationTargetException Method body throws an exception.
    267             */
    268            public abstract Object execute(Object obj, Object value)
    269                    throws IllegalAccessException, InvocationTargetException;
    270
    271            /**
    272             * Tries to reuse this executor, checking that it is compatible with
    273             * the actual set of arguments.
    274             * <p>Compatibility means that:
    275             * <code>o</code> must be of the same class as this executor's
    276             * target class,
    277             * <code>property</code> must be of the same class as this
    278             * executor's target property (for list and map based executors) and have the same
    279             * value (for other types)
    280             * and that <code>arg</code> must be a valid argument for this
    281             * executor underlying method.</p>
    282             * @param obj The object to invoke the method from.
    283             * @param key The property to set in the object.
    284             * @param value The value to use as the property value.
    285             * @return The return value or TRY_FAILED if checking failed.
    286             */
    287            public Object tryExecute(Object obj, Object key, Object value) {
    288                return TRY_FAILED;
    289            }
    290
    291        }
    292
    293
    294
    295        /**
    296         * Abstract class that is used to execute an arbitrary method.
    297         */
    298        public abstract static class Method extends AbstractExecutor implements JexlMethod {
    299            /**
    300             * A helper class to pass the method &amp; parameters.
    301             */
    302            protected static final class Parameter {
    303                /** The method. */
    304                private final java.lang.reflect.Method method;
    305                /** The method key. */
    306                private final MethodKey key;
    307                /** Creates an instance.
    308                 * @param m the method
    309                 * @param k the method key
    310                 */
    311                public Parameter(java.lang.reflect.Method m, MethodKey k) {
    312                    method = m;
    313                    key = k;
    314                }
    315            }
    316            /** The method key discovered from the arguments. */
    317            protected final MethodKey key;
    318            /**
    319             * Creates a new instance.
    320             * @param c the class this executor applies to
    321             * @param km the method and MethodKey to encapsulate.
    322             */
    323            protected Method(Class<?> c, Parameter km) {
    324                super(c, km.method);
    325                key = km.key;
    326            }
    327
    328            /** {@inheritDoc} */
    329            public final Object invoke(Object obj, Object[] params) throws Exception {
    330                return execute(obj, params);
    331            }
    332
    333            /** {@inheritDoc} */
    334            public final Object tryInvoke(String name, Object obj, Object[] params) {
    335                return tryExecute(name, obj, params);
    336            }
    337
    338            /** {@inheritDoc} */
    339            @Override
    340            public Object getTargetProperty() {
    341                return key;
    342            }
    343
    344            /**
    345             * Returns the return type of the method invoked.
    346             * @return return type
    347             */
    348            public final Class<?> getReturnType() {
    349                return method.getReturnType();
    350            }
    351
    352            /**
    353             * Invokes the method to be executed.
    354             *
    355             * @param obj the object to invoke the method upon
    356             * @param args the method arguments
    357             * @return the result of the method invocation
    358             * @throws IllegalAccessException Method is inaccessible.
    359             * @throws InvocationTargetException Method body throws an exception.
    360             */
    361            public abstract Object execute(Object obj, Object[] args)
    362                    throws IllegalAccessException, InvocationTargetException;
    363
    364            /**
    365             * Tries to reuse this executor, checking that it is compatible with
    366             * the actual set of arguments.
    367             * @param obj the object to invoke the method upon
    368             * @param name the method name
    369             * @param args the method arguments
    370             * @return the result of the method invocation or TRY_FAILED if checking failed.
    371             */
    372            public Object tryExecute(String name, Object obj, Object[] args){
    373                return TRY_FAILED;
    374            }
    375
    376        }
    377
    378    }