Program: CrossRef

We’ve all seen those 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 JavaP in Section 25.7. It also uses the java.util.zip classes (see Section 9.19) 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 JavaP.

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 are placeholders designed to make it easy for subclassing 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!”

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.212.212