jdiff.API.java Source code

Java tutorial

Introduction

Here is the source code for jdiff.API.java

Source

package jdiff;

import java.io.*;
import java.util.*;

import org.apache.commons.lang3.StringEscapeUtils;

/**
 * The internal representation of an API. 
 * 
 * RootDoc could have been used for representing this, but 
 * you cannot serialize a RootDoc object - see
 *  http://developer.java.sun.com/developer/bugParade/bugs/4125581.html
 * You might be able use Javadoc.Main() to create another RootDoc, but the 
 * methods are package private. You can run javadoc in J2SE1.4, see:
 *  http://java.sun.com/j2se/1.4/docs/tooldocs/javadoc/standard-doclet.html#runningprogrammatically
 * but you still can't get the RootDoc object.
 *
 * The advantage of writing out an XML representation of each API is that
 * later runs of JDiff don't have to have Javadoc scan all the files again,
 * a possibly lengthy process. XML also permits other source code in 
 * languages other than Java to be scanned to produce XML, and then versions
 * of JDiff can be used to create documents describing the difference in those
 * APIs.
 * 
 * See the file LICENSE.txt for copyright details.
 * @author Matthew Doar, mdoar@pobox.com
 */
public class API {

    /** 
     * The list of all the top-level packages. 
     * Each package contains classes, each class contains members, and so on.
     */
    public List packages_; // PackageAPI[]

    /** 
     * The list of all the classes. 
     * This is used to generate the methods and fields which are inherited,
     * rather than storing them in the XML file.
     */
    public Hashtable classes_;

    /** 
     * The String which identifies this API, e.g. &quotSuperProduct 1.3".
     */
    public String name_ = null;

    /** The current package being added to during parsing. */
    public PackageAPI currPkg_ = null;
    /** The current class being added to during parsing. */
    public ClassAPI currClass_ = null;
    /** The current constructor being added to during parsing. */
    public ConstructorAPI currCtor_ = null;
    /** The current method being added to during parsing. */
    public MethodAPI currMethod_ = null;
    /** The current field being added to during parsing. */
    public FieldAPI currField_ = null;

    /** Default constructor. */
    public API() {
        packages_ = new ArrayList(); //PackageAPI[]
        classes_ = new Hashtable(); //ClassAPI
    }

    //
    // Methods to display the contents of an API object.
    //

    /** Amount by which to increment each indentation. */
    public static final int indentInc = 2;

    /** Display the contents of the API object. */
    public void dump() {
        int indent = 0;
        Iterator iter = packages_.iterator();
        while (iter.hasNext()) {
            dumpPackage((PackageAPI) (iter.next()), indent);
        }
    }

    /** 
     * Display the contents of a PackageAPI object.
     *
     * @param pkg The given PackageAPI object.
     * @param indent The number of spaces to indent the output.
     */
    public void dumpPackage(PackageAPI pkg, int indent) {
        for (int i = 0; i < indent; i++)
            System.out.print(" ");
        System.out.println("Package Name: " + pkg.name_);
        Iterator iter = pkg.classes_.iterator();
        while (iter.hasNext()) {
            dumpClass((ClassAPI) (iter.next()), indent + indentInc);
        }
        // Display documentation
        if (pkg.doc_ != null) {
            System.out.print("Package doc block:");
            System.out.println("\"" + pkg.doc_ + "\"");
        }
    }

