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