Recipe 25.7 Program: CrossRef


You've probably seen those other Java books that consist entirely of listings of the Java API for version thus-and-such of the JDK. I don't suppose you thought the authors of these works sat down and typed the entire contents from scratch. As a programmer, you would have realized, I hope, that there must be a way to obtain that information from Java. But you might not have realized how easy it is! If you've read this chapter faithfully, you now know that there is one true way: make the computer do the walking. Example 25-9 is a program that puts most of the techniques together. This version generates a cross-reference listing, but by overriding the last few methods, you could easily convert it to print the information in any format you like, including an API Reference book. You'd need to deal with the details of this or that publishing software FrameMaker, troff, TEX, or whatever but that's the easy part.

This program makes fuller use of the Reflection API than did MyJavaP in Recipe 25.6. It also uses the java.util.zip classes (see Recipe 10.20) to crack the JAR archive containing the class files of the API. Each class file found in the archive is loaded and listed; the listing part is similar to MyJavaP.

Example 25-9. CrossRef.java
import java.io.*; import java.util.*; import java.util.zip.*; import java.lang.reflect.*; /**  * CrossRef prints a cross-reference about all classes named in argv.  * For each class, all public fields and methods are listed.  * "Reflectance" is used to look up the information.  *  * It is expected that the output will be post-processed e.g.,  * with sort and awk/perl. Try:      java CrossRef |          uniq | # squeeze out polymorphic forms early         sort | awk '$2=="method" { ... }' > crossref-methods.txt  * The part in "{ ... }" is left as an exercise for the reader. :-(  *  */ public class CrossRef {     /** Counter of fields/methods printed. */     protected static int n = 0;     /** A "Modifier" object, to decode modifiers of fields/methods */     protected Modifier m = new Modifier( );     /** True if we are doing classpath, so only do java. and javax. */     protected static boolean doingStandardClasses = true;          /** Simple main program, construct self, process each .ZIP file      * found in CLASSPATH or in argv.      */     public static void main(String[] argv) {         CrossRef xref = new CrossRef( );         xref.doArgs(argv);     }     protected void doArgs(String[] argv) {         if (argv.length == 0) {             // No arguments, look in CLASSPATH             String s = System.getProperties( ).getProperty("java.class.path");             //  break apart with path sep.             String pathSep = System.getProperties( ).                 getProperty("path.separator");             StringTokenizer st = new StringTokenizer(s, pathSep);             // Process each classpath             while (st.hasMoreTokens( )) {                 String cand = st.nextToken( );                 System.err.println("Trying path " + cand);                 if (cand.endsWith(".zip") || cand.endsWith(".jar"))                     processOneZip(cand);             }         } else {             // We have arguments, process them as zip files             doingStandardClasses = false;             for (int i=0; i<argv.length; i++)                 processOneZip(argv[i]);         }         System.err.println("All done! Found " + n + " entries.");         System.exit(0);     }     /** For each Zip file, for each entry, xref it */     public void processOneZip(String classes) {             ArrayList entries = new ArrayList( );             try {                 ZipFile zippy =                      new ZipFile(new File(classes));                 Enumeration all = zippy.entries( );                 // For each entry, get its name and put it into "entries"                 while (all.hasMoreElements( )) {                     entries.add(((ZipEntry)(all.nextElement( ))).getName( ));                 }             } catch (IOException err) {                 System.err.println("IO Error: " + err);                 return;             }             // Sort the entries (by class name)             Collections.sort(entries);             // Process the entries             for (int i=0; i< entries.size( ); i++) {                 doClass((String)entries.get(i));             }     }     /** Format the fields and methods of one class, given its name.      */     protected void doClass(String zipName) {         if (System.getProperties( ).getProperty("debug.names") != null)             System.out.println("doClass(" + zipName + ");");         // Ignore package/directory, other odd-ball stuff.         if (zipName.endsWith("/")) {             System.err.println("Starting directory " + zipName);             return;         }         // Ignore META-INF stuff         if (zipName.startsWith("META-INF/")) {             return;         }         // Ignore images, HTML, whatever else we find.         if (!zipName.endsWith(".class")) {             System.err.println("Ignoring " + zipName);             return;         }         // If doing CLASSPATH, Ignore com.sun.* which are "internal API".         if (doingStandardClasses && zipName.startsWith("com.sun")){             return;         }              // Convert the zip file entry name, like         //    java/lang/Math.class         // to a class name like         //    java.lang.Math         String className = zipName.replace('/', '.').             substring(0, zipName.length( ) - 6);    // 6 for ".class"         if (System.getProperties( ).getProperty("debug.names") != null)             System.err.println("ZipName " + zipName +                  "; className " + className);         try {             Class c = Class.forName(className);             printClass(c);         } catch (ClassNotFoundException e) {             System.err.println("Error: Class " +                  className + " not found!");         } catch (Exception e) {             System.err.println(e);         }         // System.err.println("in gc...");         System.gc( );         // System.err.println("done gc");     }     /**      * Print the fields and methods of one class.      */     protected void printClass(Class c) {         int i, mods;         startClass(c);         try {             Object[] fields = c.getFields( );             Arrays.sort(fields);             for (i = 0; i < fields.length; i++) {                 Field field = (Field)fields[i];                 if (!m.isPrivate(field.getModifiers( ))                  && !m.isProtected(field.getModifiers( )))                     putField(field, c);                 else System.err.println("private field ignored: " + field);             }             Method methods[] = c.getDeclaredMethods( );             // Arrays.sort(methods);             for (i = 0; i < methods.length; i++) {                 if (!m.isPrivate(methods[i].getModifiers( ))                  && !m.isProtected(methods[i].getModifiers( )))                     putMethod(methods[i], c);                 else System.err.println("pvt: " + methods[i]);             }         } catch (Exception e) {             System.err.println(e);         }         endClass( );     }     /** put a Field's information to the standard output.      * Marked protected so you can override it (hint, hint).      */     protected void putField(Field fld, Class c) {         println(fld.getName( ) + " field " + c.getName( ) + " ");         ++n;     }     /** put a Method's information to the standard output.      * Marked protected so you can override it (hint, hint).      */     protected void putMethod(Method method, Class c) {         String methName = method.getName( );         println(methName + " method " + c.getName( ) + " ");         ++n;     }     /** Print the start of a class. Unused in this version,      * designed to be overridden */     protected void startClass(Class c) {     }     /** Print the end of a class. Unused in this version,      * designed to be overridden */     protected void endClass( ) {     }     /** Convenience routine, short for System.out.println */     protected final void println(String s) {         System.out.println(s);     } }

You probably noticed the methods startClass( ) and endClass( ), which are null. These methods are placeholders designed to make subclassing easy for when you need to write something at the start and end of each class. One example might be a fancy text formatting application in which you need to output a bold header at the beginning of each class. Another would be XML (see Chapter 21), where you'd want to write a tag like <class> at the front of each class, and </class> at the end. Example 25-10 is, in fact, a working XML-specific subclass that generates (limited) XML for each field and method.

Example 25-10. CrossRefXML.java
import java.io.*; import java.lang.reflect.*; /** This class subclasses CrossRef to output the information in XML.  */ public class CrossRefXML extends CrossRef {     public static void main(String[] argv) {         CrossRef xref = new CrossRefXML( );         xref.doArgs(argv);     }     /** Print the start of a class.      */     protected void startClass(Class c) {         println("<class><classname>" + c.getName( ) + "</classname>");     }     protected void putField(Field fld, Class c) {         println("<field>" + fld + "</field>");         ++n;     }     /** put a Method's information to the standard output.      * Marked protected so you can override it (hint, hint).      */     protected void putMethod(Method method, Class c) {         println("<method>" + method + "</method>");         ++n;     }     /** Print the end of a class.       */     protected void endClass( ) {         println("</class>");     } }

By the way, if you publish a book using either of these and get rich, "Remember, remember me!"



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net