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 }