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 }