Home | History | Annotate | Download | only in jdiff
      1 package jdiff;
      2 
      3 import java.io.*;
      4 import java.util.*;
      5 
      6 /**
      7  * The internal representation of an API.
      8  *
      9  * RootDoc could have been used for representing this, but
     10  * you cannot serialize a RootDoc object - see
     11  *  http://developer.java.sun.com/developer/bugParade/bugs/4125581.html
     12  * You might be able use Javadoc.Main() to create another RootDoc, but the
     13  * methods are package private. You can run javadoc in J2SE1.4, see:
     14  *  http://java.sun.com/j2se/1.4/docs/tooldocs/javadoc/standard-doclet.html#runningprogrammatically
     15  * but you still can't get the RootDoc object.
     16  *
     17  * The advantage of writing out an XML representation of each API is that
     18  * later runs of JDiff don't have to have Javadoc scan all the files again,
     19  * a possibly lengthy process. XML also permits other source code in
     20  * languages other than Java to be scanned to produce XML, and then versions
     21  * of JDiff can be used to create documents describing the difference in those
     22  * APIs.
     23  *
     24  * See the file LICENSE.txt for copyright details.
     25  * @author Matthew Doar, mdoar (at) pobox.com
     26  */
     27 public class API {
     28 
     29     /**
     30      * The list of all the top-level packages.
     31      * Each package contains classes, each class contains members, and so on.
     32      */
     33     public List packages_; // PackageAPI[]
     34 
     35     /**
     36      * The list of all the classes.
     37      * This is used to generate the methods and fields which are inherited,
     38      * rather than storing them in the XML file.
     39      */
     40     public Hashtable classes_;
     41 
     42     /**
     43      * The String which identifies this API, e.g. &quotSuperProduct 1.3".
     44      */
     45     public String name_ = null;
     46 
     47     /** The current package being added to during parsing. */
     48     public PackageAPI currPkg_ = null;
     49     /** The current class being added to during parsing. */
     50     public ClassAPI currClass_ = null;
     51     /** The current constructor being added to during parsing. */
     52     public ConstructorAPI currCtor_ = null;
     53     /** The current method being added to during parsing. */
     54     public MethodAPI currMethod_ = null;
     55     /** The current field being added to during parsing. */
     56     public FieldAPI currField_ = null;
     57 
     58     /** Default constructor. */
     59     public API() {
     60         packages_ = new ArrayList(); //PackageAPI[]
     61         classes_ = new Hashtable(); //ClassAPI
     62     }
     63 
     64 //
     65 // Methods to display the contents of an API object.
     66 //
     67 
     68     /** Amount by which to increment each indentation. */
     69     public static final int indentInc = 2;
     70 
     71     /** Display the contents of the API object. */
     72     public void dump() {
     73         int indent = 0;
     74         Iterator iter = packages_.iterator();
     75         while (iter.hasNext()) {
     76             dumpPackage((PackageAPI)(iter.next()), indent);
     77         }
     78     }
     79 
     80     /**
     81      * Display the contents of a PackageAPI object.
     82      *
     83      * @param pkg The given PackageAPI object.
     84      * @param indent The number of spaces to indent the output.
     85      */
     86     public void dumpPackage(PackageAPI pkg, int indent) {
     87         for (int i = 0; i < indent; i++) System.out.print(" ");
     88         System.out.println("Package Name: " + pkg.name_);
     89         Iterator iter = pkg.classes_.iterator();
     90         while (iter.hasNext()) {
     91             dumpClass((ClassAPI)(iter.next()), indent + indentInc);
     92         }
     93         // Display documentation
     94         if (pkg.doc_ != null) {
     95             System.out.print("Package doc block:");
     96             System.out.println("\"" + pkg.doc_ + "\"");
     97         }
     98     }
     99 
    100     /**
    101      * Display the contents of a ClassAPI object.
    102      *
    103      * @param c The given ClassAPI object.
    104      * @param indent The number of spaces to indent the output.
    105      */
    106     public static void dumpClass(ClassAPI c, int indent) {
    107         for (int i = 0; i < indent; i++) System.out.print(" ");
    108         if (c.isInterface_)
    109             System.out.println("Interface name: " + c.name_);
    110         else
    111             System.out.println("Class Name: " + c.name_);
    112         if (c.extends_ != null) {
    113             for (int i = 0; i < indent; i++) System.out.print(" ");
    114             System.out.println("Extends: " + c.extends_);
    115         }
    116         if (c.implements_.size() != 0) {
    117             for (int i = 0; i < indent; i++) System.out.print(" ");
    118             System.out.println("Implements: ");
    119             Iterator iter = c.implements_.iterator();
    120             while (iter.hasNext()) {
    121                 String interfaceImpl = (String)(iter.next());
    122                 for (int i = 0; i < indent + 2; i++) System.out.print(" ");
    123                 System.out.println("  " + interfaceImpl);
    124             }
    125         }
    126         // Dump modifiers specific to a class
    127         if (c.isAbstract_)
    128             System.out.print("abstract ");
    129         // Dump modifiers common to all
    130         dumpModifiers(c.modifiers_, indent);
    131         // Dump ctors
    132         Iterator iter = c.ctors_.iterator();
    133         while (iter.hasNext()) {
    134             dumpCtor((ConstructorAPI)(iter.next()), indent + indentInc);
    135         }
    136         // Dump methods
    137         iter = c.methods_.iterator();
    138         while (iter.hasNext()) {
    139             dumpMethod((MethodAPI)(iter.next()), indent + indentInc);
    140         }
    141         // Dump fields
    142         iter = c.fields_.iterator();
    143         while (iter.hasNext()) {
    144             dumpField((FieldAPI)(iter.next()), indent + indentInc);
    145         }
    146         // Display documentation
    147         if (c.doc_ != null) {
    148             System.out.print("Class doc block:");
    149             System.out.println("\"" + c.doc_ + "\"");
    150         } else
    151             System.out.println();
    152     }
    153 
    154     /**
    155      * Display the contents of the Modifiers object.
    156      *
    157      * @param c The given Modifiers object.
    158      * @param indent The number of spaces to indent the output.
    159      */
    160     public static void dumpModifiers(Modifiers m, int indent) {
    161         for (int i = 0; i < indent; i++) System.out.print(" ");
    162         if (m.isStatic)
    163             System.out.print("static ");
    164         if (m.isFinal)
    165             System.out.print("final ");
    166         if (m.visibility != null)
    167             System.out.print("visibility = " + m.visibility + " ");
    168         // Flush the line
    169         System.out.println();
    170     }
    171 
    172     /**
    173      * Display the contents of a constructor.
    174      *
    175      * @param c The given constructor object.
    176      * @param indent The number of spaces to indent the output.
    177      */
    178     public static void dumpCtor(ConstructorAPI c, int indent) {
    179         for (int i = 0; i < indent; i++) System.out.print(" ");
    180         System.out.println("Ctor type: " + c.type_);
    181         // Display exceptions
    182         System.out.print("exceptions: " + c.exceptions_ + " ");
    183         // Dump modifiers common to all
    184         dumpModifiers(c.modifiers_, indent);
    185         // Display documentation
    186         if (c.doc_ != null) {
    187             System.out.print("Ctor doc block:");
    188             System.out.println("\"" + c.doc_ + "\"");
    189         }
    190     }
    191 
    192     /**
    193      * Display the contents of a MethodAPI object.
    194      *
    195      * @param m The given MethodAPI object.
    196      * @param indent The number of spaces to indent the output.
    197      */
    198     public static void dumpMethod(MethodAPI m, int indent) {
    199         if (m.inheritedFrom_ != null)
    200             return;
    201         for (int i = 0; i < indent; i++) System.out.print(" ");
    202         System.out.print("Method Name: " + m.name_);
    203         if (m.inheritedFrom_ != null)
    204             System.out.println(", inherited from: " + m.inheritedFrom_);
    205         if (m.returnType_ != null)
    206             System.out.println(", return type: " + m.returnType_);
    207         else
    208             System.out.println();
    209         // Dump modifiers specific to a method
    210         if (m.isAbstract_)
    211             System.out.print("abstract ");
    212         if (m.isNative_)
    213             System.out.print("native ");
    214         if (m.isSynchronized_)
    215             System.out.print("synchronized ");
    216         // Display exceptions
    217         System.out.print("exceptions: " + m.exceptions_ + " ");
    218         // Dump modifiers common to all
    219         dumpModifiers(m.modifiers_, indent);
    220 
    221         Iterator iter = m.params_.iterator();
    222         while (iter.hasNext()) {
    223             dumpParam((ParamAPI)(iter.next()), indent + indentInc);
    224         }
    225         // Display documentation
    226         if (m.doc_ != null) {
    227             System.out.print("Method doc block:");
    228             System.out.println("\"" + m.doc_ + "\"");
    229         }
    230     }
    231 
    232     /**
    233      * Display the contents of a field.
    234      * Does not show inherited fields.
    235      *
    236      * @param f The given field object.
    237      * @param indent The number of spaces to indent the output.
    238      */
    239     public static void dumpField(FieldAPI f, int indent) {
    240         if (f.inheritedFrom_ != null)
    241             return;
    242         for (int i = 0; i < indent; i++) System.out.print(" ");
    243         System.out.println("Field Name: " + f.name_ + ", type: " + f.type_);
    244         if (f.inheritedFrom_ != null)
    245             System.out.println(", inherited from: " + f.inheritedFrom_);
    246         if (f.isTransient_)
    247             System.out.print("transient ");
    248         if (f.isVolatile_)
    249             System.out.print("volatile ");
    250         // Dump modifiers common to all
    251         dumpModifiers(f.modifiers_, indent);
    252         // Display documentation
    253         if (f.doc_ != null)
    254             System.out.print("Field doc block:");
    255             System.out.println("\"" + f.doc_ + "\"");
    256     }
    257 
    258     /**
    259      * Display the contents of a parameter.
    260      *
    261      * @param p The given parameter object.
    262      * @param indent The number of spaces to indent the output.
    263      */
    264     public static void dumpParam(ParamAPI p, int indent) {
    265         for (int i = 0; i < indent; i++) System.out.print(" ");
    266         System.out.println("Param Name: " + p.name_ + ", type: " + p.type_);
    267     }
    268 
    269     /**
    270      * Convert all HTML tags to text by placing them inside a CDATA element.
    271      * Characters still have to be valid Unicode characters as defined by the
    272      * parser.
    273      */
    274     public static String stuffHTMLTags(String htmlText) {
    275         if (htmlText.indexOf("]]>") != -1) {
    276             System.out.println("Warning: illegal string ]]> found in text. Ignoring the comment.");
    277             return "";
    278         }
    279         return "<![CDATA[" + htmlText + "]]>";
    280     }
    281 
    282     /**
    283      * Convert all HTML tags to text by stuffing text into the HTML tag
    284      * to stop it being an HTML or XML tag. E.g. &quot;<code>foo</code>&quot;
    285      * becomes &quot;lEsS_tHaNcode>foolEsS_tHaN/code>&quot;. Replace all &lt;
    286      * characters
    287      * with the string "lEsS_tHaN". Also replace &amp; character with the
    288      * string "aNd_cHaR" to avoid text entities. Also replace &quot;
    289      * character with the
    290      * string "qUoTe_cHaR".
    291      */
    292     public static String hideHTMLTags(String htmlText) {
    293         StringBuffer sb = new StringBuffer(htmlText);
    294         int i = 0;
    295         while (i < sb.length()) {
    296             if (sb.charAt(i) == '<') {
    297                 sb.setCharAt(i ,'l');
    298                 sb.insert(i+1, "EsS_tHaN");
    299             } else if (sb.charAt(i) == '&') {
    300                 sb.setCharAt(i ,'a');
    301                 sb.insert(i+1, "Nd_cHaR");
    302             } else if (sb.charAt(i) == '"') {
    303                 sb.setCharAt(i ,'q');
    304                 sb.insert(i+1, "uote_cHaR");
    305             }
    306             i++;
    307         }
    308         return sb.toString();
    309     }
    310 
    311     /**
    312      * Convert text with stuffed HTML tags ("lEsS_tHaN", etc) into HTML text.
    313      */
    314     public static String showHTMLTags(String text) {
    315         StringBuffer sb = new StringBuffer(text);
    316         StringBuffer res = new StringBuffer();
    317         int len = sb.length();
    318         res.setLength(len);
    319         int i = 0;
    320         int resIdx = 0;
    321         while (i < len) {
    322             char c = sb.charAt(i);
    323             if (len - i > 8 && c == 'l' &&
    324                 sb.charAt(i+1) == 'E' &&
    325                 sb.charAt(i+2) == 's' &&
    326                 sb.charAt(i+3) == 'S' &&
    327                 sb.charAt(i+4) == '_' &&
    328                 sb.charAt(i+5) == 't' &&
    329                 sb.charAt(i+6) == 'H' &&
    330                 sb.charAt(i+7) == 'a' &&
    331                 sb.charAt(i+8) == 'N') {
    332                 res.setCharAt(resIdx ,'<');
    333                 i += 8;
    334             } else if (len - i > 9 && c == 'q' &&
    335                 sb.charAt(i+1) == 'U' &&
    336                 sb.charAt(i+2) == 'o' &&
    337                 sb.charAt(i+3) == 'T' &&
    338                 sb.charAt(i+4) == 'e' &&
    339                 sb.charAt(i+5) == '_' &&
    340                 sb.charAt(i+6) == 'c' &&
    341                 sb.charAt(i+7) == 'H' &&
    342                 sb.charAt(i+8) == 'a' &&
    343                 sb.charAt(i+9) == 'R') {
    344                 res.setCharAt(resIdx ,'"');
    345                 i += 9;
    346             } else if (len - i > 7 && c == 'a' &&
    347                 sb.charAt(i+1) == 'N' &&
    348                 sb.charAt(i+2) == 'd' &&
    349                 sb.charAt(i+3) == '_' &&
    350                 sb.charAt(i+4) == 'c' &&
    351                 sb.charAt(i+5) == 'H' &&
    352                 sb.charAt(i+6) == 'a' &&
    353                 sb.charAt(i+7) == 'R') {
    354                 res.setCharAt(resIdx ,'&');
    355                 i += 7;
    356             } else {
    357                 res.setCharAt(resIdx, c);
    358             }
    359             i++;
    360             resIdx++;
    361         }
    362         res.setLength(resIdx);
    363         return res.toString();
    364     }
    365 
    366     /**
    367      * <b>NOT USED</b>.
    368      *
    369      * Replace all instances of <p> with <p/>. Just for the small number
    370      * of HMTL tags which don't require a matching end tag.
    371      * Also make HTML conform to the simple HTML requirements such as
    372      * no double hyphens. Double hyphens are replaced by - and the character
    373      * entity for a hyphen.
    374      *
    375      * Cases where this fails and has to be corrected in the XML by hand:
    376      *  Attributes' values missing their double quotes , e.g. size=-2
    377      *  Mangled HTML tags e.g. &lt;ttt>
    378      *
    379      * <p><b>NOT USED</b>. There is often too much bad HTML in
    380      * doc blocks to try to handle every case correctly. Better just to
    381      * stuff the *lt; and &amp: characters with stuffHTMLTags(). Though
    382      * the resulting XML is not as elegant, it does the job with less
    383      * intervention by the user.
    384      */
    385     public static String convertHTMLTagsToXHTML(String htmlText) {
    386         StringBuffer sb = new StringBuffer(htmlText);
    387         int i = 0;
    388         boolean inTag = false;
    389         String tag = null;
    390         // Needs to re-evaluate this length at each loop
    391         while (i < sb.length()) {
    392             char c = sb.charAt(i);
    393             if (inTag) {
    394                 if (c == '>') {
    395                     // OPTION Could fail at or fix some errorneous tags here
    396                     // Make the best guess as to whether this tag is terminated
    397                     if (Comments.isMinimizedTag(tag) &&
    398                         htmlText.indexOf("</" + tag + ">", i) == -1)
    399                         sb.insert(i, "/");
    400                     inTag = false;
    401                 } else {
    402                     // OPTION could also make sure that attribute values are
    403                     // surrounded by quotes.
    404                     tag += c;
    405                 }
    406             }
    407             if (c == '<') {
    408                 inTag = true;
    409                 tag = "";
    410             }
    411             // -- is not allowed in XML, but !-- is part of an comment,
    412             // and --> is also part of a comment
    413             if (c == '-' && i > 0 && sb.charAt(i-1) == '-') {
    414                 if (!(i > 1 && sb.charAt(i-2) == '!')) {
    415                     sb.setCharAt(i, '&');
    416                     sb.insert(i+1, "#045;");
    417                     i += 5;
    418                 }
    419             }
    420             i++;
    421         }
    422         if (inTag) {
    423             // Oops. Someone forgot to close their HTML tag, e.g. "<code."
    424             // Close it for them.
    425             sb.insert(i, ">");
    426         }
    427         return sb.toString();
    428     }
    429 }
    430