001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.reflection; 020 021import java.lang.reflect.Constructor; 022import java.lang.reflect.Field; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025 026import lucee.commons.lang.StringUtil; 027import lucee.runtime.exp.PageException; 028import lucee.runtime.op.Caster; 029import lucee.runtime.reflection.pairs.ConstructorParameterPair; 030import lucee.runtime.reflection.pairs.MethodParameterPair; 031import lucee.runtime.type.ObjectWrap; 032import lucee.runtime.type.util.ArrayUtil; 033 034/** 035 * To invoke a Object on different ways 036 */ 037public final class Invoker { 038 039 private static Method[] lastMethods; 040 private static Class lastClass; 041 042 043 /** 044 * @param clazz 045 * @param parameters 046 * @return new Instance 047 * @throws NoSuchMethodException 048 * @throws IllegalArgumentException 049 * @throws InstantiationException 050 * @throws IllegalAccessException 051 * @throws InvocationTargetException 052 */ 053 public static Object newInstance(Class clazz, Object[] parameters) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { 054 ConstructorParameterPair pair= 055 getConstructorParameterPairIgnoreCase(clazz, parameters); 056 return pair.getConstructor().newInstance(pair.getParameters()); 057 } 058 059 060 /** 061 * search the matching constructor to defined parameter list, also translate parameters for matching 062 * @param clazz class to get constructo from 063 * @param parameters parameter for the constructor 064 * @return Constructor parameter pair 065 * @throws NoSuchMethodException 066 */ 067 public static ConstructorParameterPair getConstructorParameterPairIgnoreCase(Class clazz, Object[] parameters) throws NoSuchMethodException { 068 // set all values 069 //Class objectClass=object.getClass(); 070 if(parameters==null)parameters=new Object[0]; 071 072 // set parameter classes 073 Class[] parameterClasses=new Class[parameters.length]; 074 for(int i=0;i<parameters.length;i++) { 075 parameterClasses[i]=parameters[i].getClass(); 076 } 077 078 // search right method 079 Constructor[] constructor=clazz.getConstructors(); 080 for(int mode=0;mode<2;mode++) { 081 outer: for(int i=0;i<constructor.length;i++) { 082 Constructor c=constructor[i]; 083 084 085 Class[] paramTrg=c.getParameterTypes(); 086 // Same Parameter count 087 if(parameterClasses.length==paramTrg.length) { 088 for(int y=0;y<parameterClasses.length;y++) { 089 if(mode==0 && parameterClasses[y]!=primitiveToWrapperType(paramTrg[y])) { 090 continue outer; 091 } 092 else if(mode==1) { 093 Object o=compareClasses(parameters[y], paramTrg[y]); 094 if(o==null)continue outer; 095 096 parameters[y]=o; 097 parameterClasses[y]=o.getClass(); 098 099 } 100 } 101 return new ConstructorParameterPair(c,parameters); 102 } 103 104 } 105 } 106 107 // Exeception 108 String parameter=""; 109 for(int i=0;i<parameterClasses.length;i++) { 110 if(i!=0) parameter+=", "; 111 parameter+=parameterClasses[i].getName(); 112 } 113 throw new NoSuchMethodException("class constructor "+clazz.getName()+"("+parameter+") doesn't exist"); 114 } 115 116 117 118 119 /** 120 * call of a method from given object 121 * @param object object to call method from 122 * @param methodName name of the method to call 123 * @param parameters parameter for method 124 * @return return value of the method 125 * @throws SecurityException 126 * @throws NoSuchMethodException 127 * @throws IllegalArgumentException 128 * @throws IllegalAccessException 129 * @throws InvocationTargetException 130 */ 131 public static Object callMethod(Object object, String methodName, Object[] parameters) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { 132 MethodParameterPair pair= 133 getMethodParameterPairIgnoreCase(object.getClass(), methodName, parameters); 134 135 136 return pair.getMethod().invoke(object,pair.getParameters()); 137 } 138 139 /** 140 * search the matching method to defined Method Name, also translate parameters for matching 141 * @param objectClass class object where searching method from 142 * @param methodName name of the method to search 143 * @param parameters whished parameter list 144 * @return pair with method matching and parameterlist matching 145 * @throws NoSuchMethodException 146 */ 147 public static MethodParameterPair getMethodParameterPairIgnoreCase(Class objectClass, String methodName, Object[] parameters) throws NoSuchMethodException { 148 // set all values 149 if(parameters==null)parameters=new Object[0]; 150 151 // set parameter classes 152 Class[] parameterClasses=new Class[parameters.length]; 153 for(int i=0;i<parameters.length;i++) { 154 parameterClasses[i]=parameters[i].getClass(); 155 } 156 157 // search right method 158 Method[] methods=null; 159 160 if(lastClass!=null && lastClass.equals(objectClass)) { 161 methods=lastMethods; 162 } 163 else { 164 methods=objectClass.getDeclaredMethods(); 165 } 166 167 lastClass=objectClass; 168 lastMethods=methods; 169 //Method[] methods=objectClass.getMethods(); 170 //Method[] methods=objectClass.getDeclaredMethods(); 171 172 //methods=objectClass.getDeclaredMethods(); 173 for(int mode=0;mode<2;mode++) { 174 outer: for(int i=0;i<methods.length;i++) { 175 Method method=methods[i]; 176 // Same Name 177 if(method.getName().equalsIgnoreCase(methodName)) { 178 Class[] paramTrg=method.getParameterTypes(); 179 // Same Parameter count 180 if(parameterClasses.length==paramTrg.length) { 181 //if(parameterClasses.length==0)return m; 182 for(int y=0;y<parameterClasses.length;y++) { 183 184 if(mode==0 && parameterClasses[y]!=primitiveToWrapperType(paramTrg[y])) { 185 continue outer; 186 } 187 else if(mode==1) { 188 Object o=compareClasses(parameters[y], paramTrg[y]); 189 190 if(o==null) { 191 continue outer; 192 } 193 parameters[y]=o; 194 parameterClasses[y]=o.getClass(); 195 196 } 197 //if(parameterClasses.length-1==y) return m; 198 } 199 200 return new MethodParameterPair(method,parameters); 201 } 202 } 203 } 204 } 205 206 // Exeception 207 String parameter=""; 208 for(int i=0;i<parameterClasses.length;i++) { 209 if(i!=0) parameter+=", "; 210 parameter+=parameterClasses[i].getName(); 211 } 212 throw new NoSuchMethodException("method "+methodName+"("+parameter+") doesn't exist in class "+objectClass.getName()); 213 214 } 215 216 /** 217 * compare parameter with whished parameter class and convert parameter to whished type 218 * @param parameter parameter to compare 219 * @param trgClass whished type of the parameter 220 * @return converted parameter (to whished type) or null 221 */ 222 private static Object compareClasses(Object parameter, Class trgClass) { 223 Class srcClass=parameter.getClass(); 224 trgClass=primitiveToWrapperType(trgClass); 225 try { 226 if(parameter instanceof ObjectWrap) 227 parameter=((ObjectWrap)parameter).getEmbededObject(); 228 229 // parameter is already ok 230 231 if(srcClass==trgClass) return parameter; 232 233 else if (instaceOf(srcClass,trgClass)) { 234 return parameter; 235 } 236 else if (trgClass.getName().equals("java.lang.String")) { 237 return Caster.toString(parameter); 238 } 239 else if (trgClass.getName().equals("java.lang.Boolean")) { 240 return Caster.toBoolean(parameter); 241 } 242 else if (trgClass.getName().equals("java.lang.Byte")) { 243 return new Byte( Caster.toString(parameter)); 244 } 245 else if (trgClass.getName().equals("java.lang.Character")) { 246 String str = Caster.toString(parameter); 247 if(str.length()==1) return new Character(str.toCharArray()[0]); 248 return null; 249 } 250 else if (trgClass.getName().equals("java.lang.Short")) { 251 return Short.valueOf((short) Caster.toIntValue(parameter)); 252 } 253 else if (trgClass.getName().equals("java.lang.Integer")) { 254 return Integer.valueOf(Caster.toIntValue(parameter)); 255 } 256 else if (trgClass.getName().equals("java.lang.Long")) { 257 return Long.valueOf((long)Caster.toDoubleValue(parameter)); 258 } 259 else if (trgClass.getName().equals("java.lang.Float")) { 260 return Float.valueOf((float)Caster.toDoubleValue(parameter)); 261 } 262 else if (trgClass.getName().equals("java.lang.Double")) { 263 return Caster.toDouble(parameter); 264 } 265 } 266 catch (PageException e) { 267 return null; 268 } 269 270 return null; 271 } 272 273 /** 274 * @param srcClass 275 * @param trgClass 276 * @return is instance of or not 277 */ 278 private static boolean instaceOf(Class srcClass, Class trgClass) { 279 while(srcClass!=null) { 280 if(srcClass==trgClass) return true; 281 srcClass=primitiveToWrapperType(srcClass.getSuperclass()); 282 } 283 return false; 284 } 285 286 287 /** 288 * cast a primitive type class definition to his object reference type 289 * @param clazz class object to check and convert if it is of primitive type 290 * @return object reference class object 291 */ 292 private static Class primitiveToWrapperType(Class clazz) { 293 // boolean, byte, char, short, int, long, float, and double 294 if(clazz==null) return null; 295 else if(clazz.isPrimitive()) { 296 if(clazz.getName().equals("boolean"))return Boolean.class; 297 else if(clazz.getName().equals("byte"))return Byte.class; 298 else if(clazz.getName().equals("char"))return Character.class; 299 else if(clazz.getName().equals("short"))return Short.class; 300 else if(clazz.getName().equals("int"))return Integer.class; 301 else if(clazz.getName().equals("long"))return Long.class; 302 else if(clazz.getName().equals("float"))return Float.class; 303 else if(clazz.getName().equals("double"))return Double.class; 304 } 305 return clazz; 306 } 307 308 /** 309 * to invoke a getter Method of a Object 310 * @param o Object to invoke method from 311 * @param prop Name of the Method without get 312 * @return return Value of the getter Method 313 * @throws SecurityException 314 * @throws NoSuchMethodException 315 * @throws IllegalArgumentException 316 * @throws IllegalAccessException 317 * @throws InvocationTargetException 318 */ 319 public static Object callGetter(Object o, String prop) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { 320 prop="get"+prop; 321 Class c=o.getClass(); 322 Method m=getMethodParameterPairIgnoreCase(c, prop, null).getMethod(); 323 324 //Method m=getMethodIgnoreCase(c,prop,null); 325 if(m.getReturnType().getName().equals("void")) 326 throw new NoSuchMethodException("invalid return Type, method ["+m.getName()+"] can't have return type void"); 327 return m.invoke(o,ArrayUtil.OBJECT_EMPTY); 328 } 329 330 /** 331 * to invoke a setter Method of a Object 332 * @param o Object to invoke method from 333 * @param prop Name of the Method without get 334 * @param value Value to set to the Method 335 * @throws SecurityException 336 * @throws NoSuchMethodException 337 * @throws IllegalArgumentException 338 * @throws IllegalAccessException 339 * @throws InvocationTargetException 340 */ 341 public static void callSetter(Object o, String prop,Object value) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { 342 prop="set"+StringUtil.ucFirst(prop); 343 Class c=o.getClass(); 344 //Class[] cArg=new Class[]{value.getClass()}; 345 Object[] oArg=new Object[]{value}; 346 MethodParameterPair mp = getMethodParameterPairIgnoreCase(c, prop, oArg); 347 //Method m=getMethodIgnoreCase(c,prop,cArg); 348 if(!mp.getMethod().getReturnType().getName().equals("void")) 349 throw new NoSuchMethodException("invalid return Type, method ["+mp.getMethod().getName()+"] must have return type void, now ["+mp.getMethod().getReturnType().getName()+"]"); 350 mp.getMethod().invoke(o,mp.getParameters()); 351 } 352 353 /** 354 * to get a visible Property of a object 355 * @param o Object to invoke 356 * @param prop property to call 357 * @return property value 358 * @throws NoSuchFieldException 359 * @throws IllegalArgumentException 360 * @throws IllegalAccessException 361 */ 362 public static Object getProperty(Object o, String prop) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 363 Field f=getFieldIgnoreCase(o.getClass(),prop); 364 return f.get(o); 365 } 366 367 /** 368 * assign a value to a visible property of a object 369 * @param o Object to assign value to his property 370 * @param prop name of property 371 * @param value Value to assign 372 * @throws IllegalArgumentException 373 * @throws IllegalAccessException 374 * @throws NoSuchFieldException 375 */ 376 public static void setProperty(Object o, String prop,Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException { 377 getFieldIgnoreCase(o.getClass(),prop).set(o,value); 378 } 379 380 /** 381 * same like method getField from Class but ignore case from field name 382 * @param c class to search the field 383 * @param name name to search 384 * @return Matching Field 385 * @throws NoSuchFieldException 386 */ 387 public static Field getFieldIgnoreCase(Class c, String name) throws NoSuchFieldException { 388 Field[] fields=c.getFields(); 389 for(int i=0;i<fields.length;i++) { 390 Field f=fields[i]; 391 // Same Name 392 if(f.getName().equalsIgnoreCase(name)) { 393 return f; 394 } 395 } 396 throw new NoSuchFieldException("Field doesn't exist"); 397 } 398 399 /** 400 * call of a static method of a Class 401 * @param staticClass class how contains method to invoke 402 * @param methodName method name to invoke 403 * @param values Arguments for the Method 404 * @return return value from method 405 * @throws PageException 406 */ 407 public static Object callStaticMethod(Class staticClass, String methodName, Object[] values) throws PageException { 408 if(values==null)values=new Object[0]; 409 410 411 412 413 MethodParameterPair mp; 414 try { 415 mp = getMethodParameterPairIgnoreCase(staticClass, methodName, values); 416 } 417 catch (NoSuchMethodException e) { 418 throw Caster.toPageException(e); 419 } 420 421 try { 422 return mp.getMethod().invoke(null,mp.getParameters()); 423 } 424 catch (InvocationTargetException e) { 425 Throwable target = e.getTargetException(); 426 if(target instanceof PageException) throw (PageException)target; 427 throw Caster.toPageException(e.getTargetException()); 428 } 429 catch (Exception e) { 430 throw Caster.toPageException(e); 431 } 432 } 433} 434