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