001 package railo.runtime.reflection; 002 003 import java.lang.reflect.Constructor; 004 import java.lang.reflect.Field; 005 import java.lang.reflect.InvocationTargetException; 006 import java.lang.reflect.Method; 007 import java.lang.reflect.Modifier; 008 import java.util.ArrayList; 009 import java.util.Date; 010 import java.util.HashSet; 011 import java.util.Hashtable; 012 import java.util.List; 013 import java.util.Locale; 014 import java.util.Map; 015 import java.util.Set; 016 import java.util.TimeZone; 017 import java.util.Vector; 018 019 import railo.commons.io.res.Resource; 020 import railo.commons.lang.StringUtil; 021 import railo.commons.lang.types.RefInteger; 022 import railo.commons.lang.types.RefIntegerImpl; 023 import railo.runtime.exp.ApplicationException; 024 import railo.runtime.exp.ExpressionException; 025 import railo.runtime.exp.NativeException; 026 import railo.runtime.exp.PageException; 027 import railo.runtime.java.JavaObject; 028 import railo.runtime.op.Caster; 029 import railo.runtime.op.Decision; 030 import railo.runtime.op.Operator; 031 import railo.runtime.reflection.pairs.ConstructorInstance; 032 import railo.runtime.reflection.pairs.MethodInstance; 033 import railo.runtime.reflection.storage.WeakConstructorStorage; 034 import railo.runtime.reflection.storage.WeakFieldStorage; 035 import railo.runtime.reflection.storage.WeakMethodStorage; 036 import railo.runtime.type.Array; 037 import railo.runtime.type.Collection; 038 import railo.runtime.type.KeyImpl; 039 import railo.runtime.type.ObjectWrap; 040 import railo.runtime.type.Query; 041 import railo.runtime.type.Struct; 042 import railo.runtime.type.util.ArrayUtil; 043 import railo.runtime.type.util.Type; 044 045 /** 046 * Class to reflect on Objects and classes 047 */ 048 public final class Reflector { 049 050 051 private static final Object NULL = new Object(); 052 053 054 private static WeakConstructorStorage cStorage=new WeakConstructorStorage(); 055 private static WeakFieldStorage fStorage=new WeakFieldStorage(); 056 private static WeakMethodStorage mStorage=new WeakMethodStorage(); 057 058 /** 059 * check if Class is instanceof a a other Class 060 * @param srcClassName Class name to check 061 * @param trg is Class of? 062 * @return is Class Class of... 063 */ 064 public static boolean isInstaneOf(String srcClassName,Class trg) { 065 try { 066 return isInstaneOf(Class.forName(srcClassName),trg); 067 } 068 catch (ClassNotFoundException e) { 069 return false; 070 } 071 } 072 073 /** 074 * check if Class is instanceof a a other Class 075 * @param srcClassName Class name to check 076 * @param trgClassName is Class of? 077 * @return is Class Class of... 078 */ 079 public static boolean isInstaneOf(String srcClassName,String trgClassName) { 080 try { 081 return isInstaneOf(Class.forName(srcClassName),trgClassName); 082 } 083 catch (ClassNotFoundException e) { 084 return false; 085 } 086 } 087 088 /** 089 * check if Class is instanceof a a other Class 090 * @param src is Class of? 091 * @param trgClassName Class name to check 092 * @return is Class Class of... 093 */ 094 public static boolean isInstaneOf(Class src, String trgClassName) { 095 try { 096 return isInstaneOf(src,Class.forName(trgClassName)); 097 } 098 catch (ClassNotFoundException e) { 099 return false; 100 } 101 } 102 103 104 public static boolean isInstaneOfIgnoreCase(Class src ,String trg) { 105 if(src.isArray()) { 106 return isInstaneOfIgnoreCase(src.getComponentType() ,trg); 107 } 108 109 if(src.getName().equalsIgnoreCase(trg))return true; 110 111 // Interface 112 if(_checkInterfaces(src,trg)) { 113 return true; 114 } 115 // Extends 116 src=src.getSuperclass(); 117 if(src!=null) return isInstaneOfIgnoreCase(src, trg); 118 return false; 119 } 120 121 122 /** 123 * check if Class is instanceof a a other Class 124 * @param src Class to check 125 * @param trg is Class of? 126 * @return is Class Class of... 127 */ 128 public static boolean isInstaneOf(Class src ,Class trg) { 129 if(src.isArray() && trg.isArray()) { 130 return isInstaneOf(src.getComponentType() ,trg.getComponentType()); 131 } 132 133 if(src==trg)return true; 134 135 // Interface 136 if(trg.isInterface()) { 137 return _checkInterfaces(src,trg); 138 } 139 // Extends 140 141 while(src!=null) { 142 if(src==trg) return true; 143 src=src.getSuperclass(); 144 } 145 return trg==Object.class; 146 } 147 148 private static boolean _checkInterfaces(Class src, String trg) { 149 Class[] interfaces = src.getInterfaces(); 150 if(interfaces==null) return false; 151 for(int i=0;i<interfaces.length;i++) { 152 if(interfaces[i].getName().equalsIgnoreCase(trg))return true; 153 if(_checkInterfaces(interfaces[i],trg)) return true; 154 } 155 return false; 156 } 157 158 private static boolean _checkInterfaces(Class src, Class trg) { 159 Class[] interfaces = src.getInterfaces(); 160 if(interfaces==null) return false; 161 for(int i=0;i<interfaces.length;i++) { 162 if(interfaces[i]==trg)return true; 163 if(_checkInterfaces(interfaces[i],trg)) return true; 164 } 165 src=src.getSuperclass(); 166 if(src!=null) return _checkInterfaces(src,trg); 167 return false; 168 } 169 170 /** 171 * get all Classes from a Object Array 172 * @param objs Objects to get 173 * @return classes from Objects 174 */ 175 public static Class[] getClasses(Object[] objs) { 176 Class[] cls=new Class[objs.length]; 177 for(int i=0;i<objs.length;i++) { 178 if(objs[i]==null)cls[i]=Object.class; 179 else cls[i]=objs[i].getClass(); 180 } 181 return cls; 182 } 183 184 /** 185 * convert a primitive class Type to a Reference Type (Example: int -> java.lang.Integer) 186 * @param c Class to convert 187 * @return converted Class (if primitive) 188 */ 189 public static Class toReferenceClass(Class c) { 190 if(c.isPrimitive()) { 191 if(c==boolean.class) return Boolean.class; 192 if(c==byte.class) return Byte.class; 193 if(c==short.class) return Short.class; 194 if(c==char.class) return Character.class; 195 if(c==int.class) return Integer.class; 196 if(c==long.class) return Long.class; 197 if(c==float.class) return Float.class; 198 if(c==double.class) return Double.class; 199 } 200 return c; 201 } 202 203 /** 204 * creates a string list with class arguments in a displable form 205 * @param clazzArgs arguments to display 206 * @return list 207 */ 208 public static String getDspMethods(Class[] clazzArgs) { 209 StringBuffer sb=new StringBuffer(); 210 for(int i=0;i<clazzArgs.length;i++) { 211 if(i>0)sb.append(", "); 212 sb.append(Caster.toTypeName(clazzArgs[i])); 213 } 214 return sb.toString(); 215 } 216 217 /** 218 * checks if src Class is "like" trg class 219 * @param src Source Class 220 * @param trg Target Class 221 * @return is similar 222 */ 223 public static boolean like(Class src, Class trg) { 224 if(src==trg) return true; 225 return isInstaneOf(src,trg); 226 } 227 228 /** 229 * convert Object from src to trg Type, if possible 230 * @param src Object to convert 231 * @param srcClass Source Class 232 * @param trgClass Target Class 233 * @return converted Object 234 * @throws PageException 235 */ 236 public static Object convert(Object src, Class trgClass, RefInteger rating) throws PageException { 237 if(rating!=null) { 238 Object trg = _convert(src, trgClass); 239 if(src==trg) { 240 rating.plus(10); 241 return trg; 242 } 243 if(src==null || trg==null) { 244 rating.plus(0); 245 return trg; 246 } 247 if(isInstaneOf(src.getClass(), trg.getClass())) { 248 rating.plus(9); 249 return trg; 250 } 251 if(src.equals(trg)) { 252 rating.plus(8); 253 return trg; 254 } 255 256 // different number 257 boolean bothNumbers=src instanceof Number && trg instanceof Number; 258 if(bothNumbers && ((Number)src).doubleValue()==((Number)trg).doubleValue()) { 259 rating.plus(7); 260 return trg; 261 } 262 263 264 265 String sSrc=Caster.toString(src,null); 266 String sTrg=Caster.toString(trg,null); 267 if(sSrc!=null && sTrg!=null) { 268 269 // different number types 270 if(src instanceof Number && trg instanceof Number && sSrc.equals(sTrg)) { 271 rating.plus(6); 272 return trg; 273 } 274 275 // looks the same 276 if(sSrc.equals(sTrg)) { 277 rating.plus(5); 278 return trg; 279 } 280 if(sSrc.equalsIgnoreCase(sTrg)) { 281 rating.plus(4); 282 return trg; 283 } 284 } 285 286 // CF Equal 287 try { 288 if(Operator.equals(src, trg, false, true)) { 289 rating.plus(3); 290 return trg; 291 } 292 } catch (Throwable t) {} 293 294 295 return trg; 296 } 297 else 298 return _convert(src, trgClass); 299 } 300 301 public static Object _convert(Object src, Class trgClass) throws PageException { 302 if(src==null) { 303 if(trgClass.isPrimitive()) 304 throw new ApplicationException("can't convert [null] to ["+trgClass.getName()+"]"); 305 return null; 306 } 307 if(like(src.getClass(), trgClass)) return src; 308 String className=trgClass.getName(); 309 310 311 if(src instanceof ObjectWrap) { 312 src = ((ObjectWrap) src).getEmbededObject(); 313 return _convert(src, trgClass); 314 } 315 if(className.startsWith("java.lang.")){ 316 if(trgClass==Boolean.class) return Caster.toBoolean(src); 317 if(trgClass==Integer.class) return Caster.toInteger(src); 318 if(trgClass==String.class) return Caster.toString(src); 319 if(trgClass==Byte.class) return Caster.toByte(src); 320 if(trgClass==Short.class) return Caster.toShort(src); 321 if(trgClass==Long.class) return Caster.toLong(src); 322 if(trgClass==Float.class) return Caster.toFloat(src); 323 if(trgClass==Double.class) return Caster.toDouble(src); 324 if(trgClass==Character.class) { 325 String str=Caster.toString(src,null); 326 if(str!=null && str.length()==1) return new Character(str.charAt(0)); 327 } 328 } 329 330 if(Decision.isArray(src)) { 331 if(trgClass.isArray()) { 332 return toNativeArray(trgClass,src); 333 } 334 else if(isInstaneOf(trgClass, List.class)) { 335 return Caster.toList(src); 336 } 337 else if(isInstaneOf(trgClass, Array.class)) { 338 return Caster.toArray(src); 339 } 340 } 341 342 if(trgClass==Date.class) return Caster.toDate(src,true,null); 343 else if(trgClass==Query.class) return Caster.toQuery(src); 344 else if(trgClass==Map.class) return Caster.toMap(src); 345 else if(trgClass==Struct.class) return Caster.toStruct(src); 346 else if(trgClass==Resource.class) return Caster.toResource(src,false); 347 // this 2 method are used to support conversion that match neo src types 348 else if(trgClass==Hashtable.class) return Caster.toHashtable(src); 349 else if(trgClass==Vector.class) return Caster.toVetor(src); 350 else if(trgClass==java.util.Collection.class) return Caster.toJavaCollection(src); 351 else if(trgClass==TimeZone.class && Decision.isString(src)) return Caster.toTimeZone(Caster.toString(src)); 352 else if(trgClass==Collection.Key.class) return KeyImpl.toKey(src); 353 else if(trgClass==Locale.class && Decision.isString(src)) return Caster.toLocale(Caster.toString(src)); 354 if(trgClass.isPrimitive()) { 355 //return convert(src,srcClass,toReferenceClass(trgClass)); 356 return _convert(src,toReferenceClass(trgClass)); 357 } 358 throw new ApplicationException("can't convert ["+Caster.toClassName(src)+"] to ["+Caster.toClassName(trgClass)+"]"); 359 } 360 361 /** 362 * gets Constructor Instance matching given parameter 363 * @param clazz Clazz to Invoke 364 * @param args Matching args 365 * @return Matching ConstructorInstance 366 * @throws NoSuchMethodException 367 * @throws PageException 368 */ 369 370 public static ConstructorInstance getConstructorInstance(Class clazz, Object[] args) throws NoSuchMethodException { 371 ConstructorInstance ci=getConstructorInstance(clazz, args,null); 372 if(ci!=null) return ci; 373 throw new NoSuchMethodException("No matching Constructor for "+clazz.getName()+"("+getDspMethods(getClasses(args))+") found"); 374 } 375 376 public static ConstructorInstance getConstructorInstance(Class clazz, Object[] args, ConstructorInstance defaultValue) { 377 args=cleanArgs(args); 378 Constructor[] constructors=cStorage.getConstructors(clazz,args.length);//getConstructors(clazz); 379 if(constructors!=null) { 380 Class[] clazzArgs = getClasses(args); 381 // exact comparsion 382 outer:for(int i=0;i<constructors.length;i++) { 383 if(constructors[i]!=null) { 384 385 Class[] parameterTypes = constructors[i].getParameterTypes(); 386 for(int y=0;y<parameterTypes.length;y++) { 387 if(toReferenceClass(parameterTypes[y])!=clazzArgs[y]) continue outer; 388 } 389 return new ConstructorInstance(constructors[i],args); 390 } 391 } 392 // like comparsion 393 outer:for(int i=0;i<constructors.length;i++) { 394 if(constructors[i]!=null) { 395 Class[] parameterTypes = constructors[i].getParameterTypes(); 396 for(int y=0;y<parameterTypes.length;y++) { 397 if(!like(clazzArgs[y],toReferenceClass(parameterTypes[y]))) continue outer; 398 } 399 return new ConstructorInstance(constructors[i],args); 400 } 401 } 402 // convert comparsion 403 ConstructorInstance ci=null; 404 int _rating=0; 405 outer:for(int i=0;i<constructors.length;i++) { 406 if(constructors[i]!=null) { 407 RefInteger rating=(constructors.length>1)?new RefIntegerImpl(0):null; 408 Class[] parameterTypes = constructors[i].getParameterTypes(); 409 Object[] newArgs = new Object[args.length]; 410 for(int y=0;y<parameterTypes.length;y++) { 411 try { 412 newArgs[y]=convert(args[y],toReferenceClass(parameterTypes[y]),rating); 413 } catch (PageException e) { 414 continue outer; 415 } 416 } 417 if(ci==null || rating.toInt()>_rating) { 418 if(rating!=null)_rating=rating.toInt(); 419 ci=new ConstructorInstance(constructors[i],newArgs); 420 } 421 //return new ConstructorInstance(constructors[i],newArgs); 422 } 423 } 424 return ci; 425 } 426 return defaultValue; 427 //throw new NoSuchMethodException("No matching Constructor for "+clazz.getName()+"("+getDspMethods(getClasses(args))+") found"); 428 } 429 430 /** 431 * gets the MethodInstance matching given Parameter 432 * @param clazz Class Of the Method to get 433 * @param methodName Name of the Method to get 434 * @param args Arguments of the Method to get 435 * @return return Matching Method 436 * @throws 437 */ 438 public static MethodInstance getMethodInstanceEL(Class clazz, Collection.Key methodName, Object[] args) { 439 args=cleanArgs(args); 440 441 Method[] methods = mStorage.getMethods(clazz,methodName,args.length);//getDeclaredMethods(clazz); 442 443 if(methods!=null) { 444 Class[] clazzArgs = getClasses(args); 445 // exact comparsion 446 //print.e("exact:"+methodName); 447 outer:for(int i=0;i<methods.length;i++) { 448 if(methods[i]!=null) { 449 Class[] parameterTypes = methods[i].getParameterTypes(); 450 for(int y=0;y<parameterTypes.length;y++) { 451 if(toReferenceClass(parameterTypes[y])!=clazzArgs[y]) continue outer; 452 } 453 return new MethodInstance(methods[i],args); 454 } 455 } 456 // like comparsion 457 MethodInstance mi=null; 458 // print.e("like:"+methodName); 459 outer:for(int i=0;i<methods.length;i++) { 460 if(methods[i]!=null) { 461 Class[] parameterTypes = methods[i].getParameterTypes(); 462 for(int y=0;y<parameterTypes.length;y++) { 463 if(!like(clazzArgs[y],toReferenceClass(parameterTypes[y]))) continue outer; 464 } 465 return new MethodInstance(methods[i],args); 466 } 467 } 468 469 470 // convert comparsion 471 // print.e("convert:"+methodName); 472 mi=null; 473 int _rating=0; 474 outer:for(int i=0;i<methods.length;i++) { 475 if(methods[i]!=null) { 476 RefInteger rating=(methods.length>1)?new RefIntegerImpl(0):null; 477 Class[] parameterTypes = methods[i].getParameterTypes(); 478 Object[] newArgs = new Object[args.length]; 479 for(int y=0;y<parameterTypes.length;y++) { 480 try { 481 newArgs[y]=convert(args[y],toReferenceClass(parameterTypes[y]),rating); 482 } catch (PageException e) { 483 continue outer; 484 } 485 } 486 if(mi==null || rating.toInt()>_rating) { 487 if(rating!=null)_rating=rating.toInt(); 488 mi=new MethodInstance(methods[i],newArgs); 489 } 490 //return new MethodInstance(methods[i],newArgs); 491 } 492 }return mi; 493 } 494 return null; 495 } 496 497 /*private static MethodInstance create(MethodInstance existing, Method method, Object[] args) { 498 if(existing==null) return new MethodInstance(method,args); 499 Class[] exTypes = existing.getMethod().getParameterTypes(); 500 Class[] nwTypes = method.getParameterTypes(); 501 for(int i=0;i<exTypes.length;i++){ 502 if(exTypes[i]==nwTypes[i]) continue; 503 if(isInstaneOf(exTypes[i], nwTypes[i])){ 504 return existing; 505 } 506 if(isInstaneOf(nwTypes[i], exTypes[i])){ 507 return new MethodInstance(method,args); 508 } 509 } 510 return existing; 511 }*/ 512 513 514 /** 515 * gets the MethodInstance matching given Parameter 516 * @param clazz Class Of the Method to get 517 * @param methodName Name of the Method to get 518 * @param args Arguments of the Method to get 519 * @return return Matching Method 520 * @throws NoSuchMethodException 521 * @throws PageException 522 */ 523 public static MethodInstance getMethodInstance(Class clazz, String methodName, Object[] args) 524 throws NoSuchMethodException { 525 MethodInstance mi=getMethodInstanceEL(clazz, KeyImpl.getInstance(methodName), args); 526 if(mi!=null) return mi; 527 528 Class[] classes = getClasses(args); 529 StringBuilder sb=null; 530 JavaObject jo; 531 Class c; 532 ConstructorInstance ci; 533 for(int i=0;i<classes.length;i++){ 534 if(args[i] instanceof JavaObject) { 535 jo=(JavaObject) args[i]; 536 c=jo.getClazz(); 537 ci = Reflector.getConstructorInstance(c, new Object[0], null); 538 if(ci==null) { 539 540 throw new NoSuchMethodException("The "+pos(i+1)+" parameter of "+methodName+"("+getDspMethods(classes)+") ia a object created " + 541 "by the createObject function (JavaObject/JavaProxy). This object has not been instantiated because it does not have a constructor " + 542 "that takes zero arguments. Railo cannot instantiate it for you, please use the .init(...) method to instantiate it with the correct parameters first"); 543 544 545 } 546 } 547 } 548 /* 549 the argument list contains objects created by createObject, 550 that are no instantiated (first,third,10th) and because this object have no constructor taking no arguments, Railo cannot instantiate them. 551 you need first to instantiate this objects. 552 */ 553 throw new NoSuchMethodException("No matching Method for "+methodName+"("+getDspMethods(classes)+") found for "+ 554 Caster.toTypeName(clazz)); 555 } 556 557 private static String pos(int index) { 558 if(index==1) return "first"; 559 if(index==2) return "second"; 560 if(index==3) return "third"; 561 562 return index+"th"; 563 } 564 565 /** 566 * same like method getField from Class but ignore case from field name 567 * @param clazz class to search the field 568 * @param name name to search 569 * @return Matching Field 570 * @throws NoSuchFieldException 571 572 */ 573 public static Field[] getFieldsIgnoreCase(Class clazz, String name) throws NoSuchFieldException { 574 Field[] fields=fStorage.getFields(clazz,name); 575 if(fields!=null) return fields; 576 throw new NoSuchFieldException("there is no field with name "+name+" in object ["+Type.getName(clazz)+"]"); 577 } 578 579 public static Field[] getFieldsIgnoreCase(Class clazz, String name, Field[] defaultValue) { 580 Field[] fields=fStorage.getFields(clazz,name); 581 if(fields!=null) return fields; 582 return defaultValue; 583 584 585 } 586 587 public static String[] getPropertyKeys(Class clazz) { 588 Set keys=new HashSet(); 589 Field[] fields = clazz.getFields(); 590 Field field; 591 Method[] methods = clazz.getMethods(); 592 Method method; 593 String name; 594 595 for(int i=0;i<fields.length;i++) { 596 field=fields[i]; 597 if(Modifier.isPublic(field.getModifiers()) )keys.add(field.getName()); 598 } 599 600 for(int i=0;i<methods.length;i++) { 601 method=methods[i]; 602 if(Modifier.isPublic(method.getModifiers()) ) { 603 if(isGetter(method)) { 604 name=method.getName(); 605 if(name.startsWith("get"))keys.add(method.getName().substring(3)); 606 else keys.add(method.getName().substring(2)); 607 } 608 else if(isSetter(method)) keys.add(method.getName().substring(3)); 609 } 610 } 611 612 return (String[]) keys.toArray(new String[keys.size()]); 613 } 614 615 public static boolean hasPropertyIgnoreCase(Class clazz, String name) { 616 if(hasFieldIgnoreCase(clazz, name)) return true; 617 618 Method[] methods = clazz.getMethods(); 619 Method method; 620 String n; 621 for(int i=0;i<methods.length;i++) { 622 method=methods[i]; 623 if(Modifier.isPublic(method.getModifiers()) && StringUtil.endsWithIgnoreCase(method.getName(),name)) { 624 n=null; 625 if(isGetter(method)) { 626 n=method.getName(); 627 if(n.startsWith("get"))n=method.getName().substring(3); 628 else n=method.getName().substring(2); 629 } 630 else if(isSetter(method)) n=method.getName().substring(3); 631 if(n!=null && n.equalsIgnoreCase(name)) return true; 632 } 633 } 634 return false; 635 } 636 637 638 public static boolean hasFieldIgnoreCase(Class clazz, String name) { 639 return !ArrayUtil.isEmpty(getFieldsIgnoreCase(clazz, name, null)); 640 //getFieldIgnoreCaseEL(clazz, name)!=null; 641 } 642 643 644 /** 645 * call constructor of a class with matching arguments 646 * @param clazz Class to get Instance 647 * @param args Arguments for the Class 648 * @return invoked Instance 649 * @throws PageException 650 */ 651 public static Object callConstructor(Class clazz, Object[] args) throws PageException { 652 args=cleanArgs(args); 653 try { 654 return getConstructorInstance(clazz,args).invoke(); 655 } 656 catch (InvocationTargetException e) { 657 Throwable target = e.getTargetException(); 658 if(target instanceof PageException) throw (PageException)target; 659 throw Caster.toPageException(e.getTargetException()); 660 } 661 catch (Exception e) { 662 throw Caster.toPageException(e); 663 } 664 } 665 666 public static Object callConstructor(Class clazz, Object[] args, Object defaultValue) { 667 args=cleanArgs(args); 668 try { 669 ConstructorInstance ci = getConstructorInstance(clazz,args,null); 670 if(ci==null) return defaultValue; 671 return ci.invoke(); 672 } 673 catch (Throwable t) { 674 return defaultValue; 675 } 676 } 677 678 /** 679 * calls a Method of a Objct 680 * @param obj Object to call Method on it 681 * @param methodName Name of the Method to get 682 * @param args Arguments of the Method to get 683 * @return return return value of the called Method 684 * @throws PageException 685 */ 686 public static Object callMethod(Object obj, String methodName, Object[] args) throws PageException { 687 return callMethod(obj, KeyImpl.getInstance(methodName), args); 688 } 689 690 public static Object callMethod(Object obj, Collection.Key methodName, Object[] args) throws PageException { 691 if(obj==null) { 692 throw new ExpressionException("can't call method ["+methodName+"] on object, object is null"); 693 } 694 MethodInstance mi=getMethodInstanceEL(obj.getClass(), methodName, args); 695 if(mi==null) 696 throw throwCall(obj,methodName,args); 697 try { 698 699 return mi.invoke(obj); 700 } 701 catch (InvocationTargetException e) { 702 Throwable target = e.getTargetException(); 703 if(target instanceof PageException) throw (PageException)target; 704 throw new NativeException(e.getTargetException()); 705 } 706 catch (Exception e) { 707 throw new NativeException(e); 708 } 709 } 710 711 public static ExpressionException throwCall(Object obj,String methodName, Object[] args) { 712 return new ExpressionException("No matching Method/Function for "+Type.getName(obj)+"."+methodName+"("+getDspMethods(getClasses(args))+") found"); 713 } 714 public static ExpressionException throwCall(Object obj,Collection.Key methodName, Object[] args) { 715 return new ExpressionException("No matching Method/Function for "+Type.getName(obj)+"."+methodName+"("+getDspMethods(getClasses(args))+") found"); 716 } 717 718 /** 719 * calls a Static Method on the given CLass 720 * @param clazz Class to call Method on it 721 * @param methodName Name of the Method to get 722 * @param args Arguments of the Method to get 723 * @return return return value of the called Method 724 * @throws PageException 725 */ 726 public static Object callStaticMethod(Class clazz, String methodName, Object[] args) throws PageException { 727 try { 728 return getMethodInstance(clazz,methodName,args).invoke(null); 729 } 730 catch (InvocationTargetException e) { 731 Throwable target = e.getTargetException(); 732 if(target instanceof PageException) throw (PageException)target; 733 throw Caster.toPageException(e.getTargetException()); 734 } 735 catch (Exception e) { 736 throw Caster.toPageException(e); 737 } 738 } 739 740 /** 741 * to get a Getter Method of a Object 742 * @param clazz Class to invoke method from 743 * @param prop Name of the Method without get 744 * @return return Value of the getter Method 745 * @throws NoSuchMethodException 746 * @throws PageException 747 */ 748 public static MethodInstance getGetter(Class clazz, String prop) throws PageException, NoSuchMethodException { 749 String getterName = "get"+StringUtil.ucFirst(prop); 750 MethodInstance mi = getMethodInstanceEL(clazz,KeyImpl.getInstance(getterName),ArrayUtil.OBJECT_EMPTY); 751 752 if(mi==null){ 753 String isName = "is"+StringUtil.ucFirst(prop); 754 mi = getMethodInstanceEL(clazz,KeyImpl.getInstance(isName),ArrayUtil.OBJECT_EMPTY); 755 if(mi!=null){ 756 Method m = mi.getMethod(); 757 Class rtn = m.getReturnType(); 758 if(rtn!=Boolean.class && rtn!=boolean.class) mi=null; 759 } 760 } 761 762 763 if(mi==null) 764 throw new ExpressionException("No matching property ["+prop+"] found in ["+Caster.toTypeName(clazz)+"]"); 765 Method m=mi.getMethod(); 766 767 if(m.getReturnType()==void.class) 768 throw new NoSuchMethodException("invalid return Type, method ["+m.getName()+"] for Property ["+getterName+"] must have return type not void"); 769 770 return mi; 771 } 772 773 /** 774 * to get a Getter Method of a Object 775 * @param clazz Class to invoke method from 776 * @param prop Name of the Method without get 777 * @return return Value of the getter Method 778 */ 779 public static MethodInstance getGetterEL(Class clazz, String prop) { 780 prop="get"+StringUtil.ucFirst(prop); 781 MethodInstance mi = getMethodInstanceEL(clazz,KeyImpl.getInstance(prop),ArrayUtil.OBJECT_EMPTY); 782 if(mi==null) return null; 783 if(mi.getMethod().getReturnType()==void.class) return null; 784 return mi; 785 } 786 787 /** 788 * to invoke a getter Method of a Object 789 * @param obj Object to invoke method from 790 * @param prop Name of the Method without get 791 * @return return Value of the getter Method 792 * @throws PageException 793 */ 794 public static Object callGetter(Object obj, String prop) throws PageException { 795 try { 796 return getGetter(obj.getClass(), prop).invoke(obj); 797 } 798 catch (InvocationTargetException e) { 799 Throwable target = e.getTargetException(); 800 if(target instanceof PageException) throw (PageException)target; 801 throw Caster.toPageException(e.getTargetException()); 802 } 803 catch (Exception e) { 804 throw Caster.toPageException(e); 805 } 806 } 807 808 /** 809 * to invoke a setter Method of a Object 810 * @param obj Object to invoke method from 811 * @param prop Name of the Method without get 812 * @param value Value to set to the Method 813 * @return MethodInstance 814 * @throws NoSuchMethodException 815 * @throws PageException 816 */ 817 public static MethodInstance getSetter(Object obj, String prop,Object value) throws NoSuchMethodException { 818 prop="set"+StringUtil.ucFirst(prop); 819 MethodInstance mi = getMethodInstance(obj.getClass(),prop,new Object[]{value}); 820 Method m=mi.getMethod(); 821 822 if(m.getReturnType()!=void.class) 823 throw new NoSuchMethodException("invalid return Type, method ["+m.getName()+"] must have return type void, now ["+m.getReturnType().getName()+"]"); 824 return mi; 825 } 826 827 828 829 830 /** 831 * to invoke a setter Method of a Object 832 * @param obj Object to invoke method from 833 * @param prop Name of the Method without get 834 * @param value Value to set to the Method 835 * @return MethodInstance 836 * @deprecated use instead <code>getSetter(Object obj, String prop,Object value, MethodInstance defaultValue)</code> 837 */ 838 public static MethodInstance getSetterEL(Object obj, String prop,Object value) { 839 prop="set"+StringUtil.ucFirst(prop); 840 MethodInstance mi = getMethodInstanceEL(obj.getClass(),KeyImpl.getInstance(prop),new Object[]{value}); 841 if(mi==null) return null; 842 Method m=mi.getMethod(); 843 844 if(m.getReturnType()!=void.class) return null; 845 return mi; 846 } 847 848 /** 849 * to invoke a setter Method of a Object 850 * @param obj Object to invoke method from 851 * @param prop Name of the Method without get 852 * @param value Value to set to the Method 853 * @return MethodInstance 854 */ 855 public static MethodInstance getSetter(Object obj, String prop,Object value, MethodInstance defaultValue) { 856 prop="set"+StringUtil.ucFirst(prop); 857 MethodInstance mi = getMethodInstanceEL(obj.getClass(),KeyImpl.getInstance(prop),new Object[]{value}); 858 if(mi==null) return defaultValue; 859 Method m=mi.getMethod(); 860 861 if(m.getReturnType()!=void.class) return defaultValue; 862 return mi; 863 } 864 865 /** 866 * to invoke a setter Method of a Object 867 * @param obj Object to invoke method from 868 * @param prop Name of the Method without get 869 * @param value Value to set to the Method 870 * @throws PageException 871 */ 872 public static void callSetter(Object obj, String prop,Object value) throws PageException { 873 try { 874 getSetter(obj, prop, value).invoke(obj); 875 } 876 catch (InvocationTargetException e) { 877 Throwable target = e.getTargetException(); 878 if(target instanceof PageException) throw (PageException)target; 879 throw Caster.toPageException(e.getTargetException()); 880 } 881 catch (Exception e) { 882 throw Caster.toPageException(e); 883 } 884 } 885 886 /** 887 * do nothing when not exist 888 * @param obj 889 * @param prop 890 * @param value 891 * @throws PageException 892 */ 893 public static void callSetterEL(Object obj, String prop,Object value) throws PageException { 894 try { 895 MethodInstance setter = getSetterEL(obj, prop, value); 896 if(setter!=null)setter.invoke(obj); 897 } 898 catch (InvocationTargetException e) { 899 Throwable target = e.getTargetException(); 900 if(target instanceof PageException) throw (PageException)target; 901 throw Caster.toPageException(e.getTargetException()); 902 } 903 catch (Exception e) { 904 throw Caster.toPageException(e); 905 } 906 } 907 908 /** 909 * to get a visible Field of a object 910 * @param obj Object to invoke 911 * @param prop property to call 912 * @return property value 913 * @throws PageException 914 */ 915 public static Object getField(Object obj, String prop) throws PageException { 916 try { 917 return getFieldsIgnoreCase(obj.getClass(),prop)[0].get(obj); 918 } 919 catch (Throwable e) { 920 throw Caster.toPageException(e); 921 } 922 } 923 924 public static Object getField(Object obj, String prop, Object defaultValue) { 925 if(obj==null) return defaultValue; 926 Field[] fields = getFieldsIgnoreCase(obj.getClass(),prop,null); 927 if(ArrayUtil.isEmpty(fields)) return defaultValue; 928 929 try { 930 return fields[0].get(obj); 931 } catch (Throwable t) { 932 return defaultValue; 933 } 934 } 935 936 /** 937 * assign a value to a visible Field of a object 938 * @param obj Object to assign value to his property 939 * @param prop name of property 940 * @param value Value to assign 941 * @throws PageException 942 */ 943 public static boolean setField(Object obj, String prop,Object value) throws PageException { 944 Class clazz=value.getClass(); 945 try { 946 Field[] fields = getFieldsIgnoreCase(obj.getClass(),prop); 947 // exact comparsion 948 for(int i=0;i<fields.length;i++) { 949 if(toReferenceClass(fields[i].getType())==clazz){ 950 fields[i].set(obj,value); 951 return true; 952 } 953 } 954 // like comparsion 955 for(int i=0;i<fields.length;i++) { 956 if(like(fields[i].getType(),clazz)) { 957 fields[i].set(obj,value); 958 return true; 959 } 960 } 961 // convert comparsion 962 for(int i=0;i<fields.length;i++) { 963 try { 964 fields[i].set(obj,convert(value,toReferenceClass(fields[i].getType()),null)); 965 return true; 966 } catch (PageException e) {} 967 } 968 } 969 catch (Exception e) { 970 throw Caster.toPageException(e); 971 } 972 return false; 973 } 974 975 /** 976 * to get a visible Propety (Field or Getter) of a object 977 * @param obj Object to invoke 978 * @param prop property to call 979 * @return property value 980 * @throws PageException 981 */ 982 public static Object getProperty(Object obj, String prop) throws PageException { 983 Object rtn=getField(obj,prop,NULL);// NULL is used because the field can contain null as well 984 if(rtn!=NULL) return rtn; 985 986 char first=prop.charAt(0); 987 if(first>='0' && first<='9') throw new ApplicationException("there is no property with name ["+prop+"]"); 988 return callGetter(obj,prop); 989 990 } 991 992 /** 993 * to get a visible Propety (Field or Getter) of a object 994 * @param obj Object to invoke 995 * @param prop property to call 996 * @return property value 997 */ 998 public static Object getProperty(Object obj, String prop, Object defaultValue) { 999 1000 // first try field 1001 Field[] fields = getFieldsIgnoreCase(obj.getClass(),prop,null); 1002 if(!ArrayUtil.isEmpty(fields)) { 1003 try { 1004 return fields[0].get(obj); 1005 } catch (Throwable t) {} 1006 } 1007 1008 // then getter 1009 try { 1010 char first=prop.charAt(0); 1011 if(first>='0' && first<='9') return defaultValue; 1012 return getGetter(obj.getClass(), prop).invoke(obj); 1013 } catch (Throwable e1) { 1014 return defaultValue; 1015 } 1016 } 1017 1018 /** 1019 * assign a value to a visible Property (Field or Setter) of a object 1020 * @param obj Object to assign value to his property 1021 * @param prop name of property 1022 * @param value Value to assign 1023 * @throws PageException 1024 */ 1025 public static void setProperty(Object obj, String prop,Object value) throws PageException{ 1026 boolean done=false; 1027 try { 1028 if(setField(obj,prop,value))done=true; 1029 } 1030 catch (Throwable t) { 1031 1032 } 1033 if(!done)callSetter(obj,prop,value); 1034 } 1035 1036 /** 1037 * assign a value to a visible Property (Field or Setter) of a object 1038 * @param obj Object to assign value to his property 1039 * @param prop name of property 1040 * @param value Value to assign 1041 */ 1042 public static void setPropertyEL(Object obj, String prop,Object value) { 1043 1044 // first check for field 1045 Field[] fields = getFieldsIgnoreCase(obj.getClass(),prop,null); 1046 if(!ArrayUtil.isEmpty(fields)){ 1047 try { 1048 fields[0].set(obj,value); 1049 return; 1050 } catch (Throwable t) {} 1051 } 1052 1053 // then check for setter 1054 try { 1055 getSetter(obj, prop, value).invoke(obj); 1056 } 1057 catch (Throwable t) {} 1058 1059 } 1060 1061 /** 1062 * clean all Arguments 1063 * @param args arguments to clean 1064 * @return cleaned Arguments 1065 * @throws PageException 1066 */ 1067 private static Object[] cleanArgs(Object[] args) { 1068 if(args==null)return new Object[0]; 1069 for(int i=0;i<args.length;i++) { 1070 if(args[i] instanceof ObjectWrap) { 1071 try { 1072 args[i]=((ObjectWrap)args[i]).getEmbededObject(); 1073 } 1074 catch (PageException e) {} 1075 } 1076 } 1077 1078 return args; 1079 } 1080 1081 1082 private static Object toNativeArray(Class clazz, Object obj) throws PageException { 1083 if(obj.getClass()==clazz) return obj; 1084 Object[] objs=null; 1085 if(obj instanceof Array) objs=toRefArray((Array)obj); 1086 else if(obj instanceof List) objs=toRefArray((List)obj); 1087 else if(Decision.isNativeArray(obj)) { 1088 if(obj.getClass()==boolean[].class) objs=toRefArray((boolean[])obj); 1089 else if(obj.getClass()==byte[].class) objs=toRefArray((byte[])obj); 1090 else if(obj.getClass()==char[].class) objs=toRefArray((char[])obj); 1091 else if(obj.getClass()==short[].class) objs=toRefArray((short[])obj); 1092 else if(obj.getClass()==int[].class) objs=toRefArray((int[])obj); 1093 else if(obj.getClass()==long[].class) objs=toRefArray((long[])obj); 1094 else if(obj.getClass()==float[].class) objs=toRefArray((float[])obj); 1095 else if(obj.getClass()==double[].class) objs=toRefArray((double[])obj); 1096 else objs=(Object[]) obj; 1097 1098 } 1099 //if(objs==null) return defaultValue; 1100 //Class srcClass = objs.getClass().getComponentType(); 1101 Class trgClass = clazz.getComponentType(); 1102 Object rtn = java.lang.reflect.Array.newInstance(trgClass, objs.length); 1103 for(int i=0;i<objs.length;i++) { 1104 //java.lang.reflect.Array.set(rtn, i, convert(objs[i], srcClass, trgClass)); 1105 java.lang.reflect.Array.set(rtn, i, convert(objs[i], trgClass,null)); 1106 } 1107 return rtn; 1108 } 1109 1110 1111 private static Object[] toRefArray(boolean[] src) { 1112 Object[] trg=new Object[src.length]; 1113 for(int i=0;i<trg.length;i++) { 1114 trg[i]=src[i]?Boolean.TRUE:Boolean.FALSE; 1115 } 1116 return trg; 1117 } 1118 1119 private static Object[] toRefArray(byte[] src) { 1120 Object[] trg=new Object[src.length]; 1121 for(int i=0;i<trg.length;i++) { 1122 trg[i]=new Byte(src[i]); 1123 } 1124 return trg; 1125 } 1126 1127 private static Object[] toRefArray(char[] src) { 1128 Object[] trg=new Object[src.length]; 1129 for(int i=0;i<trg.length;i++) { 1130 trg[i]=new Character(src[i]); 1131 } 1132 return trg; 1133 } 1134 1135 private static Object[] toRefArray(short[] src) { 1136 Object[] trg=new Object[src.length]; 1137 for(int i=0;i<trg.length;i++) { 1138 trg[i]=Short.valueOf(src[i]); 1139 } 1140 return trg; 1141 } 1142 1143 private static Object[] toRefArray(int[] src) { 1144 Object[] trg=new Object[src.length]; 1145 for(int i=0;i<trg.length;i++) { 1146 trg[i]=Integer.valueOf(src[i]); 1147 } 1148 return trg; 1149 } 1150 1151 private static Object[] toRefArray(long[] src) { 1152 Object[] trg=new Object[src.length]; 1153 for(int i=0;i<trg.length;i++) { 1154 trg[i]=Long.valueOf(src[i]); 1155 } 1156 return trg; 1157 } 1158 1159 private static Object[] toRefArray(float[] src) { 1160 Object[] trg=new Object[src.length]; 1161 for(int i=0;i<trg.length;i++) { 1162 trg[i]=new Float(src[i]); 1163 } 1164 return trg; 1165 } 1166 1167 private static Object[] toRefArray(double[] src) { 1168 Object[] trg=new Object[src.length]; 1169 for(int i=0;i<trg.length;i++) { 1170 trg[i]=new Double(src[i]); 1171 } 1172 return trg; 1173 } 1174 1175 private static Object[] toRefArray(Array array) throws PageException { 1176 Object[] objs=new Object[array.size()]; 1177 for(int i=0;i<objs.length;i++) { 1178 objs[i]=array.getE(i+1); 1179 } 1180 return objs; 1181 } 1182 1183 private static Object[] toRefArray(List list) { 1184 Object[] objs=new Object[list.size()]; 1185 for(int i=0;i<objs.length;i++) { 1186 objs[i]=list.get(i); 1187 } 1188 return objs; 1189 } 1190 1191 public static boolean isGetter(Method method) { 1192 if(method.getParameterTypes().length>0) return false; 1193 if(method.getReturnType()==void.class) return false; 1194 if(!method.getName().startsWith("get") && !method.getName().startsWith("is")) return false; 1195 if(method.getDeclaringClass()==Object.class) return false; 1196 return true; 1197 } 1198 1199 public static boolean isSetter(Method method) { 1200 if(method.getParameterTypes().length!=1) return false; 1201 if(method.getReturnType()!=void.class) return false; 1202 if(!method.getName().startsWith("set")) return false; 1203 if(method.getDeclaringClass()==Object.class) return false; 1204 return true; 1205 } 1206 1207 /** 1208 * return all methods that are defined by the class itself (not extended) 1209 * @param clazz 1210 * @return 1211 */ 1212 public static Method[] getDeclaredMethods(Class clazz) { 1213 Method[] methods = clazz.getMethods(); 1214 ArrayList list=new ArrayList(); 1215 for(int i=0;i<methods.length;i++) { 1216 if(methods[i].getDeclaringClass()==clazz) list.add(methods[i]); 1217 } 1218 if(list.size()==0) return new Method[0]; 1219 return (Method[]) list.toArray(new Method[list.size()]); 1220 } 1221 1222 public static Method[] getSetters(Class clazz) { 1223 Method[] methods = clazz.getMethods(); 1224 ArrayList list=new ArrayList(); 1225 for(int i=0;i<methods.length;i++) { 1226 if(isSetter(methods[i])) list.add(methods[i]); 1227 } 1228 if(list.size()==0) return new Method[0]; 1229 return (Method[]) list.toArray(new Method[list.size()]); 1230 } 1231 1232 public static Method[] getGetters(Class clazz) { 1233 Method[] methods = clazz.getMethods(); 1234 ArrayList list=new ArrayList(); 1235 for(int i=0;i<methods.length;i++) { 1236 if(isGetter(methods[i])) list.add(methods[i]); 1237 } 1238 if(list.size()==0) return new Method[0]; 1239 return (Method[]) list.toArray(new Method[list.size()]); 1240 } 1241 1242 /** 1243 * check if given class "from" can be converted to class "to" without explicit casting 1244 * @param from source class 1245 * @param to target class 1246 * @return is it possible to convert from "from" to "to" 1247 */ 1248 public static boolean canConvert(Class from, Class to) { 1249 // Identity Conversions 1250 if(from==to) return true; 1251 1252 // Widening Primitive Conversion 1253 if(from==byte.class) { 1254 return to==short.class || to==int.class || to==long.class || to==float.class || to==double.class ; 1255 } 1256 if(from==short.class) { 1257 return to==int.class || to==long.class || to==float.class || to==double.class ; 1258 } 1259 if(from==char.class) { 1260 return to==int.class || to==long.class || to==float.class || to==double.class ; 1261 } 1262 if(from==int.class) { 1263 return to==long.class || to==float.class || to==double.class ; 1264 } 1265 if(from==long.class) { 1266 return to==float.class || to==double.class ; 1267 } 1268 if(from==float.class) { 1269 return to==double.class ; 1270 } 1271 return false; 1272 } 1273 1274 public static String removeGetterPrefix(String name) { 1275 if(name.startsWith("get")) return name.substring(3); 1276 if(name.startsWith("is")) return name.substring(2); 1277 return name; 1278 } 1279 1280 1281 1282 1283 }