package com.macmillan.nmeyers; import java.lang.*; import java.lang.reflect.*; import java.util.*; import java.awt.*; import com.sun.java.util.collections.*; /* * DumpClass11: Dump details about a class found in the classpath. * * Author: Nathan Meyers, nmeyers@javalinux.net * $Id: DumpClass11.java,v 1.4 1999/11/08 00:26:59 nathanm Exp $ * * 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, 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 * with this program. If not, the license is available from the * GNU project, at http://www.gnu.org. */ // DumpClass11: Dump member information on a class and all of its // ancestors. Useful for getting a complete list of class members. // // Usage: DumpClass11 [options] // // Normal output format is: // Class name & interfaces // Class hierarchy // "Constructors" Heading // List of constructors // "Fields" Heading // List of fields // "Methods" Heading // List of methods // // Class members are listed with a basename key at the beginning of the // line. Example: // // HAND_CURSOR: public static final int java.awt.Frame.HAND_CURSOR // // Options: // -public // -protected [default] // -package // -private // -suppress:[name,interfaces,hierarchy,headings,keys,all] // -noancestors // -inaccessible // // The -public, -protected, -package, -private options control which members // we'll expose: -public exposes only public members, -private exposes all // members. // // The -noancestors option causes us to print members belonging to the // requested class[es], but none of its ancestors. Like the javap utility. // // The -suppress options can suppress certain details in the listing. // Suppressing all detail will result in a list of class members, without // the basename keys or any other information. // // The -inaccessible option shows members that are not accessible from // this class: ancestor constructors, ancestor private members, ancestor // package-visible members from a different package. class DumpClass11 { static class ClassMember implements Comparable { Member member = null; ClassMember(Member m) { member = m; } public String toString() { return member.toString(); } public String getName() { String result = member.getName(); int pos = result.lastIndexOf('.'); if (pos != -1) result = result.substring(pos + 1); return result; } // Implementation of compareTo: create an ordering between // ClassMember representations of members. public int compareTo(Object m) { if (!member.getClass().equals(((ClassMember)m).member.getClass())) { return member.getClass().toString().compareTo( ((ClassMember)m).member.getClass().toString()); } if (member.getClass().equals(Constructor.class)) { final Constructor constructor1 = (Constructor)member; final Constructor constructor2 = (Constructor)((ClassMember)m).member; int result = constructor1.getName().compareTo( constructor2.getName()); if (result != 0) return result; Class[] parm1 = constructor1.getParameterTypes(); Class[] parm2 = constructor2.getParameterTypes(); for (int i = 0; i < parm1.length && i < parm2.length; i++) { result = parm1[i].toString().compareTo(parm2[i].toString()); if (result != 0) return result; } return parm1.length - parm2.length; } if (member.getClass().equals(Method.class)) { final Method method1 = (Method)member; final Method method2 = (Method)((ClassMember)m).member; int result = method1.getName().compareTo( method2.getName()); if (result != 0) return result; Class[] parm1 = method1.getParameterTypes(); Class[] parm2 = method2.getParameterTypes(); for (int i = 0; i < parm1.length && i < parm2.length; i++) { result = parm1[i].toString().compareTo(parm2[i].toString()); if (result != 0) return result; } return parm1.length - parm2.length; } if (member.getClass().equals(Field.class)) return member.getName().compareTo( ((ClassMember)m).member.getName()); return member.toString().compareTo( ((ClassMember)m).member.toString()); } } // Our own implementation of a set, with some filtering on the add() // operation static class MemberSet extends TreeSet { boolean showProtected = true; boolean showPackage = false; boolean showPrivate = false; boolean showInaccessible = false; String clsName = null; String packageName = null; MemberSet(Class cls, boolean f1, boolean f2, boolean f3, boolean f4) { showProtected = f1; showPackage = f2; showPrivate = f3; showInaccessible = f4; clsName = cls.getName(); int ppos = clsName.lastIndexOf("."); packageName = (ppos >= 0) ? clsName.substring(0, ppos) : ""; } public boolean add(Class cls, ClassMember element) { int modifier = element.member.getModifiers(); // Root out inaccessible members if (!showInaccessible) { String cName = cls.getName(); int ppos = clsName.lastIndexOf("."); String pName = (ppos >= 0) ? clsName.substring(0, ppos) : ""; // Private ancestor members if (Modifier.isPrivate(modifier) && !cName.equals(clsName) || // Ancestor constructors element.member.getClass().equals(Constructor.class) && !cName.equals(clsName) || // Package-visible ancestors from different packages !Modifier.isPublic(modifier) && !Modifier.isProtected(modifier) && !pName.equals(packageName)) return false; } // This logic assumes relationships between permission // levels that will always be true (e.g. showPrivate->show all). if (Modifier.isPublic(modifier) || showPrivate || Modifier.isProtected(modifier) && showProtected || !Modifier.isPrivate(modifier) && showPackage) return super.add(element); else return false; } } static void usageMsg() { System.err.println("Usage: DumpClass11 [options] "); System.err.println("\nOptions:"); System.err.println(" -public"); System.err.println(" -protected (default)"); System.err.println(" -package"); System.err.println(" -private"); System.err.println(" -suppress:{name,interfaces," + "hierarchy,headings,keys,all}"); System.err.println(" -noancestors"); System.err.println(" -inaccessible"); System.exit(1); } public static void main(String[] argv) { int argn; boolean showProtected = true; boolean showPackage = false; boolean showPrivate = false; boolean showName = true; boolean showInterfaces = true; boolean showHierarchy = true; boolean showHeadings = true; boolean showKeys = true; boolean showAncestorMembers = true; boolean showInaccessible = false; for (argn = 0; argn < argv.length && argv[argn].startsWith("-"); argn++) { if (argv[argn].equals("-public")) showProtected = showPackage = showPrivate = false; else if (argv[argn].equals("-protected")) { showProtected = true; showPackage = showPrivate = false; } else if (argv[argn].equals("-package")) { showProtected = showPackage = true; showPrivate = false; } else if (argv[argn].equals("-private")) showProtected = showPackage = showPrivate = true; else if (argv[argn].equals("-noancestors")) showAncestorMembers = false; else if (argv[argn].equals("-inaccessible")) showInaccessible = true; else if (argv[argn].startsWith("-suppress:")) { String args = argv[argn].substring(10); while (args.length() > 0) { int comma = args.indexOf(','); String arg; if (comma > 0) { arg = args.substring(0, comma); args = args.substring(comma + 1); } else { arg = args; args = ""; } if (arg.equals("name") || arg.equals("all")) showName = false; if (arg.equals("interfaces") || arg.equals("all")) showInterfaces = false; if (arg.equals("hierarchy") || arg.equals("all")) showHierarchy = false; if (arg.equals("headings") || arg.equals("all")) showHeadings = false; if (arg.equals("keys") || arg.equals("all")) showKeys = false; } } else { usageMsg(); } } // For each class requested for (boolean firstClass = true; argn < argv.length; firstClass = false, argn++) { Class cls; // Load the class try { cls = Class.forName(argv[argn]); } catch (ClassNotFoundException e) { System.err.println("Class " + argv[argn] + " not found"); continue; } // Build a set of members MemberSet memberSet = new MemberSet(cls, showProtected, showPackage, showPrivate, showInaccessible); // Step up the class hierarchy until we run out. The indent // controls indentation of classes in the hierarchy chart, and // is also used to ascertain when we're operating on the // requested class or an ancestor. for (String indent = "";; indent += " ") { if (showName) { if (indent.equals("")) { // Do some processing specific to the requested class if (!firstClass) System.out.println(""); int modifiers = cls.getModifiers(); String modString = Modifier.toString(modifiers); // Filter out weirdness in handling of "interface" // modifier int pos = modString.indexOf(" interface"); if (pos >= 0) modString = modString.substring(0, pos) + modString.substring(pos + 10); if (modString.length() > 0) System.out.print(modString + " "); } if (showHierarchy || indent.equals("")) System.out.print(indent + cls); // For the requested class, but not superclasses, list // the supported interfaces if (indent.equals("") && showInterfaces) { Class[] interfaces; interfaces = cls.getInterfaces(); if (interfaces != null && interfaces.length > 0) { System.out.print(" implements " + interfaces[0].getName()); for (int j = 1; j < interfaces.length; j++) System.out.print(", " + interfaces[j].getName()); } } if (showHierarchy || indent.equals("")) System.out.println(""); } if (showAncestorMembers || indent.equals("")) { // Build a list of methods for this class Method[] methods; try { methods = cls.getDeclaredMethods(); } catch (SecurityException e) { System.err.println("Security exception calling " + "getDeclaredMethods() for " + argv[argn]); break; } for (int j = 0; j < methods.length; j++) memberSet.add(cls, new ClassMember(methods[j])); // Build a list of constructors for this class Constructor[] constructors; try { constructors = cls.getDeclaredConstructors(); } catch (SecurityException e) { System.err.println("Security exception calling " + "getDeclaredConstructors() for " + argv[argn]); break; } for (int j = 0; j < constructors.length; j++) memberSet.add(cls, new ClassMember(constructors[j])); // Build a list of fields for this class Field[] fields; try { fields = cls.getDeclaredFields(); } catch (SecurityException e) { System.err.println("Security exception calling " + "getDeclaredFields() for " + argv[argn]); break; } for (int j = 0; j < fields.length; j++) memberSet.add(cls, new ClassMember(fields[j])); } // We're done when we run out of classes to analyze if (cls.equals(Object.class)) break; cls = cls.getSuperclass(); if (cls == null) break; } // Output results Class currentMemberType = null; for (Iterator j = memberSet.iterator(); j.hasNext();) { ClassMember mm = (ClassMember)j.next(); if (showHeadings && !mm.member.getClass().equals(currentMemberType)) { currentMemberType = mm.member.getClass(); if (currentMemberType == Constructor.class) System.out.println("\nConstructors\n"); else if (currentMemberType == Field.class) System.out.println("\nFields\n"); else System.out.println("\nMethods\n"); } if (showKeys) System.out.print(mm.getName() + ": "); System.out.println(mm.toString()); } } System.exit(0); } }