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 */
017package org.apache.commons.jexl2.internal;
018import org.apache.commons.jexl2.internal.introspection.MethodKey;
019import org.apache.commons.jexl2.introspection.JexlMethod;
020import org.apache.commons.jexl2.introspection.JexlPropertySet;
021import org.apache.commons.jexl2.introspection.JexlPropertyGet;
022import 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 */
031public 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}