/* * gnu/tools/ClassTool.java -- display useful information about classes * Copyright (C) 1998 Wes Biggs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package gnu.tools; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.File; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import gnu.getopt.Getopt; import gnu.getopt.LongOpt; /** * ClassTool is a utility for reconstructing and formatting Java class * APIs. Without any options, it will output (to System.out) the * public, protected and package-private inner classes and interfaces, * fields, methods and constructors for each class named on the * command line. *

* Various options will cause ClassTool to include and exclude various * scoping levels and types of output. For a complete list, run * ClassTool with the "-h" or "--help" option. * * @author Wes Biggs * @version 1.02, 30 November 1998 */ public class ClassTool { private static String s_version = "1.02"; private static int filter; private static int INDENT_INCREMENT = 4; private static boolean noMethods, noFields, noConstructors, noInners, classOnly, stubs, compatibility; /** Return the version number of this utility. */ public static String getVersion() { return s_version; } private static void showUsage() { String line; BufferedReader br = new BufferedReader(new InputStreamReader(ClassTool.class.getResourceAsStream("ClassToolUsage.txt"))); try { while ((line = br.readLine()) != null) System.out.println(line); } catch (IOException e) { } System.exit(0); } /** * Reads the command line to set options, then outputs each class * specified. Classes may be specified either as a filename or in * class notation (e.g. "java/lang/Thread.class" or "java.lang.Thread"). * * @param argv The command line arguments (use "-h" or "--help" for * details) */ public static void main(String[] argv) { if (argv.length == 0) showUsage(); LongOpt[] options = { new LongOpt("none", LongOpt.NO_ARGUMENT, null, '0'), new LongOpt("public", LongOpt.NO_ARGUMENT, null, '1'), new LongOpt("protected", LongOpt.NO_ARGUMENT, null, '2'), new LongOpt("package", LongOpt.NO_ARGUMENT, null, '3'), new LongOpt("private", LongOpt.NO_ARGUMENT, null, '4'), new LongOpt("no-constructors", LongOpt.NO_ARGUMENT, null, 'c'), new LongOpt("exclude", LongOpt.REQUIRED_ARGUMENT, null, 'e'), new LongOpt("no-fields", LongOpt.NO_ARGUMENT, null, 'f'), new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'), new LongOpt("include", LongOpt.REQUIRED_ARGUMENT, null, 'i'), new LongOpt("no-methods", LongOpt.NO_ARGUMENT, null, 'm'), new LongOpt("no-inner", LongOpt.NO_ARGUMENT, null, 'n'), new LongOpt("stubs", LongOpt.NO_ARGUMENT, null, 's'), new LongOpt("tab-size", LongOpt.REQUIRED_ARGUMENT, null, 't'), new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'v'), new LongOpt("compatibility", LongOpt.NO_ARGUMENT, null, 'x') }; Getopt g = new Getopt(ClassTool.class.getName(), argv, "01234ce:fhi:mnst:vx", options); int c; String arg; while ((c = g.getopt()) != -1) { switch (c) { case 'x': compatibility = true; break; case 'c': noConstructors = true; break; case 'm': noMethods = true; break; case 'n': noInners = true; break; case 's': stubs = true; break; case 'f': noFields = true; break; case 't': INDENT_INCREMENT = Integer.parseInt(g.getOptarg()); break; case 'i': arg = g.getOptarg(); if (arg.equals("public")) filter |= Modifier.PUBLIC; else if (arg.equals("protected")) filter |= Modifier.PROTECTED; else if (arg.equals("package")) filter |= 0x10000; else if (arg.equals("private")) filter |= Modifier.PRIVATE; break; case 'e': arg = g.getOptarg(); if (arg.equals("public")) filter &= (0x3FFFF ^ Modifier.PUBLIC); else if (arg.equals("protected")) filter &= (0x3FFFF ^ Modifier.PROTECTED); else if (arg.equals("package")) filter &= 0x2FFFF; else if (arg.equals("private")) filter &= (0x3FFFF ^ Modifier.PRIVATE); break; case '0': classOnly = true; filter = 0x20000; break; case '4': filter |= Modifier.PRIVATE; case '3': filter |= 0x10000; case '2': filter |= Modifier.PROTECTED; case '1': filter |= Modifier.PUBLIC; break; case 'v': System.out.println(ClassTool.class.getName() + ' ' + s_version + " (C) 1998 Wes Biggs"); System.out.println(""); System.out.println("This is free software -- see the source for copying conditions. There is NO"); System.out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."); System.exit(0); case 'h': case '?': showUsage(); } } // Default to public, protected and package. if (filter == 0) filter = 0x10000 | Modifier.PUBLIC | Modifier.PROTECTED; c = g.getOptind(); while (c < argv.length) { new ClassTool(argv[c++], 0, System.out); } } private PrintStream out; private int indent; private ClassTool(String className,int f_indent,PrintStream f_out) { out = f_out; indent = f_indent; className = className.replace(File.separatorChar,'.'); if (className.endsWith(".class")) className = className.substring(0, className.length() - 6); Class clazz = null; try { clazz = Class.forName(className); } catch (ClassNotFoundException e0) { System.err.println("Class not found: " + className); return; } catch (IllegalArgumentException e1) { System.err.println("Illegal class name: " + className); return; } int mods = clazz.getModifiers(); indent(); if ((mods & Modifier.PUBLIC) != 0) out.print("public "); if ((mods & Modifier.STATIC) != 0) out.print("static "); if ((mods & Modifier.ABSTRACT) != 0) out.print("abstract "); if ((mods & Modifier.INTERFACE) != 0) out.print("interface "); else { if ((mods & Modifier.FINAL) != 0) out.print("final "); out.print("class "); } out.print(className(clazz)); Class superclazz = clazz.getSuperclass(); if (superclazz != null) { out.print(" extends " + className(superclazz)); } listClasses(clazz.getInterfaces(), " implements "); if (classOnly) { out.println(";"); return; } out.println(" {"); indent += INDENT_INCREMENT; // Inner classes/interfaces if (!noInners) { Class[] inner = clazz.getDeclaredClasses(); if (inner != null) { for (int i = 0; i < inner.length; i++) { if (excluded(inner[i])) continue; new ClassTool(inner[i].getName(), indent, out); } } } // Fields if (!noFields) { Field[] fields = clazz.getDeclaredFields(); if (fields != null) { for (int i = 0; i < fields.length; i++) { if (excluded(fields[i])) continue; // Kluge for the way non-static inner classes are // reported in Sun JDK 1.2rc1 if (fields[i].getName().equals("this$0")) continue; display(fields[i]); out.println(className(fields[i].getType()) + " " + fields[i].getName() + ";"); } } } // Methods if (!noMethods) { Method[] methods = clazz.getDeclaredMethods(); if (methods != null) { for (int i = 0; i < methods.length; i++) { if (excluded(methods[i])) continue; display(methods[i]); out.print(className(methods[i].getReturnType()) + " " + methods[i].getName() + "("); listClasses(methods[i].getParameterTypes(), ""); out.print(")"); listClasses(methods[i].getExceptionTypes(), " throws "); if (stubs) { out.println(" {"); indent(); out.println("}"); out.println(""); } else out.println(";"); } } } // Constructors if (!noConstructors) { Constructor[] constructors = clazz.getDeclaredConstructors(); if (constructors != null) { for (int i = 0; i < constructors.length; i++) { if (excluded(constructors[i])) continue; display(constructors[i]); out.print(className(clazz) + "("); Class[] params = constructors[i].getParameterTypes(); // Kluge for JDK 1.2rc1 non-static inner classes if ((indent > 0) && ((mods & Modifier.STATIC) == 0) && (params.length > 0) && (className(clazz).startsWith(className(params[0]) + '.'))) { if (params.length > 1) { Class[] p2 = new Class [params.length - 1]; System.arraycopy(params,1,p2,0,params.length - 1); params = p2; } else params = null; } listClasses(params, ""); out.print(")"); listClasses(constructors[i].getExceptionTypes(), " throws "); if (stubs) { out.println(" {"); indent(); out.println("}"); out.println(""); } else out.println(";"); } } } indent -= INDENT_INCREMENT; indent(); out.println("}"); } private void listClasses(Class[] c, String ifAny) { if (c == null) return; if (c.length > 0) out.print(ifAny); for (int i = 0; i < c.length; i++) { if (i > 0) out.print(", "); out.print(className(c[i])); } } private String className(Class c) { String name = c.getName().replace('$','.'); if (c.isArray()) { switch (name.charAt(1)) { case 'B': name = "byte"; break; case 'C': name = "char"; break; case 'D': name = "double"; break; case 'F': name = "float"; break; case 'I': name = "int"; break; case 'J': name = "long"; break; case 'L': name = name.substring(2, name.length() - 1); break; case 'S': name = "short"; break; case 'Z': name = "boolean"; break; } name = name + "[]"; } return name; } private boolean excluded(Member m) { int mods = m.getModifiers(); if (mods == 0) mods = 0x10000; return ((mods & filter) == 0); } private boolean excluded(Class m) { int mods = m.getModifiers(); if (mods == 0) mods = 0x10000; return ((mods & filter) == 0); } private void display(Member m) { int mods = m.getModifiers(); indent(); if ((mods & Modifier.PUBLIC) != 0) out.print("public "); if ((mods & Modifier.PRIVATE) != 0) out.print("private "); if ((mods & Modifier.PROTECTED) != 0) out.print("protected "); if ((mods & Modifier.ABSTRACT) != 0) out.print("abstract "); if ((mods & Modifier.STATIC) != 0) out.print("static "); if ((mods & Modifier.TRANSIENT) != 0) out.print("transient "); if ((mods & Modifier.FINAL) != 0) out.print("final "); if (!compatibility) { if ((mods & Modifier.NATIVE) != 0) out.print("native "); if ((mods & Modifier.SYNCHRONIZED) != 0) out.print("synchronized "); if ((mods & Modifier.VOLATILE) != 0) out.print("volatile "); } // This will be needed in 1.2 // if ((mods & Modifier.STRICT) != 0) out.print("strict "); } private void indent() { for (int i = 0; i < indent; i++) out.print(' '); } }