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