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 & 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 }