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    }