View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl2.internal;
18  import org.apache.commons.jexl2.internal.introspection.MethodKey;
19  import org.apache.commons.jexl2.introspection.JexlMethod;
20  import org.apache.commons.jexl2.introspection.JexlPropertySet;
21  import org.apache.commons.jexl2.introspection.JexlPropertyGet;
22  import java.lang.reflect.InvocationTargetException;
23  
24  /**
25   * Abstract class that is used to execute an arbitrary
26   * method that is introspected. This is the superclass
27   * for all other AbstractExecutor classes.
28   *
29   * @since 1.0
30   */
31  public abstract class AbstractExecutor {
32      /** A marker for invocation failures in tryInvoke. */
33      public static final Object TRY_FAILED = new Object() {
34          @Override
35          public String toString() {
36              return "tryExecute failed";
37          }
38      };
39  
40      /**
41       * A helper to initialize the marker methods (array.get, list.get, etc...).
42       * @param clazz the class to introspect
43       * @param name the name of the method
44       * @param parms the parameters
45       * @return the method
46       */
47      static java.lang.reflect.Method initMarker(Class<?> clazz, String name, Class<?>... parms) {
48          try {
49              return clazz.getMethod(name, parms);
50          } catch (Exception xnever) {
51              throw new Error(xnever);
52          }
53      }
54  
55      /**
56       * Creates an arguments array.
57       * @param args the list of arguments
58       * @return the arguments array
59       */
60      static Object[] makeArgs(Object... args) {
61          return args;
62      }
63  
64      /** The class this executor applies to. */
65      protected final Class<?> objectClass;
66      /** Method to be executed. */
67      protected final java.lang.reflect.Method method;
68  
69      /**
70       * Default and sole constructor.
71       * @param theClass the class this executor applies to
72       * @param theMethod the method held by this executor
73       */
74      protected AbstractExecutor(Class<?> theClass, java.lang.reflect.Method theMethod) {
75          objectClass = theClass;
76          method = theMethod;
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      public boolean equals(Object obj) {
82          return this == obj || (obj instanceof AbstractExecutor && equals((AbstractExecutor) obj));
83      }
84  
85      /** {@inheritDoc} */
86      @Override
87      public int hashCode() {
88          return method.hashCode();
89      }
90  
91      /**
92       *  Indicates whether some other executor is equivalent to this one.
93       * @param arg the other executor to check
94       * @return true if both executors are equivalent, false otherwise
95       */
96      public boolean equals(AbstractExecutor arg) {
97          // common equality check
98          if (!this.getClass().equals(arg.getClass())) {
99              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 }