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