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
|
* https://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:
|
* https://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();
|
}
|
}
|