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    }