001    package railo.commons.lang;
002    
003    import java.io.File;
004    import java.io.IOException;
005    import java.io.InputStream;
006    import java.lang.reflect.Constructor;
007    import java.lang.reflect.Field;
008    import java.lang.reflect.InvocationTargetException;
009    import java.lang.reflect.Method;
010    import java.net.URL;
011    import java.net.URLClassLoader;
012    import java.util.Map;
013    import java.util.Set;
014    
015    import railo.commons.collections.HashTable;
016    import railo.commons.io.FileUtil;
017    import railo.commons.io.IOUtil;
018    import railo.runtime.PageContext;
019    import railo.runtime.PageContextImpl;
020    import railo.runtime.config.Config;
021    import railo.runtime.engine.ThreadLocalPageContext;
022    import railo.runtime.exp.PageException;
023    import railo.runtime.op.Caster;
024    import railo.runtime.type.Array;
025    import railo.runtime.type.util.ListUtil;
026    
027    
028    public final class ClassUtil {
029    
030            /**
031             * @param pc
032             * @param lcType
033             * @param type
034             * @return
035             * @throws ClassException 
036             * @throws PageException
037             */
038            public static Class toClass(String className) throws ClassException {
039                    className = className.trim();
040                    String lcClassName=className.toLowerCase();
041                    boolean isRef=false;
042                    if(lcClassName.startsWith("java.lang.")){
043                            lcClassName=lcClassName.substring(10);
044                            isRef=true;
045                    }
046    
047                    if(lcClassName.equals("boolean"))       {
048                            if(isRef) return Boolean.class;
049                            return boolean.class; 
050                    }
051                    if(lcClassName.equals("byte"))  {
052                            if(isRef) return Byte.class;
053                            return byte.class; 
054                    }
055                    if(lcClassName.equals("int"))   {
056                            return int.class; 
057                    }
058                    if(lcClassName.equals("long"))  {
059                            if(isRef) return Long.class;
060                            return long.class; 
061                    }
062                    if(lcClassName.equals("float")) {
063                            if(isRef) return Float.class;
064                            return float.class; 
065                    }
066                    if(lcClassName.equals("double"))        {
067                            if(isRef) return Double.class;
068                            return double.class; 
069                    }
070                    if(lcClassName.equals("char"))  {
071                            return char.class; 
072                    }
073                    if(lcClassName.equals("short")) {
074                            if(isRef) return Short.class;
075                            return short.class; 
076                    }
077                    
078                    if(lcClassName.equals("integer"))       return Integer.class; 
079                    if(lcClassName.equals("character"))     return Character.class; 
080                    if(lcClassName.equals("object"))        return Object.class; 
081                    if(lcClassName.equals("string"))        return String.class; 
082                    if(lcClassName.equals("null"))          return Object.class; 
083                    if(lcClassName.equals("numeric"))       return Double.class; 
084                    
085                    return ClassUtil.loadClass(className);
086            }
087            
088            
089            
090            /**
091             * loads a class from a String classname
092             * @param className
093             * @param defaultValue 
094             * @return matching Class
095             */
096            public static Class loadClass(String className, Class defaultValue) {
097                    return loadClass(null,className,defaultValue);
098            }
099    
100            /**
101             * loads a class from a String classname
102             * @param className
103             * @return matching Class
104             * @throws ClassException 
105             */
106            public static Class loadClass(String className) throws ClassException {
107                    Config config = ThreadLocalPageContext.getConfig();
108                    Class clazz = loadClass(config==null?null:config.getClassLoader(),className,null);
109                    if(clazz!=null) return clazz;
110                    throw new ClassException("cannot load class through its string name, because no definition for the class with the specified name ["+className+"] could be found");
111            }
112    
113            /**
114             * loads a class from a specified Classloader with given classname
115             * @param className
116             * @param cl 
117             * @return matching Class
118             */
119            public static Class loadClass(ClassLoader cl,String className, Class defaultValue) {
120                    
121                    if(cl==null){
122                            PageContextImpl pci = (PageContextImpl) ThreadLocalPageContext.get();
123                            if(pci!=null){
124                                    try {
125                                            cl=pci.getClassLoader();
126                                    }
127                                    catch (IOException e) {}
128                            }
129                            if(cl==null) {
130                                    Config config = ThreadLocalPageContext.getConfig();
131                                    if(config!=null)cl=config.getClassLoader();
132                            }
133                    }
134                    
135                    
136                    
137                    try {
138                            if(cl==null)return Class.forName(className.trim());
139                            return cl.loadClass(className.trim());
140                            
141                    }
142                    catch (ClassNotFoundException e) {
143                            try {
144                                    return Class.forName(className, false, cl);
145                            } 
146                            catch (ClassNotFoundException e1) {
147                                    if("boolean".equals(className)) return boolean.class;
148                                    if("char".equals(className)) return char.class;
149                                    if("float".equals(className)) return float.class;
150                                    if("short".equals(className)) return short.class;
151                                    if("int".equals(className)) return int.class;
152                                    if("long".equals(className)) return long.class;
153                                    if("double".equals(className)) return double.class;
154                                    
155                                    
156                                    return defaultValue;
157                            }
158                    }
159            }
160    
161            /**
162             * loads a class from a specified Classloader with given classname
163             * @param className
164             * @param cl 
165             * @return matching Class
166             * @throws ClassException 
167             */
168            public static Class loadClass(ClassLoader cl,String className) throws ClassException {
169                    Class clazz = loadClass(cl,className,null);
170                    if(clazz!=null) return clazz;
171                    throw new ClassException("cannot load class through its string name, because no definition for the class with the specified name ["+className+"] could be found");
172            }
173    
174            /**
175             * loads a class from a String classname
176             * @param clazz class to load
177             * @return matching Class
178             * @throws ClassException 
179             */
180            public static Object loadInstance(Class clazz) throws ClassException{
181                    try {
182                            return clazz.newInstance();
183                    }
184                    catch (InstantiationException e) {
185                            throw new ClassException("the specified class object ["+clazz.getName()+"()] cannot be instantiated");
186                    }
187                    catch (IllegalAccessException e) {
188                            throw new ClassException("can't load class because the currently executing method does not have access to the definition of the specified class");
189                    }
190            }
191    
192            public static Object loadInstance(String className) throws ClassException{
193                    return loadInstance(loadClass(className));
194            }
195            public static Object loadInstance(ClassLoader cl, String className) throws ClassException{
196                    return loadInstance(loadClass(cl,className));
197            }
198            
199            /**
200             * loads a class from a String classname
201             * @param clazz class to load
202             * @return matching Class
203             */
204            public static Object loadInstance(Class clazz, Object defaultValue){
205                    try {
206                            return clazz.newInstance();
207                    }
208                    catch (Throwable t) {
209                            return defaultValue;
210                    }
211            }
212            
213            public static Object loadInstance(String className, Object deaultValue){
214                    Class clazz = loadClass(className,null);
215                    if(clazz==null) return deaultValue;
216                    return loadInstance(clazz,deaultValue);
217            }
218            
219            public static Object loadInstance(ClassLoader cl, String className, Object deaultValue) {
220                    Class clazz = loadClass(cl,className,null);
221                    if(clazz==null) return deaultValue;
222                    return loadInstance(clazz,deaultValue);
223            }
224            
225            /**
226             * loads a class from a String classname
227             * @param clazz class to load
228             * @param args 
229             * @return matching Class
230             * @throws ClassException 
231             * @throws ClassException 
232             * @throws InvocationTargetException 
233             */
234            public static Object loadInstance(Class clazz, Object[] args) throws ClassException, InvocationTargetException {
235                    if(args==null || args.length==0) return loadInstance(clazz);
236                    
237                    Class[] cArgs=new Class[args.length];
238                    for(int i=0;i<args.length;i++) {
239                            cArgs[i]=args[i].getClass();
240                    }
241                    
242                    try {
243                            Constructor c = clazz.getConstructor(cArgs);
244                            return c.newInstance(args);
245                            
246                    }
247                    catch (SecurityException e) {
248                            throw new ClassException("there is a security violation (throwed by security manager)");
249                    }
250                    catch (NoSuchMethodException e) {
251                            
252                            StringBuilder sb=new StringBuilder(clazz.getName());
253                            char del='(';
254                            for(int i=0;i<cArgs.length;i++) {
255                                    sb.append(del);
256                                    sb.append(cArgs[i].getName());
257                                    del=',';
258                            }
259                            sb.append(')');
260                            
261                            throw new ClassException("there is no constructor with this ["+sb+"] signature for the class ["+clazz.getName()+"]");
262                    }
263                    catch (IllegalArgumentException e) {
264                            throw new ClassException("has been passed an illegal or inappropriate argument");
265                    }
266                    catch (InstantiationException e) {
267                            throw new ClassException("the specified class object ["+clazz.getName()+"] cannot be instantiated because it is an interface or is an abstract class");
268                    }
269                    catch (IllegalAccessException e) {
270                            throw new ClassException("can't load class because the currently executing method does not have access to the definition of the specified class");
271                    }
272            }
273    
274            public static Object loadInstance(String className, Object[] args) throws ClassException, InvocationTargetException{
275                    return loadInstance(loadClass(className),args);
276            }
277            
278            public static Object loadInstance(ClassLoader cl, String className, Object[] args) throws ClassException, InvocationTargetException{
279                    return loadInstance(loadClass(cl,className),args);
280            }
281            
282            /**
283             * loads a class from a String classname
284             * @param clazz class to load
285             * @param args 
286             * @return matching Class
287             */
288            public static Object loadInstance(Class clazz, Object[] args, Object defaultValue) {
289                    if(args==null || args.length==0) return loadInstance(clazz,defaultValue);
290                    try {
291                            Class[] cArgs=new Class[args.length];
292                            for(int i=0;i<args.length;i++) {
293                                    if(args[i]==null)cArgs[i]=Object.class;
294                                    else cArgs[i]=args[i].getClass();
295                            }
296                            Constructor c = clazz.getConstructor(cArgs);
297                            return c.newInstance(args);
298                            
299                    }
300                    catch (Throwable t) {//print.printST(t);
301                            return defaultValue;
302                    }
303                    
304            }
305            
306            public static Object loadInstance(String className, Object[] args, Object deaultValue){
307                    Class clazz = loadClass(className,null);
308                    if(clazz==null) return deaultValue;
309                    return loadInstance(clazz,args,deaultValue);
310            }
311            
312            public static Object loadInstance(ClassLoader cl, String className, Object[] args, Object deaultValue) {
313                    Class clazz = loadClass(cl,className,null);
314                    if(clazz==null) return deaultValue;
315                    return loadInstance(clazz,args,deaultValue);
316            }
317            
318            /**
319             * @return returns a string array of all pathes in classpath
320             */
321            public static String[] getClassPath(Config config) {
322    
323            Map pathes=new HashTable();
324                    String pathSeperator=System.getProperty("path.separator");
325                    if(pathSeperator==null)pathSeperator=";";
326                            
327            // pathes from system properties
328                    String strPathes=System.getProperty("java.class.path");
329                    if(strPathes!=null) {
330                            Array arr=ListUtil.listToArrayRemoveEmpty(strPathes,pathSeperator);
331                            int len=arr.size();
332                            for(int i=1;i<=len;i++) {
333                                    File file=FileUtil.toFile(Caster.toString(arr.get(i,""),"").trim());
334                                    if(file.exists())
335                                            try {
336                                                    pathes.put(file.getCanonicalPath(),"");
337                                            } catch (IOException e) {}
338                            }
339                    }
340                    
341                    
342            // pathes from url class Loader (dynamic loaded classes)
343                    getClassPathesFromLoader(new ClassUtil().getClass().getClassLoader(), pathes);
344                    getClassPathesFromLoader(config.getClassLoader(), pathes);
345                    
346                    Set set = pathes.keySet();
347                    return (String[]) set.toArray(new String[set.size()]);
348            }
349            
350            /**
351             * get class pathes from all url ClassLoaders
352             * @param ucl URL Class Loader
353             * @param pathes Hashmap with allpathes
354             */
355            private static void getClassPathesFromLoader(ClassLoader cl, Map pathes) {
356                    if(cl instanceof URLClassLoader) 
357                            _getClassPathesFromLoader((URLClassLoader) cl, pathes);
358            }
359                    
360            
361            private static void _getClassPathesFromLoader(URLClassLoader ucl, Map pathes) {
362                    getClassPathesFromLoader(ucl.getParent(), pathes);
363                    
364                    // get all pathes
365                    URL[] urls=ucl.getURLs();
366                    
367                    for(int i=0;i<urls.length;i++) {
368                            File file=FileUtil.toFile(urls[i].getPath());
369                            if(file.exists())
370                                    try {
371                                            pathes.put(file.getCanonicalPath(),"");
372                                    } catch (IOException e) {}
373                    }
374            }
375            
376            // CafeBabe (Java Magic Number)
377            private static final int ICA=202;//CA 
378        private static final int IFE=254;//FE
379        private static final int IBA=186;//BA
380        private static final int IBE=190;//BE
381        
382        // CF33 (Railo Magic Number)
383        private static final int ICF=207;//CF 
384        private static final int I33=51;//33
385        
386    
387            private static final byte BCA=(byte)ICA;//CA 
388        private static final byte BFE=(byte)IFE;//FE
389        private static final byte BBA=(byte)IBA;//BA
390        private static final byte BBE=(byte)IBE;//BE
391        
392        private static final byte BCF=(byte)ICF;//CF 
393        private static final byte B33=(byte)I33;//33
394        
395        
396        /** 
397         * check if given stream is a bytecode stream, if yes remove bytecode mark 
398         * @param is 
399         * @return is bytecode stream 
400         * @throws IOException 
401         */ 
402        public static boolean isBytecode(InputStream is) throws IOException { 
403                if(!is.markSupported()) 
404                        throw new IOException("can only read input streams that support mark/reset"); 
405                is.mark(-1); 
406                //print(bytes);
407                int first=is.read();
408                int second=is.read();
409                 boolean rtn=(first==ICF && second==I33) || (first==ICA && second==IFE && is.read()==IBA && is.read()==IBE);
410                
411            is.reset(); 
412            return rtn; 
413        }
414        
415    
416        public static boolean isBytecode(byte[] barr){ 
417            if(barr.length<4) return false;
418            return (barr[0]==BCF && barr[1]==B33) || (barr[0]==BCA && barr[1]==BFE && barr[2]==BBA && barr[3]==BBE); 
419        }
420        public static boolean isRawBytecode(byte[] barr){ 
421            if(barr.length<4) return false;
422            return (barr[0]==BCA && barr[1]==BFE && barr[2]==BBA && barr[3]==BBE); 
423        }
424        
425        public static boolean hasCF33Prefix(byte[] barr) { 
426            if(barr.length<4) return false;
427            return (barr[0]==BCF && barr[1]==B33); 
428        }
429        
430            public static byte[] removeCF33Prefix(byte[] barr) {
431                    if(!hasCF33Prefix(barr)) return barr;
432            
433                    byte[] dest = new byte[barr.length-10];
434                    System.arraycopy(barr, 10, dest, 0, 10);
435                    return dest;
436            }
437    
438            public static String getName(Class clazz) {
439                    if(clazz.isArray()){
440                            return getName(clazz.getComponentType())+"[]";
441                    }
442                    
443                    return clazz.getName();
444            }
445    
446            public static Method getMethodIgnoreCase(Class clazz, String methodName, Class[] args) throws ClassException {
447                    Method[] methods = clazz.getMethods();
448                    Method method;
449                    Class[] params;
450                    outer:for(int i=0;i<methods.length;i++){
451                            method=methods[i];
452                            if(method.getName().equalsIgnoreCase(methodName)){
453                                    params = method.getParameterTypes();
454                                    if(params.length==args.length){
455                                            for(int y=0;y<params.length;y++){
456                                                    if(!params[y].equals(args[y])){
457                                                            continue outer;
458                                                    }
459                                            }
460                                            return method;
461                                    }
462                            }
463                    }
464                    
465                    throw new ClassException("class "+clazz.getName()+" has no method with name "+methodName);
466            }
467    
468            
469            /**
470             * return all field names as String array
471             * @param clazz class to get field names from
472             * @return field names
473             */
474            public static String[] getFieldNames(Class clazz) {
475                    Field[] fields = clazz.getFields();
476                    String[] names=new String[fields.length];
477                    for(int i=0;i<names.length;i++){
478                            names[i]=fields[i].getName();
479                    }
480                    return names;
481            }
482    
483            public static byte[] toBytes(Class clazz) throws IOException {
484                    return IOUtil.toBytes(clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.','/')+".class"),true);
485            }
486    
487            /**
488             * return a array class based on the given class (opposite from Class.getComponentType())
489             * @param clazz
490             * @return
491             */
492            public static Class toArrayClass(Class clazz) {
493                    return java.lang.reflect.Array.newInstance(clazz, 0).getClass();
494            }
495    
496    
497    
498            public static Class<?> toComponentType(Class<?> clazz) {
499                    Class<?> tmp;
500                    while(true){
501                            tmp=clazz.getComponentType();
502                            if(tmp==null) break;
503                            clazz=tmp;
504                    }
505                    return clazz;
506            }
507    }