    /** 
     * Display the contents of a ClassAPI object.
     *
     * @param c The given ClassAPI object.
     * @param indent The number of spaces to indent the output.
     */
    public static void dumpClass(ClassAPI c, int indent) {
        for (int i = 0; i < indent; i++)
            System.out.print(" ");
        if (c.isInterface_)
            System.out.println("Interface name: " + c.name_);
        else
            System.out.println("Class Name: " + c.name_);
        if (c.extends_ != null) {
            for (int i = 0; i < indent; i++)
                System.out.print(" ");
            System.out.println("Extends: " + c.extends_);
        }
        if (c.implements_.size() != 0) {
            for (int i = 0; i < indent; i++)
                System.out.print(" ");
            System.out.println("Implements: ");
            Iterator iter = c.implements_.iterator();
            while (iter.hasNext()) {
                String interfaceImpl = (String) (iter.next());
                for (int i = 0; i < indent + 2; i++)
                    System.out.print(" ");
                System.out.println("  " + interfaceImpl);
            }
        }
        // Dump modifiers specific to a class
        if (c.isAbstract_)
            System.out.print("abstract ");
        // Dump modifiers common to all
        dumpModifiers(c.modifiers_, indent);
        // Dump ctors
        Iterator iter = c.ctors_.iterator();
        while (iter.hasNext()) {
            dumpCtor((ConstructorAPI) (iter.next()), indent + indentInc);
        }
        // Dump methods
        iter = c.methods_.iterator();
        while (iter.hasNext()) {
            dumpMethod((MethodAPI) (iter.next()), indent + indentInc);
        }
        // Dump fields
        iter = c.fields_.iterator();
        while (iter.hasNext()) {
            dumpField((FieldAPI) (iter.next()), indent + indentInc);
        }
        // Display documentation
        if (c.doc_ != null) {
            System.out.print("Class doc block:");
            System.out.println("\"" + c.doc_ + "\"");
        } else
            System.out.println();
    }

    /** 
     * Display the contents of the Modifiers object.
     *
     * @param c The given Modifiers object.
     * @param indent The number of spaces to indent the output.
     */
    public static void dumpModifiers(Modifiers m, int indent) {
        for (int i = 0; i < indent; i++)
            System.out.print(" ");
        if (m.isStatic)
            System.out.print("static ");
        if (m.isFinal)
            System.out.print("final ");
        if (m.visibility != null)
            System.out.print("visibility = " + m.visibility + " ");
        // Flush the line
        System.out.println();
    }

    /** 
     * Display the contents of a constructor.
     *
     * @param c The given constructor object.
     * @param indent The number of spaces to indent the output.
     */
    public static void dumpCtor(ConstructorAPI c, int indent) {
        for (int i = 0; i < indent; i++)
            System.out.print(" ");
        System.out.println("Ctor type: " + getShortName(c.type_));
        // Display exceptions 
        System.out.print("exceptions: " + c.exceptions_ + " ");
        // Dump modifiers common to all
        dumpModifiers(c.modifiers_, indent);
        // Display documentation
        if (c.doc_ != null) {
            System.out.print("Ctor doc block:");
            System.out.println("\"" + c.doc_ + "\"");
        }
    }

    /** 
     * Display the contents of a MethodAPI object.
     *
     * @param m The given MethodAPI object.
     * @param indent The number of spaces to indent the output.
     */
    public static void dumpMethod(MethodAPI m, int indent) {
        if (m.inheritedFrom_ != null)
            return;
        for (int i = 0; i < indent; i++)
            System.out.print(" ");
        System.out.print("Method Name: " + m.name_);
        if (m.inheritedFrom_ != null)
            System.out.println(", inherited from: " + m.inheritedFrom_);
        if (m.returnType_ != null)
            System.out.println(", return type: " + m.returnType_);
        else
            System.out.println();
        // Dump modifiers specific to a method
        if (m.isAbstract_)
            System.out.print("abstract ");
        if (m.isNative_)
            System.out.print("native ");
        if (m.isSynchronized_)
            System.out.print("synchronized ");
        // Display exceptions 
        System.out.print("exceptions: " + m.exceptions_ + " ");
        // Dump modifiers common to all
        dumpModifiers(m.modifiers_, indent);

        Iterator iter = m.params_.iterator();
        while (iter.hasNext()) {
            dumpParam((ParamAPI) (iter.next()), indent + indentInc);
        }
        // Display documentation
        if (m.doc_ != null) {
            System.out.print("Method doc block:");
            System.out.println("\"" + m.doc_ + "\"");
        }
    }

