Initial Commit
[packages] / xemacs-packages / jde / java / src / jde / util / ClassInfo.java
1 /*
2  *    ClassInfo.java
3  *    Copyright (C) 2002, 2003 Rodrigo Reyes (reyes@chez.com),
4  *                       Javier Lopez (jslopez@forumsys.com), 
5  *                       Petter M\81åhl\81én (petter.mahlen@chello.se)
6  *                       Paul Kinnucan (pkinnucan@attbi.com)
7  *
8  *    $Revision: 1.1 $
9  *
10  *    This program is free software; you can redistribute it and/or modify
11  *    it under the terms of the GNU General Public License as published by
12  *    the Free Software Foundation; either version 2 of the License, or
13  *    (at your option) any later version.
14  *
15  *    This program is distributed in the hope that it will be useful,
16  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *    GNU General Public License for more details.
19  *
20  *    You should have received a copy of the GNU General Public License
21  *    along with this program; if not, write to the Free Software
22  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 package jde.util;
26
27 import java.io.*;
28 import java.lang.reflect.*;
29 import jde.util.DynamicClassLoader;
30
31 /**
32  * This class provides information about classes on jde-global-classpath.
33  *
34  * @author Rodrigo Reyes (reyes@chez.com),
35  * @author Javier Lopez (jslopez@forumsys.com)
36  * @author Petter M\81åhl\81én (petter.mahlen@chello.se)
37  * @author Paul Kinnucan (pkinnucan@attbi.com) 
38  */
39
40 public class ClassInfo {
41
42   /*************************************************************************
43    * Constants
44    *************************************************************************/
45   public static final String NIL = "nil";
46   public static final String NL = "\n";
47   public static final String T = "t";
48   public static final String LIST = "list";
49   public static final String START_PAREN = "(";
50   public static final String END_PAREN = ")";
51   public static final String DOUBLE_QUOTE = "\"";
52   public static final String SPACE = " ";
53   public static final String START_LIST;
54
55   static {
56     StringBuffer sb = new StringBuffer (10);
57     sb.append(START_PAREN);
58     sb.append(LIST);
59     sb.append(SPACE);
60     START_LIST = sb.toString();
61   }
62     
63   /**
64    * Access level for a class member
65    */
66   public static final int PUBLIC    = 0;
67     
68   /**
69    * Access level for a class member
70    */
71   public static final int PROTECTED = 1;
72     
73   /**
74    * Access level for a class member
75    */
76   public static final int PACKAGE   = 2;
77     
78   /**
79    * Access level for a class member
80    */
81   public static final int PRIVATE   = 3;
82
83   public static final int FIELD_INFO = 0;
84   public static final int CTOR_INFO = 1;
85   public static final int METHOD_INFO = 2;
86   public static final int INNER_CLASS_INFO = 3;
87
88
89
90   /**
91    * Tests whether a class is an ancestor of another class.
92    * This method prints "t" to standard out if the class is an ancestor
93    * of the other class. If the class is not an ancestor or either class
94    * cannot be found, this method prints nil to standard out.
95    * @param ancestor Name of the ancestor class
96    * @param child  Name of the supposed child class
97    */
98   public static void isAncestorOf(String ancestor, String child) {
99     try {
100       Class classAncestor = Class.forName( ancestor );
101       Class classChild = Class.forName( child );
102       if( classAncestor.isAssignableFrom( classChild ) )
103           System.out.println(T);
104       else
105         System.out.println(NIL);
106     } catch( Exception ex) {
107       System.out.println(NIL);
108     }
109   }
110     
111   /**
112    * Returns true if the entity is accessible to the specified level of
113    * protection.
114    * @param modifiers the modifiers as returned by Member.getModifiers()
115    * or Class.getModifiers() 
116    * @param level the level of protection
117    * @return if the Member is accessible to the specified level of
118    * protection.
119    * @see Member.getModifiers()
120    * @see Class.getModifiers()
121    */
122   private static boolean isAccessible(int modifiers, int level) {
123     switch(level) {
124     case PUBLIC:    // member is accessible if it has public access
125       return  Modifier.isPublic    (modifiers);
126     case PROTECTED: // accessible if member is protected
127       return  Modifier.isProtected (modifiers);
128     case PACKAGE: // accessible if member is not public, protected
129       // or private
130       return (!Modifier.isPublic   (modifiers) &&
131               !Modifier.isProtected(modifiers) &&
132               !Modifier.isPrivate  (modifiers));
133     case PRIVATE:   // accessible if member is private
134       return  Modifier.isPrivate   (modifiers);
135     default:
136       // cannot get here any more, since the access level is
137       // only used internally
138       throw new Error("Completion.isAccessible(int, int) " +
139                       "called with incorrect access level parameter:"
140                       + level);
141     }//switch
142   }
143
144   private static String accessLevel(int modifiers) {
145         String level;
146         if (Modifier.isPublic(modifiers)) {
147           level = String.valueOf(PUBLIC);
148         } else if (Modifier.isProtected(modifiers)) {
149           level = String.valueOf(PROTECTED);      
150         } else if (Modifier.isPrivate(modifiers)) {
151           level = String.valueOf(PRIVATE);
152         } else {
153           level = String.valueOf(PACKAGE);
154         }
155         
156         return level;
157   }
158         
159   private static StringBuffer listModifiers(int modifiers) {
160         StringBuffer sb = new StringBuffer (30);
161
162         if (Modifier.isAbstract(modifiers)) {
163           sb.append("\"abstract\"");
164           sb.append(SPACE);       
165         }
166
167         if (Modifier.isFinal(modifiers)) {
168           sb.append("\"final\"");
169           sb.append(SPACE);       
170         }
171
172         if (Modifier.isInterface(modifiers)) {
173           sb.append("\"interface\"");
174           sb.append(SPACE);       
175         }
176
177         if (Modifier.isNative(modifiers)) {
178           sb.append("\"native\"");
179           sb.append(SPACE);       
180         }
181
182         if (Modifier.isPrivate(modifiers)) {
183           sb.append("\"private\"");
184           sb.append(SPACE);       
185         }
186
187         if (Modifier.isProtected(modifiers)) {
188           sb.append("\"protected\"");
189           sb.append(SPACE);       
190         }
191
192         if (Modifier.isPublic(modifiers)) {
193           sb.append("\"public\"");
194           sb.append(SPACE);       
195         }
196
197         if (Modifier.isStatic(modifiers)) {
198           sb.append("\"static\"");
199           sb.append(SPACE);       
200         }
201
202         if (Modifier.isStrict(modifiers)) {
203           sb.append("\"strict\"");
204           sb.append(SPACE);       
205         }
206
207         if (Modifier.isSynchronized(modifiers)) {
208           sb.append("\"synchronized\"");
209           sb.append(SPACE);       
210         }
211
212         if (Modifier.isTransient(modifiers)) {
213           sb.append("\"transient\"");
214           sb.append(SPACE);       
215         }
216
217         if (Modifier.isVolatile(modifiers)) {
218           sb.append("\"volotile\"");
219           sb.append(SPACE);       
220         }
221
222         if (sb.length() > 0) {
223           StringBuffer temp = new StringBuffer(sb.length() + 26);
224           temp.append("(cons 'typemodifiers (list ");
225           temp.append(sb);
226           temp.append("))");
227           sb = temp;
228         }
229         
230         return sb;
231         
232   }
233
234   private static StringBuffer listExceptions(Class[] classes) {
235         StringBuffer sb = new StringBuffer (30);
236                 
237         sb.append("(cons 'throws  (list ");
238         for (int i = 0; i < classes.length; i++) {
239           sb.append(printWithinQuotes(className(classes[i])));
240           if ((i + 1) != classes.length) {
241                 sb.append(SPACE);
242           }
243         }
244         sb.append("))");
245
246         return sb;
247   }
248
249
250   /**
251    * Creates a field info list. The list has the following form
252    *
253    *  (0 name access-level type)
254    *
255    * where 0 indicates that this info list is for a field, name is 
256    * the name of the field, access-level is one of
257    * PUBLIC, PROTECTED, PACKAGE, or PRIVATE, and type is
258    * the type of the field. 
259    *  
260    * @param field field name 
261    */
262   private static StringBuffer tokenizeField(Field field) {
263     StringBuffer sb = new StringBuffer (30);
264     sb.append(START_LIST);
265     sb.append(printWithinQuotes(field.getName()));
266     sb.append(SPACE);
267     sb.append("'variable");
268     sb.append(SPACE);
269     sb.append(printWithinQuotes(className(field.getType())));
270     sb.append(SPACE);
271         sb.append(NIL); // default value
272     sb.append(SPACE);
273
274         StringBuffer modifiers = listModifiers(field.getModifiers());
275         if (modifiers.length() > 0) {
276           sb.append(START_LIST);
277           sb.append(modifiers);
278           sb.append(END_PAREN);
279         } else {
280           sb.append(NIL);
281         }
282     sb.append(SPACE);
283
284         sb.append(NIL); // docstring
285         sb.append(END_PAREN);
286         
287     return sb;
288   }
289     
290   /**
291    * Prints (list "name" "params") to the system output.
292    *
293    * @param name constructor name
294    * @param params parameter type
295    */
296   private static StringBuffer tokenizeCtor(Constructor ctor) {
297     StringBuffer sb = new StringBuffer (30);
298     sb.append(START_LIST);
299     sb.append(printWithinQuotes(ctor.getName()));
300     sb.append(SPACE);
301     sb.append("'function ");
302     sb.append(listClasses(ctor.getParameterTypes()));
303     sb.append(SPACE);
304
305     // extra specifiers
306     sb.append(START_LIST);
307     sb.append("'(constructor . t)");
308     StringBuffer temp = listModifiers(ctor.getModifiers());
309     if (temp.length() > 0) {
310       sb.append(SPACE);
311           sb.append(temp);
312     } 
313     Class[] types = ctor.getExceptionTypes();
314     if (types.length > 0) {
315       sb.append(SPACE);
316       sb.append(listExceptions(types));
317     }
318     sb.append(END_PAREN);
319     sb.append(SPACE);
320
321     sb.append(NIL); // no docstring
322
323     sb.append(END_PAREN);
324
325     return sb;
326   }
327
328   /**
329    * Prints (list "name" "returnType" "args") to the system output.
330    *
331    * @param name method name
332    * @param returnType method return type
333    * @param args method arguments
334    */
335   private static StringBuffer tokenizeMethod(Method method) {
336     StringBuffer sb = new StringBuffer (30);
337     sb.append(START_LIST);
338     sb.append(printWithinQuotes(method.getName()));
339     sb.append(SPACE);
340     sb.append("'function ");
341     sb.append(SPACE);
342     sb.append(listClasses(method.getParameterTypes()));
343     sb.append(SPACE);
344
345     // extra specifiers
346     StringBuffer temp = listModifiers(method.getModifiers());
347     Class[] types = method.getExceptionTypes();
348
349     if (sb.length() > 0 || types.length > 0) {
350       sb.append("(list");
351       
352       if (temp.length() > 0) {
353         sb.append(SPACE);
354         sb.append(temp);
355       } 
356
357       if (types.length > 0) {
358         sb.append(SPACE);
359         sb.append(listExceptions(types));
360       }
361        
362     }
363
364     sb.append(END_PAREN);
365     sb.append(SPACE);
366
367     sb.append(NIL); // no docstring
368
369     sb.append(END_PAREN);
370
371     return sb; 
372   } 
373
374   /**
375    * Get (list INNERCLASSINFO "className" access)
376    *
377    * @param name className
378    */
379   private static StringBuffer innerClassInfo(Class ic) {
380     StringBuffer sb = new StringBuffer (30);
381     sb.append(START_LIST);
382     sb.append(printWithinQuotes(ic.getName()));
383     sb.append(SPACE);
384     sb.append(String.valueOf(INNER_CLASS_INFO));
385     sb.append(SPACE);
386     sb.append(accessLevel(ic.getModifiers()));
387     sb.append(END_PAREN);
388     return sb;
389   }    
390
391             
392   /**
393    * Prints item within quotes i.e "item"
394    *
395    * @param item string to be quoted.
396    */
397   private static String printWithinQuotes(String item) {
398     StringBuffer sb = new StringBuffer (30);
399     sb.append(DOUBLE_QUOTE);
400     sb.append(item);
401     sb.append(DOUBLE_QUOTE);
402         
403     return sb.toString();
404   }
405
406
407   
408  
409  private static void getInheritedInnerClasses(Class c, StringBuffer sb)  {
410     //This is only used while initializing
411     if (c == null) {
412       return;
413     }
414         
415         
416         Class[] classes = c.getDeclaredClasses();
417
418         
419     for (int index = 0; index < classes.length ; index++) {
420       Class ic = classes[index];
421       if (!Modifier.isPrivate(ic.getModifiers())) {
422                 sb.append(innerClassInfo(ic));
423       }
424     }
425
426         getInheritedInnerClasses(c.getSuperclass(), sb);
427
428   }
429
430   private static void getInnerClasses(Class c, StringBuffer sb) {
431
432
433         // Get methods declared by c.
434         Class[] classes = c.getDeclaredClasses();
435
436     for (int index = 0; index < classes.length ; index++) {
437           sb.append(innerClassInfo(classes[index]));
438     }
439
440         getInheritedInnerClasses(c.getSuperclass(), sb);
441
442    }
443         
444
445   private static void getMemberInfo(Class c, StringBuffer sb) {
446     sb.append(START_LIST);
447         
448         // Get fields declared by c.
449         Field[] fields = c.getDeclaredFields();
450
451     for (int index = 0; index < fields.length ; index++) {
452           sb.append(tokenizeField(fields[index]));
453         }
454
455     // constructors
456     Constructor[] ctors = c.getDeclaredConstructors();
457       
458     for (int index = 0; index < ctors.length; index++) {
459       sb.append(tokenizeCtor(ctors[index]));
460         }
461
462     // Get methods declared by c.
463     Method[] methods = c.getDeclaredMethods();
464
465     for (int index = 0; index < methods.length ; index++) {
466       sb.append(tokenizeMethod(methods[index]));
467     }
468
469     // inner classes, including inherited classes
470     getInnerClasses(c, sb);
471
472     sb.append(END_PAREN);
473     sb.append(NL);
474         
475     return;
476   }
477    
478   /**
479    * Gets information on the specified class. Information is returned as a 
480    * list of lists that is printed to System.out.
481    *
482    * @param className a <code>String</code> value
483    */
484   public static void getClassInfo(String className) {
485     try {
486       DynamicClassLoader dcl = new DynamicClassLoader();
487       Class c = dcl.loadClass(className);
488       if (c != null) {
489         StringBuffer sb = new StringBuffer (3000);
490         sb.append(START_LIST);
491                 sb.append(printWithinQuotes(className));
492                 sb.append(SPACE);
493                 sb.append("'type");
494                 sb.append(SPACE);
495         getMemberInfo(c, sb);
496         sb.append(END_PAREN);
497         sb.append(NL);
498                 
499         Writer out
500           = new BufferedWriter(new OutputStreamWriter(System.out));
501         try {
502           out.write(sb.toString());
503           out.flush();
504         } catch (IOException e) {
505         }
506       }
507     } catch (ClassNotFoundException e) {
508       System.out.println(NIL);              
509     } catch (Exception e) {
510       System.out.println("(error \"Trying to load " + className +
511                          " caused a Java exception: " + e + "\")");       
512     } catch (UnsatisfiedLinkError e) {
513       // This occurs with classes that have native methods whose native
514       // implementations cannot be found.
515       System.out.println("(error \"Trying to load " + className +
516                          " caused a Java UnsatisfiedLinkError: " + e + "\")");       
517     } catch (LinkageError e) {
518       System.out.println("(error \"Trying to load " + className +
519                          " caused a Java LinkageError: " + e + "\")");       
520     }
521   }
522
523
524     
525   /**
526    * Looks up an unqualified class name in the class path to find possible
527    * fully qualified matches.
528    *
529    * @param className a value of type 'String'
530    * @param imports   an array of imported packages
531    */
532   public static void getClassInfo(String className,
533                                   String[]imports) {
534     String name; 
535     Class c;
536     for (int i = 0 ; i < imports.length ; i++) {
537       name = imports[i] + className;
538       try {
539         c = Class.forName(name);
540         if (c != null) {
541           getClassInfo(name);
542         }
543       } catch (ClassNotFoundException cnfe) { }
544                 
545     }
546     System.out.println(NIL);
547   }
548     
549   static String className(Class c) {
550     if (c.isArray())
551       return c.getComponentType().getName() + "[]";
552     else
553       return c.getName();
554   }
555     
556   static StringBuffer listClasses(Class[] classes) {
557     StringBuffer sb = new StringBuffer (100);
558         if (classes.length > 0) {
559                 
560           sb.append(START_LIST);
561           sb.append(SPACE);
562           for (int i = 0; i < classes.length; i++) {
563                 sb.append(printWithinQuotes(className(classes[i])));
564                 if ((i + 1) != classes.length) {
565                   sb.append(SPACE);
566                 }
567           }
568           sb.append(END_PAREN);
569         } else {
570           sb.append(NIL);
571         }
572         
573         return sb;
574   }
575
576   /**
577    * Tests whether a class has a member of a specified name.
578    * If so, prints t to standard out; if not, nil.
579    *
580    * @param className Name of class
581    * @param memberName Name of member
582    */
583   public static void hasMember(String className, String memberName) {
584     try {
585       DynamicClassLoader dcl = new DynamicClassLoader();
586       Class c = dcl.loadClass(className);
587       if (c != null) {
588
589         Field fields[] = c.getFields();
590         boolean hasField = false;
591
592         for (int index = 0; index < fields.length ; index++) {
593           if (fields[index].getName().equals(memberName)) {
594             hasField = true;
595             break;
596           }
597         }
598
599         Method methods[] = c.getMethods();
600         boolean hasMethod = false;
601
602         for (int index = 0; index < methods.length ; index++) {
603           if (methods[index].getName().equals(memberName)) {
604             hasMethod = true;
605             break;
606           }
607         }
608
609
610         StringBuffer sb = new StringBuffer (3000);
611         sb.append(START_LIST);
612                 sb.append(hasField ? "t" : "nil");
613                 sb.append(hasMethod ? " t" : " nil");
614         sb.append(END_PAREN);
615         sb.append(NL);
616                 
617         Writer out
618           = new BufferedWriter(new OutputStreamWriter(System.out));
619         try {
620           out.write(sb.toString());
621           out.flush();
622         } catch (IOException e) {
623         }
624       }
625     } catch (Exception cnfe) {System.out.println(NIL);}
626   }
627
628
629   public static void main (String[] args) {
630     getClassInfo("java.lang.Object");
631   }
632
633 } // ClassInfo
634
635 /*
636  * $Log: ClassInfo.java,v $
637  * Revision 1.1  2005/12/02 05:37:00  paulk
638  * Provides info for files on JDE classpath.
639  *
640  */
641
642 // End of ClassInfo.java