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)
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.
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.
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.
28 import java.lang.reflect.*;
29 import jde.util.DynamicClassLoader;
32 * This class provides information about classes on jde-global-classpath.
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)
40 public class ClassInfo {
42 /*************************************************************************
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;
56 StringBuffer sb = new StringBuffer (10);
57 sb.append(START_PAREN);
60 START_LIST = sb.toString();
64 * Access level for a class member
66 public static final int PUBLIC = 0;
69 * Access level for a class member
71 public static final int PROTECTED = 1;
74 * Access level for a class member
76 public static final int PACKAGE = 2;
79 * Access level for a class member
81 public static final int PRIVATE = 3;
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;
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
98 public static void isAncestorOf(String ancestor, String child) {
100 Class classAncestor = Class.forName( ancestor );
101 Class classChild = Class.forName( child );
102 if( classAncestor.isAssignableFrom( classChild ) )
103 System.out.println(T);
105 System.out.println(NIL);
106 } catch( Exception ex) {
107 System.out.println(NIL);
112 * Returns true if the entity is accessible to the specified level of
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
119 * @see Member.getModifiers()
120 * @see Class.getModifiers()
122 private static boolean isAccessible(int modifiers, int 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
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);
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:"
144 private static String accessLevel(int modifiers) {
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);
153 level = String.valueOf(PACKAGE);
159 private static StringBuffer listModifiers(int modifiers) {
160 StringBuffer sb = new StringBuffer (30);
162 if (Modifier.isAbstract(modifiers)) {
163 sb.append("\"abstract\"");
167 if (Modifier.isFinal(modifiers)) {
168 sb.append("\"final\"");
172 if (Modifier.isInterface(modifiers)) {
173 sb.append("\"interface\"");
177 if (Modifier.isNative(modifiers)) {
178 sb.append("\"native\"");
182 if (Modifier.isPrivate(modifiers)) {
183 sb.append("\"private\"");
187 if (Modifier.isProtected(modifiers)) {
188 sb.append("\"protected\"");
192 if (Modifier.isPublic(modifiers)) {
193 sb.append("\"public\"");
197 if (Modifier.isStatic(modifiers)) {
198 sb.append("\"static\"");
202 if (Modifier.isStrict(modifiers)) {
203 sb.append("\"strict\"");
207 if (Modifier.isSynchronized(modifiers)) {
208 sb.append("\"synchronized\"");
212 if (Modifier.isTransient(modifiers)) {
213 sb.append("\"transient\"");
217 if (Modifier.isVolatile(modifiers)) {
218 sb.append("\"volotile\"");
222 if (sb.length() > 0) {
223 StringBuffer temp = new StringBuffer(sb.length() + 26);
224 temp.append("(cons 'typemodifiers (list ");
234 private static StringBuffer listExceptions(Class[] classes) {
235 StringBuffer sb = new StringBuffer (30);
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) {
251 * Creates a field info list. The list has the following form
253 * (0 name access-level type)
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.
260 * @param field field name
262 private static StringBuffer tokenizeField(Field field) {
263 StringBuffer sb = new StringBuffer (30);
264 sb.append(START_LIST);
265 sb.append(printWithinQuotes(field.getName()));
267 sb.append("'variable");
269 sb.append(printWithinQuotes(className(field.getType())));
271 sb.append(NIL); // default value
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);
284 sb.append(NIL); // docstring
285 sb.append(END_PAREN);
291 * Prints (list "name" "params") to the system output.
293 * @param name constructor name
294 * @param params parameter type
296 private static StringBuffer tokenizeCtor(Constructor ctor) {
297 StringBuffer sb = new StringBuffer (30);
298 sb.append(START_LIST);
299 sb.append(printWithinQuotes(ctor.getName()));
301 sb.append("'function ");
302 sb.append(listClasses(ctor.getParameterTypes()));
306 sb.append(START_LIST);
307 sb.append("'(constructor . t)");
308 StringBuffer temp = listModifiers(ctor.getModifiers());
309 if (temp.length() > 0) {
313 Class[] types = ctor.getExceptionTypes();
314 if (types.length > 0) {
316 sb.append(listExceptions(types));
318 sb.append(END_PAREN);
321 sb.append(NIL); // no docstring
323 sb.append(END_PAREN);
329 * Prints (list "name" "returnType" "args") to the system output.
331 * @param name method name
332 * @param returnType method return type
333 * @param args method arguments
335 private static StringBuffer tokenizeMethod(Method method) {
336 StringBuffer sb = new StringBuffer (30);
337 sb.append(START_LIST);
338 sb.append(printWithinQuotes(method.getName()));
340 sb.append("'function ");
342 sb.append(listClasses(method.getParameterTypes()));
346 StringBuffer temp = listModifiers(method.getModifiers());
347 Class[] types = method.getExceptionTypes();
349 if (sb.length() > 0 || types.length > 0) {
352 if (temp.length() > 0) {
357 if (types.length > 0) {
359 sb.append(listExceptions(types));
364 sb.append(END_PAREN);
367 sb.append(NIL); // no docstring
369 sb.append(END_PAREN);
375 * Get (list INNERCLASSINFO "className" access)
377 * @param name className
379 private static StringBuffer innerClassInfo(Class ic) {
380 StringBuffer sb = new StringBuffer (30);
381 sb.append(START_LIST);
382 sb.append(printWithinQuotes(ic.getName()));
384 sb.append(String.valueOf(INNER_CLASS_INFO));
386 sb.append(accessLevel(ic.getModifiers()));
387 sb.append(END_PAREN);
393 * Prints item within quotes i.e "item"
395 * @param item string to be quoted.
397 private static String printWithinQuotes(String item) {
398 StringBuffer sb = new StringBuffer (30);
399 sb.append(DOUBLE_QUOTE);
401 sb.append(DOUBLE_QUOTE);
403 return sb.toString();
409 private static void getInheritedInnerClasses(Class c, StringBuffer sb) {
410 //This is only used while initializing
416 Class[] classes = c.getDeclaredClasses();
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));
426 getInheritedInnerClasses(c.getSuperclass(), sb);
430 private static void getInnerClasses(Class c, StringBuffer sb) {
433 // Get methods declared by c.
434 Class[] classes = c.getDeclaredClasses();
436 for (int index = 0; index < classes.length ; index++) {
437 sb.append(innerClassInfo(classes[index]));
440 getInheritedInnerClasses(c.getSuperclass(), sb);
445 private static void getMemberInfo(Class c, StringBuffer sb) {
446 sb.append(START_LIST);
448 // Get fields declared by c.
449 Field[] fields = c.getDeclaredFields();
451 for (int index = 0; index < fields.length ; index++) {
452 sb.append(tokenizeField(fields[index]));
456 Constructor[] ctors = c.getDeclaredConstructors();
458 for (int index = 0; index < ctors.length; index++) {
459 sb.append(tokenizeCtor(ctors[index]));
462 // Get methods declared by c.
463 Method[] methods = c.getDeclaredMethods();
465 for (int index = 0; index < methods.length ; index++) {
466 sb.append(tokenizeMethod(methods[index]));
469 // inner classes, including inherited classes
470 getInnerClasses(c, sb);
472 sb.append(END_PAREN);
479 * Gets information on the specified class. Information is returned as a
480 * list of lists that is printed to System.out.
482 * @param className a <code>String</code> value
484 public static void getClassInfo(String className) {
486 DynamicClassLoader dcl = new DynamicClassLoader();
487 Class c = dcl.loadClass(className);
489 StringBuffer sb = new StringBuffer (3000);
490 sb.append(START_LIST);
491 sb.append(printWithinQuotes(className));
495 getMemberInfo(c, sb);
496 sb.append(END_PAREN);
500 = new BufferedWriter(new OutputStreamWriter(System.out));
502 out.write(sb.toString());
504 } catch (IOException e) {
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 + "\")");
526 * Looks up an unqualified class name in the class path to find possible
527 * fully qualified matches.
529 * @param className a value of type 'String'
530 * @param imports an array of imported packages
532 public static void getClassInfo(String className,
536 for (int i = 0 ; i < imports.length ; i++) {
537 name = imports[i] + className;
539 c = Class.forName(name);
543 } catch (ClassNotFoundException cnfe) { }
546 System.out.println(NIL);
549 static String className(Class c) {
551 return c.getComponentType().getName() + "[]";
556 static StringBuffer listClasses(Class[] classes) {
557 StringBuffer sb = new StringBuffer (100);
558 if (classes.length > 0) {
560 sb.append(START_LIST);
562 for (int i = 0; i < classes.length; i++) {
563 sb.append(printWithinQuotes(className(classes[i])));
564 if ((i + 1) != classes.length) {
568 sb.append(END_PAREN);
577 * Tests whether a class has a member of a specified name.
578 * If so, prints t to standard out; if not, nil.
580 * @param className Name of class
581 * @param memberName Name of member
583 public static void hasMember(String className, String memberName) {
585 DynamicClassLoader dcl = new DynamicClassLoader();
586 Class c = dcl.loadClass(className);
589 Field fields[] = c.getFields();
590 boolean hasField = false;
592 for (int index = 0; index < fields.length ; index++) {
593 if (fields[index].getName().equals(memberName)) {
599 Method methods[] = c.getMethods();
600 boolean hasMethod = false;
602 for (int index = 0; index < methods.length ; index++) {
603 if (methods[index].getName().equals(memberName)) {
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);
618 = new BufferedWriter(new OutputStreamWriter(System.out));
620 out.write(sb.toString());
622 } catch (IOException e) {
625 } catch (Exception cnfe) {System.out.println(NIL);}
629 public static void main (String[] args) {
630 getClassInfo("java.lang.Object");
636 * $Log: ClassInfo.java,v $
637 * Revision 1.1 2005/12/02 05:37:00 paulk
638 * Provides info for files on JDE classpath.
642 // End of ClassInfo.java