    /** 
     * Display the contents of a field.
     * Does not show inherited fields.
     *
     * @param f The given field object.
     * @param indent The number of spaces to indent the output.
     */
    public static void dumpField(FieldAPI f, int indent) {
        if (f.inheritedFrom_ != null)
            return;
        for (int i = 0; i < indent; i++)
            System.out.print(" ");
        System.out.println("Field Name: " + f.name_ + ", type: " + f.type_);
        if (f.inheritedFrom_ != null)
            System.out.println(", inherited from: " + f.inheritedFrom_);
        if (f.isTransient_)
            System.out.print("transient ");
        if (f.isVolatile_)
            System.out.print("volatile ");
        // Dump modifiers common to all
        dumpModifiers(f.modifiers_, indent);
        // Display documentation
        if (f.doc_ != null)
            System.out.print("Field doc block:");
        System.out.println("\"" + f.doc_ + "\"");
    }

    /** 
     * Display the contents of a parameter.
     *
     * @param p The given parameter object.
     * @param indent The number of spaces to indent the output.
     */
    public static void dumpParam(ParamAPI p, int indent) {
        for (int i = 0; i < indent; i++)
            System.out.print(" ");
        System.out.println("Param Name: " + p.name_ + ", type: " + p.type_);
    }

    /** 
     * Convert all HTML tags to text by placing them inside a CDATA element.
     * Characters still have to be valid Unicode characters as defined by the 
     * parser.
     */
    public static String stuffHTMLTags(String htmlText) {
        if (htmlText.indexOf("]]>") != -1) {
            System.out.println("Warning: illegal string ]]> found in text. Ignoring the comment.");
            return "";
        }
        return "<![CDATA[" + htmlText + "]]>";
    }

    /**
     * Convert all HTML tags to text by stuffing text into the HTML tag
     * to stop it being an HTML or XML tag. E.g. &quot;<code>foo</code>&quot;
     * becomes &quot;lEsS_tHaNcode>foolEsS_tHaN/code>&quot;. Replace all &lt; 
     * characters
     * with the string "lEsS_tHaN". Also replace &amp; character with the  
     * string "aNd_cHaR" to avoid text entities. Also replace &quot; 
     * character with the  
     * string "qUoTe_cHaR".
     */
    public static String hideHTMLTags(String htmlText) {
        StringBuffer sb = new StringBuffer(htmlText);
        int i = 0;
        while (i < sb.length()) {
            if (sb.charAt(i) == '<') {
                sb.setCharAt(i, 'l');
                sb.insert(i + 1, "EsS_tHaN");
            } else if (sb.charAt(i) == '&') {
                sb.setCharAt(i, 'a');
                sb.insert(i + 1, "Nd_cHaR");
            } else if (sb.charAt(i) == '"') {
                sb.setCharAt(i, 'q');
                sb.insert(i + 1, "uote_cHaR");
            }
            i++;
        }
        return sb.toString();
    }

