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