| package jdiff; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| /** |
| * 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. "SuperProduct 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: " + c.getSignature()); |
| // 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. "<code>foo</code>" |
| * becomes "lEsS_tHaNcode>foolEsS_tHaN/code>". Replace all < |
| * characters |
| * with the string "lEsS_tHaN". Also replace & character with the |
| * string "aNd_cHaR" to avoid text entities. Also replace " |
| * 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. <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 &: 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(); |
| } |
| } |