    /** 
     * Convert text with stuffed HTML tags ("lEsS_tHaN", etc) into HTML text.
     */
    public static String showHTMLTags(String text) {
        StringBuffer sb = new StringBuffer(text);
        StringBuffer res = new StringBuffer();
        int len = sb.length();
        res.setLength(len);
        int i = 0;
        int resIdx = 0;
        while (i < len) {
            char c = sb.charAt(i);
            if (len - i > 8 && c == 'l' && sb.charAt(i + 1) == 'E' && sb.charAt(i + 2) == 's'
                    && sb.charAt(i + 3) == 'S' && sb.charAt(i + 4) == '_' && sb.charAt(i + 5) == 't'
                    && sb.charAt(i + 6) == 'H' && sb.charAt(i + 7) == 'a' && sb.charAt(i + 8) == 'N') {
                res.setCharAt(resIdx, '<');
                i += 8;
            } else if (len - i > 9 && c == 'q' && sb.charAt(i + 1) == 'U' && sb.charAt(i + 2) == 'o'
                    && sb.charAt(i + 3) == 'T' && sb.charAt(i + 4) == 'e' && sb.charAt(i + 5) == '_'
                    && sb.charAt(i + 6) == 'c' && sb.charAt(i + 7) == 'H' && sb.charAt(i + 8) == 'a'
                    && sb.charAt(i + 9) == 'R') {
                res.setCharAt(resIdx, '"');
                i += 9;
            } else if (len - i > 7 && c == 'a' && sb.charAt(i + 1) == 'N' && sb.charAt(i + 2) == 'd'
                    && sb.charAt(i + 3) == '_' && sb.charAt(i + 4) == 'c' && sb.charAt(i + 5) == 'H'
                    && sb.charAt(i + 6) == 'a' && sb.charAt(i + 7) == 'R') {
                res.setCharAt(resIdx, '&');
                i += 7;
            } else {
                res.setCharAt(resIdx, c);
            }
            i++;
            resIdx++;
        }
        res.setLength(resIdx);
        return res.toString();
    }

    /** 
     * <b>NOT USED</b>. 
     *
     * Replace all instances of <p> with <p/>. Just for the small number
     * of HMTL tags which don't require a matching end tag.
     * Also make HTML conform to the simple HTML requirements such as 
     * no double hyphens. Double hyphens are replaced by - and the character
     * entity for a hyphen.
     *
     * Cases where this fails and has to be corrected in the XML by hand: 
     *  Attributes' values missing their double quotes , e.g. size=-2
     *  Mangled HTML tags e.g. &lt;ttt> 
     *
     * <p><b>NOT USED</b>. There is often too much bad HTML in
     * doc blocks to try to handle every case correctly. Better just to
     * stuff the *lt; and &amp: characters with stuffHTMLTags(). Though
     * the resulting XML is not as elegant, it does the job with less
     * intervention by the user.
     */
    public static String convertHTMLTagsToXHTML(String htmlText) {
        StringBuffer sb = new StringBuffer(htmlText);
        int i = 0;
        boolean inTag = false;
        String tag = null;
        // Needs to re-evaluate this length at each loop
        while (i < sb.length()) {
            char c = sb.charAt(i);
            if (inTag) {
                if (c == '>') {
                    // OPTION Could fail at or fix some errorneous tags here
                    // Make the best guess as to whether this tag is terminated
                    if (Comments.isMinimizedTag(tag) && htmlText.indexOf("</" + tag + ">", i) == -1)
                        sb.insert(i, "/");
                    inTag = false;
                } else {
                    // OPTION could also make sure that attribute values are
                    // surrounded by quotes.
                    tag += c;
                }
            }
            if (c == '<') {
                inTag = true;
                tag = "";
            }
            // -- is not allowed in XML, but !-- is part of an comment,
            // and --> is also part of a comment
            if (c == '-' && i > 0 && sb.charAt(i - 1) == '-') {
                if (!(i > 1 && sb.charAt(i - 2) == '!')) {
                    sb.setCharAt(i, '&');
                    sb.insert(i + 1, "#045;");
                    i += 5;
                }
            }
            i++;
        }
        if (inTag) {
            // Oops. Someone forgot to close their HTML tag, e.g. "<code."
            // Close it for them.
            sb.insert(i, ">");
        }
        return sb.toString();
    }

    public void dumpPackagesAtClassLevel(PrintWriter writer) {
        // TODO Auto-generated method stub
        Iterator iter = packages_.iterator();
        while (iter.hasNext()) {
            dumpPackageAtClassLevel((PackageAPI) (iter.next()), writer);
        }
    }

