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    }