    private void dumpPackageAtClassLevel(PackageAPI packageAPI, PrintWriter writer) {
        // writer.println("Package Name: " + packageAPI.name_);
        // Display documentation
        if (packageAPI.doc_ != null) {
            // writer.println("\"" + packageAPI.doc_ + "\"");
        }
        Iterator iter = packageAPI.classes_.iterator();
        int index = 1;
        while (iter.hasNext()) {
            dumpClass((ClassAPI) (iter.next()), writer, packageAPI.name_);
            index++;
        }

    }

    private void dumpClass(ClassAPI c, PrintWriter writer, String packageName) {

        if (c.isInterface_)
            writer.println(packageName + "." + c.name_);
        else
            writer.println(packageName + "." + c.name_);
        if (c.doc_ != null) {
            writer.println(c.doc_);
        }

    }

    public void dumpPackagesAtMethodLevel(PrintWriter writer) {
        Iterator iter = packages_.iterator();
        while (iter.hasNext()) {
            dumpPackageAtMethodLevel((PackageAPI) (iter.next()), writer);
        }
    }

    private void dumpPackageAtMethodLevel(PackageAPI packageAPI, PrintWriter writer) {
        // TODO Auto-generated method stub
        if (packageAPI.doc_ != null) {
            // writer.println("\"" + packageAPI.doc_ + "\"");
        }
        Iterator iter = packageAPI.classes_.iterator();
        int index = 1;
        while (iter.hasNext()) {
            writer.println("------------------------------------------------------------------------------------");
            dumpClassAtMethodLevel((ClassAPI) (iter.next()), writer, packageAPI.name_);
            index++;
        }
    }

    private void dumpClassAtMethodLevel(ClassAPI classAPI, PrintWriter writer, String packageName) {
        writer.println("package " + packageName + ";");
        if (classAPI.isInterface_)
            writer.println("public interface " + classAPI.name_ + "{");
        else {
            writer.print("public ");

            if (classAPI.modifiers_.isStatic)
                writer.print("static ");
            if (classAPI.modifiers_.isFinal)
                writer.print("final ");

            if (classAPI.isAbstract_) {
                writer.println("abstract ");
            }
            writer.print("class " + classAPI.name_);
            if ((classAPI.extends_ != null) && (!classAPI.extends_.isEmpty())
                    && (!classAPI.extends_.equals("java.lang.Object"))) {
                writer.print(" extends ");

                writer.print(classAPI.extends_ + " ");
            }

            if (classAPI.implements_.size() != 0) {

                writer.println(" implements ");
                Iterator iter = classAPI.implements_.iterator();
                int index = 0;
                while (iter.hasNext()) {
                    String interfaceImpl = (String) (iter.next());
                    if (index > 0) {
                        writer.print(", ");
                    }
                    writer.print(interfaceImpl);
                    index++;
                }
            }
            writer.println(" {");
        }

        // Dump ctors
        Iterator iter = classAPI.ctors_.iterator();
        while (iter.hasNext()) {
            dumpCtor((ConstructorAPI) (iter.next()), classAPI.name_, writer);
        }
        // Dump methods
        iter = classAPI.methods_.iterator();
        while (iter.hasNext()) {
            dumpMethod((MethodAPI) (iter.next()), writer);
        }
        // Dump fields
        iter = classAPI.fields_.iterator();
        while (iter.hasNext()) {
            dumpField((FieldAPI) (iter.next()), writer);
        }
        writer.println("}");
    }

    private void dumpField(FieldAPI f, PrintWriter writer) {

    }

    private void dumpMethod(MethodAPI m, PrintWriter writer) {
        if (m.inheritedFrom_ != null)
            return;

        writer.print("    public ");

        if (m.modifiers_.isStatic)
            writer.print("static ");
        if (m.modifiers_.isFinal)
            writer.print("final ");

        // Dump modifiers specific to a method
        if (m.isAbstract_)
            System.out.print(" abstract");
        if (m.isNative_)
            System.out.print(" native");
        if (m.isSynchronized_)
            System.out.print(" synchronized");

        if (m.returnType_ != null)
            writer.print(getShortName(m.returnType_));
        else
            writer.print(" void ");

        writer.print(" " + m.name_);

        writer.print("(");
        Iterator iter = m.params_.iterator();
        int index = 0;
        while (iter.hasNext()) {
            if (index > 0) {
                writer.print(", ");
            }
            dumpParam((ParamAPI) (iter.next()), writer);
            index++;
        }
        writer.print(")");
        // Display exceptions
        // if ((m.exceptions_!= null) && (!m.exceptions_.isEmpty()) && (!m.exceptions_.equals("no exceptions")))
        //{
        //    writer.print(" throws " + m.exceptions_ + " ");
        //}
        writer.println(" {}");
    }

    private void dumpParam(ParamAPI p, PrintWriter writer) {
        writer.print(getShortName(p.type_) + " " + p.name_);
    }

    private static String getShortName(String fullyQualifiedName) {
        String shortName = fullyQualifiedName;
        for (int i = fullyQualifiedName.length() - 1; i >= 0; i--) {
            String c = fullyQualifiedName.substring(i, i + 1);
            if (c.equals(".")) {
                shortName = fullyQualifiedName.substring(i + 1, fullyQualifiedName.length());
                break;
            }
        }
        System.out.println(String.format("%s is shortened to %s", fullyQualifiedName, shortName));
        return shortName;
    }

    private void dumpCtor(ConstructorAPI c, String className, PrintWriter writer) {
        writer.print("    public " + getShortName(c.type_) + " ");

        if (c.modifiers_.isStatic)
            writer.print("static ");
        if (c.modifiers_.isFinal)
            writer.print("final ");

        writer.print(className);
        writer.print("(");
        writer.print(")");
        if ((c.exceptions_ != null) && (!c.exceptions_.isEmpty()) && (!c.exceptions_.equals("no exceptions"))) {
            writer.print(" throws " + c.exceptions_ + " ");
        }

        writer.println(" {}");
    }

    public void dumpPackagesAtClassLevelInCsvFormat(PrintWriter writer) {

        Iterator iter = packages_.iterator();
        while (iter.hasNext()) {
            dumpPackageAtClassLevelInCsvFormat((PackageAPI) (iter.next()), writer);
        }

    }

    private void dumpPackageAtClassLevelInCsvFormat(PackageAPI packageAPI, PrintWriter writer) {
        writer.println(packageAPI.name_);
        // Display documentation
        if (packageAPI.doc_ != null) {
            // writer.println("\"" + packageAPI.doc_ + "\"");
        }
        Iterator iter = packageAPI.classes_.iterator();
        int index = 1;
        while (iter.hasNext()) {
            dumpClassInCsvFormat((ClassAPI) (iter.next()), writer);
            index++;
        }
    }

    private void dumpClassInCsvFormat(ClassAPI classAPI, PrintWriter writer) {
        if (classAPI.isInterface_)
            writer.print(classAPI.name_);
        else
            writer.print(classAPI.name_);
        if (classAPI.doc_ != null) {
            writer.println(", " + StringEscapeUtils.escapeCsv(removeNewLine(classAPI.doc_)));
        } else {
            writer.println();
        }
    }

    private String removeNewLine(String sourceString) {
        String resultString = sourceString.replace("\n", " ").replace("\r", " ");
        return resultString;
    }

    public void dumpPackagesAtClassLevelBrief(PrintWriter writer) {
        Iterator iter = packages_.iterator();
        while (iter.hasNext()) {
            dumpPackageAtClassLevelBrief((PackageAPI) (iter.next()), writer);
        }
    }

    private void dumpPackageAtClassLevelBrief(PackageAPI packageAPI, PrintWriter writer) {
        Iterator iter = packageAPI.classes_.iterator();
        int index = 1;
        while (iter.hasNext()) {
            writer.println(packageAPI.name_ + ((ClassAPI) (iter.next())).name_);
        }
    }

}