aboutsummaryrefslogtreecommitdiff
path: root/src/jdiff/RootDocToXML.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdiff/RootDocToXML.java')
-rwxr-xr-xsrc/jdiff/RootDocToXML.java1147
1 files changed, 1147 insertions, 0 deletions
diff --git a/src/jdiff/RootDocToXML.java b/src/jdiff/RootDocToXML.java
new file mode 100755
index 0000000..f68a60b
--- /dev/null
+++ b/src/jdiff/RootDocToXML.java
@@ -0,0 +1,1147 @@
+package jdiff;
+
+import com.sun.javadoc.*;
+import com.sun.javadoc.ParameterizedType;
+import com.sun.javadoc.Type;
+
+import java.util.*;
+import java.io.*;
+import java.lang.reflect.*;
+
+/**
+ * Converts a Javadoc RootDoc object into a representation in an
+ * XML file.
+ *
+ * See the file LICENSE.txt for copyright details.
+ * @author Matthew Doar, mdoar@pobox.com
+ */
+public class RootDocToXML {
+
+ /** Default constructor. */
+ public RootDocToXML() {
+ }
+
+ /**
+ * Write the XML representation of the API to a file.
+ *
+ * @param root the RootDoc object passed by Javadoc
+ * @return true if no problems encountered
+ */
+ public static boolean writeXML(RootDoc root) {
+ String tempFileName = outputFileName;
+ if (outputDirectory != null) {
+ tempFileName = outputDirectory;
+ if (!tempFileName.endsWith(JDiff.DIR_SEP))
+ tempFileName += JDiff.DIR_SEP;
+ tempFileName += outputFileName;
+ }
+
+ try {
+ FileOutputStream fos = new FileOutputStream(tempFileName);
+ outputFile = new PrintWriter(fos);
+ System.out.println("JDiff: writing the API to file '" + tempFileName + "'...");
+ if (root.specifiedPackages().length != 0 || root.specifiedClasses().length != 0) {
+ RootDocToXML apiWriter = new RootDocToXML();
+ apiWriter.emitXMLHeader();
+ apiWriter.logOptions();
+ apiWriter.processPackages(root);
+ apiWriter.emitXMLFooter();
+ }
+ outputFile.close();
+ } catch(IOException e) {
+ System.out.println("IO Error while attempting to create " + tempFileName);
+ System.out.println("Error: " + e.getMessage());
+ System.exit(1);
+ }
+ // If validation is desired, write out the appropriate api.xsd file
+ // in the same directory as the XML file.
+ if (XMLToAPI.validateXML) {
+ writeXSD();
+ }
+ return true;
+ }
+
+ /**
+ * Write the XML Schema file used for validation.
+ */
+ public static void writeXSD() {
+ String xsdFileName = outputFileName;
+ if (outputDirectory == null) {
+ int idx = xsdFileName.lastIndexOf('\\');
+ int idx2 = xsdFileName.lastIndexOf('/');
+ if (idx == -1 && idx2 == -1) {
+ xsdFileName = "";
+ } else if (idx == -1 && idx2 != -1) {
+ xsdFileName = xsdFileName.substring(0, idx2);
+ } else if (idx != -1 && idx2 == -1) {
+ xsdFileName = xsdFileName.substring(0, idx);
+ } else if (idx != -1 && idx2 != -1) {
+ int max = idx2 > idx ? idx2 : idx;
+ xsdFileName = xsdFileName.substring(0, max);
+ }
+ } else {
+ xsdFileName = outputDirectory;
+ if (!xsdFileName.endsWith(JDiff.DIR_SEP))
+ xsdFileName += JDiff.DIR_SEP;
+ }
+ xsdFileName += "api.xsd";
+ try {
+ FileOutputStream fos = new FileOutputStream(xsdFileName);
+ PrintWriter xsdFile = new PrintWriter(fos);
+ // The contents of the api.xsd file
+ xsdFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
+ xsdFile.println("<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">");
+ xsdFile.println("");
+ xsdFile.println("<xsd:annotation>");
+ xsdFile.println(" <xsd:documentation>");
+ xsdFile.println(" Schema for JDiff API representation.");
+ xsdFile.println(" </xsd:documentation>");
+ xsdFile.println("</xsd:annotation>");
+ xsdFile.println();
+ xsdFile.println("<xsd:element name=\"api\" type=\"apiType\"/>");
+ xsdFile.println("");
+ xsdFile.println("<xsd:complexType name=\"apiType\">");
+ xsdFile.println(" <xsd:sequence>");
+ xsdFile.println(" <xsd:element name=\"package\" type=\"packageType\" minOccurs='1' maxOccurs='unbounded'/>");
+ xsdFile.println(" </xsd:sequence>");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"jdversion\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"packageType\">");
+ xsdFile.println(" <xsd:sequence>");
+ xsdFile.println(" <xsd:choice maxOccurs='unbounded'>");
+ xsdFile.println(" <xsd:element name=\"class\" type=\"classType\"/>");
+ xsdFile.println(" <xsd:element name=\"interface\" type=\"classType\"/>");
+ xsdFile.println(" </xsd:choice>");
+ xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
+ xsdFile.println(" </xsd:sequence>");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"classType\">");
+ xsdFile.println(" <xsd:sequence>");
+ xsdFile.println(" <xsd:element name=\"implements\" type=\"interfaceTypeName\" minOccurs='0' maxOccurs='unbounded'/>");
+ xsdFile.println(" <xsd:element name=\"constructor\" type=\"constructorType\" minOccurs='0' maxOccurs='unbounded'/>");
+ xsdFile.println(" <xsd:element name=\"method\" type=\"methodType\" minOccurs='0' maxOccurs='unbounded'/>");
+ xsdFile.println(" <xsd:element name=\"field\" type=\"fieldType\" minOccurs='0' maxOccurs='unbounded'/>");
+ xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
+ xsdFile.println(" </xsd:sequence>");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"extends\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"abstract\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"interfaceTypeName\">");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"constructorType\">");
+ xsdFile.println(" <xsd:sequence>");
+ xsdFile.println(" <xsd:element name=\"exception\" type=\"exceptionType\" minOccurs='0' maxOccurs='unbounded'/>");
+ xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
+ xsdFile.println(" </xsd:sequence>");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"paramsType\">");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"exceptionType\">");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"methodType\">");
+ xsdFile.println(" <xsd:sequence>");
+ xsdFile.println(" <xsd:element name=\"param\" type=\"paramsType\" minOccurs='0' maxOccurs='unbounded'/>");
+ xsdFile.println(" <xsd:element name=\"exception\" type=\"exceptionType\" minOccurs='0' maxOccurs='unbounded'/>");
+ xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
+ xsdFile.println(" </xsd:sequence>");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"return\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"abstract\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"native\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"synchronized\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("<xsd:complexType name=\"fieldType\">");
+ xsdFile.println(" <xsd:sequence>");
+ xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
+ xsdFile.println(" </xsd:sequence>");
+ xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"transient\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"volatile\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"value\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
+ xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
+ xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
+ xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
+ xsdFile.println("</xsd:complexType>");
+ xsdFile.println();
+ xsdFile.println("</xsd:schema>");
+ xsdFile.close();
+ } catch(IOException e) {
+ System.out.println("IO Error while attempting to create " + xsdFileName);
+ System.out.println("Error: " + e.getMessage());
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Write the options which were used to generate this XML file
+ * out as XML comments.
+ */
+ public void logOptions() {
+ outputFile.print("<!-- ");
+ outputFile.print(" Command line arguments = " + Options.cmdOptions);
+ outputFile.println(" -->");
+ }
+
+ /**
+ * Process each package and the classes/interfaces within it.
+ *
+ * @param pd an array of PackageDoc objects
+ */
+ public void processPackages(RootDoc root) {
+ PackageDoc[] specified_pd = root.specifiedPackages();
+ Map pdl = new TreeMap();
+ for (int i = 0; specified_pd != null && i < specified_pd.length; i++) {
+ pdl.put(specified_pd[i].name(), specified_pd[i]);
+ }
+
+ // Classes may be specified separately, so merge their packages into the
+ // list of specified packages.
+ ClassDoc[] cd = root.specifiedClasses();
+ // This is lists of the specific classes to document
+ Map classesToUse = new HashMap();
+ for (int i = 0; cd != null && i < cd.length; i++) {
+ PackageDoc cpd = cd[i].containingPackage();
+ if (cpd == null && !packagesOnly) {
+ // If the RootDoc object has been created from a jar file
+ // this duplicates classes, so we have to be able to disable it.
+ // TODO this is still null?
+ cpd = root.packageNamed("anonymous");
+ }
+ String pkgName = cpd.name();
+ String className = cd[i].name();
+ if (trace) System.out.println("Found package " + pkgName + " for class " + className);
+ if (!pdl.containsKey(pkgName)) {
+ if (trace) System.out.println("Adding new package " + pkgName);
+ pdl.put(pkgName, cpd);
+ }
+
+ // Keep track of the specific classes to be used for this package
+ List classes;
+ if (classesToUse.containsKey(pkgName)) {
+ classes = (ArrayList) classesToUse.get(pkgName);
+ } else {
+ classes = new ArrayList();
+ }
+ classes.add(cd[i]);
+ classesToUse.put(pkgName, classes);
+ }
+
+ PackageDoc[] pd = (PackageDoc[]) pdl.values().toArray(new PackageDoc[0]);
+ for (int i = 0; pd != null && i < pd.length; i++) {
+ String pkgName = pd[i].name();
+
+ // Check for an exclude tag in the package doc block, but not
+ // in the package.htm[l] file.
+ if (!shownElement(pd[i], null))
+ continue;
+
+ if (trace) System.out.println("PROCESSING PACKAGE: " + pkgName);
+ outputFile.println("<package name=\"" + pkgName + "\">");
+
+ int tagCount = pd[i].tags().length;
+ if (trace) System.out.println("#tags: " + tagCount);
+
+ List classList;
+ if (classesToUse.containsKey(pkgName)) {
+ // Use only the specified classes in the package
+ System.out.println("Using the specified classes");
+ classList = (ArrayList) classesToUse.get(pkgName);
+ } else {
+ // Use all classes in the package
+ classList = new LinkedList(Arrays.asList(pd[i].allClasses()));
+ }
+ Collections.sort(classList);
+ ClassDoc[] classes = new ClassDoc[classList.size()];
+ classes = (ClassDoc[])classList.toArray(classes);
+ processClasses(classes, pkgName);
+
+ addPkgDocumentation(root, pd[i], 2);
+
+ outputFile.println("</package>");
+ }
+ } // processPackages
+
+ /**
+ * Process classes and interfaces.
+ *
+ * @param cd An array of ClassDoc objects.
+ */
+ public void processClasses(ClassDoc[] cd, String pkgName) {
+ if (cd.length == 0)
+ return;
+ if (trace) System.out.println("PROCESSING CLASSES, number=" + cd.length);
+ for (int i = 0; i < cd.length; i++) {
+ String className = cd[i].name();
+ if (trace) System.out.println("PROCESSING CLASS/IFC: " + className);
+ // Only save the shown elements
+ if (!shownElement(cd[i], classVisibilityLevel))
+ continue;
+ boolean isInterface = false;
+ if (cd[i].isInterface())
+ isInterface = true;
+ if (isInterface) {
+ outputFile.println(" <!-- start interface " + pkgName + "." + className + " -->");
+ outputFile.print(" <interface name=\"" + className + "\"");
+ } else {
+ outputFile.println(" <!-- start class " + pkgName + "." + className + " -->");
+ outputFile.print(" <class name=\"" + className + "\"");
+ }
+ // Add attributes to the class element
+ Type parent = cd[i].superclassType();
+ if (parent != null)
+ outputFile.println(" extends=\"" + buildEmittableTypeString(parent) + "\"");
+ outputFile.println(" abstract=\"" + cd[i].isAbstract() + "\"");
+ addCommonModifiers(cd[i], 4);
+ outputFile.println(">");
+ // Process class members. (Treat inner classes as members.)
+ processInterfaces(cd[i].interfaceTypes());
+ processConstructors(cd[i].constructors());
+ processMethods(cd[i], cd[i].methods());
+ processFields(cd[i].fields());
+
+ addDocumentation(cd[i], 4);
+
+ if (isInterface) {
+ outputFile.println(" </interface>");
+ outputFile.println(" <!-- end interface " + pkgName + "." + className + " -->");
+ } else {
+ outputFile.println(" </class>");
+ outputFile.println(" <!-- end class " + pkgName + "." + className + " -->");
+ }
+ // Inner classes have already been added.
+ /*
+ ClassDoc[] ic = cd[i].innerClasses();
+ for (int k = 0; k < ic.length; k++) {
+ System.out.println("Inner class " + k + ", name = " + ic[k].name());
+ }
+ */
+ }//for
+ }//processClasses()
+
+ /**
+ * Add qualifiers for the program element as attributes.
+ *
+ * @param ped The given program element.
+ */
+ public void addCommonModifiers(ProgramElementDoc ped, int indent) {
+ addSourcePosition(ped, indent);
+ // Static and final and visibility on one line
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ outputFile.print("static=\"" + ped.isStatic() + "\"");
+ outputFile.print(" final=\"" + ped.isFinal() + "\"");
+ // Visibility
+ String visibility = null;
+ if (ped.isPublic())
+ visibility = "public";
+ else if (ped.isProtected())
+ visibility = "protected";
+ else if (ped.isPackagePrivate())
+ visibility = "package";
+ else if (ped.isPrivate())
+ visibility = "private";
+ outputFile.println(" visibility=\"" + visibility + "\"");
+
+ // Deprecation on its own line
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ boolean isDeprecated = false;
+ Tag[] ta = ((Doc)ped).tags("deprecated");
+ if (ta.length != 0) {
+ isDeprecated = true;
+ }
+ if (ta.length > 1) {
+ System.out.println("JDiff: warning: multiple @deprecated tags found in comments for " + ped.name() + ". Using the first one only.");
+ System.out.println("Text is: " + ((Doc)ped).getRawCommentText());
+ }
+ if (isDeprecated) {
+ String text = ta[0].text(); // Use only one @deprecated tag
+ if (text != null && text.compareTo("") != 0) {
+ int idx = endOfFirstSentence(text);
+ if (idx == 0) {
+ // No useful comment
+ outputFile.print("deprecated=\"deprecated, no comment\"");
+ } else {
+ String fs = null;
+ if (idx == -1)
+ fs = text;
+ else
+ fs = text.substring(0, idx+1);
+ String st = API.hideHTMLTags(fs);
+ outputFile.print("deprecated=\"" + st + "\"");
+ }
+ } else {
+ outputFile.print("deprecated=\"deprecated, no comment\"");
+ }
+ } else {
+ outputFile.print("deprecated=\"not deprecated\"");
+ }
+
+ } //addQualifiers()
+
+ /**
+ * Insert the source code details, if available.
+ *
+ * @param ped The given program element.
+ */
+ public void addSourcePosition(ProgramElementDoc ped, int indent) {
+ if (!addSrcInfo)
+ return;
+ if (JDiff.javaVersion.startsWith("1.1") ||
+ JDiff.javaVersion.startsWith("1.2") ||
+ JDiff.javaVersion.startsWith("1.3")) {
+ return; // position() only appeared in J2SE1.4
+ }
+ try {
+ // Could cache the method for improved performance
+ Class c = ProgramElementDoc.class;
+ Method m = c.getMethod("position", (Class[]) null);
+ Object sp = m.invoke(ped, (Object[]) null);
+ if (sp != null) {
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ outputFile.println("src=\"" + sp + "\"");
+ }
+ } catch (NoSuchMethodException e2) {
+ System.err.println("Error: method \"position\" not found");
+ e2.printStackTrace();
+ } catch (IllegalAccessException e4) {
+ System.err.println("Error: class not permitted to be instantiated");
+ e4.printStackTrace();
+ } catch (InvocationTargetException e5) {
+ System.err.println("Error: method \"position\" could not be invoked");
+ e5.printStackTrace();
+ } catch (Exception e6) {
+ System.err.println("Error: ");
+ e6.printStackTrace();
+ }
+ }
+
+ /**
+ * Process the interfaces implemented by the class.
+ *
+ * @param ifaces An array of ClassDoc objects
+ */
+ public void processInterfaces(Type[] ifaces) {
+ if (trace) System.out.println("PROCESSING INTERFACES, number=" + ifaces.length);
+ for (int i = 0; i < ifaces.length; i++) {
+ String ifaceName = buildEmittableTypeString(ifaces[i]);
+ if (trace) System.out.println("PROCESSING INTERFACE: " + ifaceName);
+ outputFile.println(" <implements name=\"" + ifaceName + "\"/>");
+ }//for
+ }//processInterfaces()
+
+ /**
+ * Process the constructors in the class.
+ *
+ * @param ct An array of ConstructorDoc objects
+ */
+ public void processConstructors(ConstructorDoc[] ct) {
+ if (trace) System.out.println("PROCESSING CONSTRUCTORS, number=" + ct.length);
+ for (int i = 0; i < ct.length; i++) {
+ String ctorName = ct[i].name();
+ if (trace) System.out.println("PROCESSING CONSTRUCTOR: " + ctorName);
+ // Only save the shown elements
+ if (!shownElement(ct[i], memberVisibilityLevel))
+ continue;
+ outputFile.print(" <constructor name=\"" + ctorName + "\"");
+
+ Parameter[] params = ct[i].parameters();
+ boolean first = true;
+ if (params.length != 0) {
+ outputFile.print(" type=\"");
+ for (int j = 0; j < params.length; j++) {
+ if (!first)
+ outputFile.print(", ");
+ emitType(params[j].type());
+ first = false;
+ }
+ outputFile.println("\"");
+ } else
+ outputFile.println();
+ addCommonModifiers(ct[i], 6);
+ outputFile.println(">");
+
+ // Generate the exception elements if any exceptions are thrown
+ processExceptions(ct[i].thrownExceptions());
+
+ addDocumentation(ct[i], 6);
+
+ outputFile.println(" </constructor>");
+ }//for
+ }//processConstructors()
+
+ /**
+ * Process all exceptions thrown by a constructor or method.
+ *
+ * @param cd An array of ClassDoc objects
+ */
+ public void processExceptions(ClassDoc[] cd) {
+ if (trace) System.out.println("PROCESSING EXCEPTIONS, number=" + cd.length);
+ for (int i = 0; i < cd.length; i++) {
+ String exceptionName = cd[i].name();
+ if (trace) System.out.println("PROCESSING EXCEPTION: " + exceptionName);
+ outputFile.print(" <exception name=\"" + exceptionName + "\" type=\"");
+ emitType(cd[i]);
+ outputFile.println("\"/>");
+ }//for
+ }//processExceptions()
+
+ /**
+ * Process the methods in the class.
+ *
+ * @param md An array of MethodDoc objects
+ */
+ public void processMethods(ClassDoc cd, MethodDoc[] md) {
+ if (trace) System.out.println("PROCESSING " +cd.name()+" METHODS, number = " + md.length);
+ for (int i = 0; i < md.length; i++) {
+ String methodName = md[i].name();
+ if (trace) System.out.println("PROCESSING METHOD: " + methodName);
+ // Skip <init> and <clinit>
+ if (methodName.startsWith("<"))
+ continue;
+ // Only save the shown elements
+ if (!shownElement(md[i], memberVisibilityLevel))
+ continue;
+ outputFile.print(" <method name=\"" + methodName + "\"");
+ com.sun.javadoc.Type retType = md[i].returnType();
+ if (retType.qualifiedTypeName().compareTo("void") == 0) {
+ // Don't add a return attribute if the return type is void
+ outputFile.println();
+ } else {
+ outputFile.print(" return=\"");
+ emitType(retType);
+ outputFile.println("\"");
+ }
+ outputFile.print(" abstract=\"" + md[i].isAbstract() + "\"");
+ outputFile.print(" native=\"" + md[i].isNative() + "\"");
+ outputFile.println(" synchronized=\"" + md[i].isSynchronized() + "\"");
+ addCommonModifiers(md[i], 6);
+ outputFile.println(">");
+ // Generate the parameter elements, if any
+ Parameter[] params = md[i].parameters();
+ for (int j = 0; j < params.length; j++) {
+ outputFile.print(" <param name=\"" + params[j].name() + "\"");
+ outputFile.print(" type=\"");
+ emitType(params[j].type());
+ outputFile.println("\"/>");
+ }
+
+ // Generate the exception elements if any exceptions are thrown
+ processExceptions(md[i].thrownExceptions());
+
+ addDocumentation(md[i], 6);
+
+ outputFile.println(" </method>");
+ }//for
+ }//processMethods()
+
+ /**
+ * Process the fields in the class.
+ *
+ * @param fd An array of FieldDoc objects
+ */
+ public void processFields(FieldDoc[] fd) {
+ if (trace) System.out.println("PROCESSING FIELDS, number=" + fd.length);
+ for (int i = 0; i < fd.length; i++) {
+ String fieldName = fd[i].name();
+ if (trace) System.out.println("PROCESSING FIELD: " + fieldName);
+ // Only save the shown elements
+ if (!shownElement(fd[i], memberVisibilityLevel))
+ continue;
+ outputFile.print(" <field name=\"" + fieldName + "\"");
+ outputFile.print(" type=\"");
+ emitType(fd[i].type());
+ outputFile.println("\"");
+ outputFile.print(" transient=\"" + fd[i].isTransient() + "\"");
+ outputFile.println(" volatile=\"" + fd[i].isVolatile() + "\"");
+/* JDK 1.4 and later */
+/*
+ String value = fd[i].constantValueExpression();
+ if (value != null)
+ outputFile.println(" value=\"" + value + "\"");
+*/
+ addCommonModifiers(fd[i], 6);
+ outputFile.println(">");
+
+ addDocumentation(fd[i], 6);
+
+ outputFile.println(" </field>");
+
+ }//for
+ }//processFields()
+
+ /**
+ * Emit the type name. Removed any prefixed warnings about ambiguity.
+ * The type maybe an array.
+ *
+ * @param type A Type object.
+ */
+ public void emitType(com.sun.javadoc.Type type) {
+ String name = buildEmittableTypeString(type);
+ if (name == null)
+ return;
+ outputFile.print(name);
+ }
+
+ /**
+ * Build the emittable type name. The type may be an array and/or
+ * a generic type.
+ *
+ * @param type A Type object
+ * @return The emittable type name
+ */
+ private String buildEmittableTypeString(com.sun.javadoc.Type type) {
+ if (type == null) {
+ return null;
+ }
+ // type.toString() returns the fully qualified name of the type
+ // including the dimension and the parameters we just need to
+ // escape the generic parameters brackets so that the XML
+ // generated is correct
+ String name = type.toString().replaceAll("<", "&lt;").replaceAll(">", "&gt;");
+ if (name.startsWith("<<ambiguous>>")) {
+ name = name.substring(13);
+ }
+ return name;
+ }
+
+ /**
+ * Emit the XML header.
+ */
+ public void emitXMLHeader() {
+ outputFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
+ outputFile.println("<!-- Generated by the JDiff Javadoc doclet -->");
+ outputFile.println("<!-- (" + JDiff.jDiffLocation + ") -->");
+ outputFile.println("<!-- on " + new Date() + " -->");
+ outputFile.println();
+/* No need for this any longer, since doc block text is in an CDATA element
+ outputFile.println("<!-- XML Schema is used, but XHTML transitional DTD is needed for nbsp -->");
+ outputFile.println("<!-- entity definitions etc.-->");
+ outputFile.println("<!DOCTYPE api");
+ outputFile.println(" PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
+ outputFile.println(" \"" + baseURI + "/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
+*/
+ outputFile.println("<api");
+ outputFile.println(" xmlns:xsi='" + baseURI + "/2001/XMLSchema-instance'");
+ outputFile.println(" xsi:noNamespaceSchemaLocation='api.xsd'");
+ outputFile.println(" name=\"" + apiIdentifier + "\"");
+ outputFile.println(" jdversion=\"" + JDiff.version + "\">");
+ outputFile.println();
+ }
+
+ /**
+ * Emit the XML footer.
+ */
+ public void emitXMLFooter() {
+ outputFile.println();
+ outputFile.println("</api>");
+ }
+
+ /**
+ * Determine if the program element is shown, according to the given
+ * level of visibility.
+ *
+ * @param ped The given program element.
+ * @param visLevel The desired visibility level; "public", "protected",
+ * "package" or "private". If null, only check for an exclude tag.
+ * @return boolean Set if this element is shown.
+ */
+ public boolean shownElement(Doc doc, String visLevel) {
+ // If a doc block contains @exclude or a similar such tag,
+ // then don't display it.
+ if (doExclude && excludeTag != null && doc != null) {
+ String rct = doc.getRawCommentText();
+ if (rct != null && rct.indexOf(excludeTag) != -1) {
+ return false;
+ }
+ }
+ if (visLevel == null) {
+ return true;
+ }
+ ProgramElementDoc ped = null;
+ if (doc instanceof ProgramElementDoc) {
+ ped = (ProgramElementDoc)doc;
+ }
+ if (visLevel.compareTo("private") == 0)
+ return true;
+ // Show all that is not private
+ if (visLevel.compareTo("package") == 0)
+ return !ped.isPrivate();
+ // Show all that is not private or package
+ if (visLevel.compareTo("protected") == 0)
+ return !(ped.isPrivate() || ped.isPackagePrivate());
+ // Show all that is not private or package or protected,
+ // i.e. all that is public
+ if (visLevel.compareTo("public") == 0)
+ return ped.isPublic();
+ return false;
+ } //shownElement()
+
+ /**
+ * Strip out non-printing characters, replacing them with a character
+ * which will not change where the end of the first sentence is found.
+ * This character is the hash mark, '&#035;'.
+ */
+ public String stripNonPrintingChars(String s, Doc doc) {
+ if (!stripNonPrintables)
+ return s;
+ char[] sa = s.toCharArray();
+ for (int i = 0; i < sa.length; i++) {
+ char c = sa[i];
+ // TODO still have an issue with Unicode: 0xfc in java.lang.String.toUpperCase comments
+// if (Character.isDefined(c))
+ if (Character.isLetterOrDigit(c))
+ continue;
+ // There must be a better way that is still platform independent!
+ if (c == ' ' ||
+ c == '.' ||
+ c == ',' ||
+ c == '\r' ||
+ c == '\t' ||
+ c == '\n' ||
+ c == '!' ||
+ c == '?' ||
+ c == ';' ||
+ c == ':' ||
+ c == '[' ||
+ c == ']' ||
+ c == '(' ||
+ c == ')' ||
+ c == '~' ||
+ c == '@' ||
+ c == '#' ||
+ c == '$' ||
+ c == '%' ||
+ c == '^' ||
+ c == '&' ||
+ c == '*' ||
+ c == '-' ||
+ c == '=' ||
+ c == '+' ||
+ c == '_' ||
+ c == '|' ||
+ c == '\\' ||
+ c == '/' ||
+ c == '\'' ||
+ c == '}' ||
+ c == '{' ||
+ c == '"' ||
+ c == '<' ||
+ c == '>' ||
+ c == '`'
+ )
+ continue;
+/* Doesn't seem to return the expected values?
+ int val = Character.getNumericValue(c);
+// if (s.indexOf("which is also a test for non-printable") != -1)
+// System.out.println("** Char " + i + "[" + c + "], val =" + val); //DEBUG
+ // Ranges from http://www.unicode.org/unicode/reports/tr20/
+ // Should really replace 0x2028 and 0x2029 with <br/>
+ if (val == 0x0 ||
+ inRange(val, 0x2028, 0x2029) ||
+ inRange(val, 0x202A, 0x202E) ||
+ inRange(val, 0x206A, 0x206F) ||
+ inRange(val, 0xFFF9, 0xFFFC) ||
+ inRange(val, 0xE0000, 0xE007F)) {
+ if (trace) {
+ System.out.println("Warning: changed non-printing character " + sa[i] + " in " + doc.name());
+ }
+ sa[i] = '#';
+ }
+*/
+ // Replace the non-printable character with a printable character
+ // which does not change the end of the first sentence
+ sa[i] = '#';
+ }
+ return new String(sa);
+ }
+
+ /** Return true if val is in the range [min|max], inclusive. */
+ public boolean inRange(int val, int min, int max) {
+ if (val < min)
+ return false;
+ if (val > max)
+ return false;
+ return true;
+ }
+
+ /**
+ * Add at least the first sentence from a doc block to the API. This is
+ * used by the report generator if no comment is provided.
+ * Need to make sure that HTML tags are not confused with XML tags.
+ * This could be done by stuffing the &lt; character to another string
+ * or by handling HTML in the parser. This second option seems neater. Note that
+ * XML expects all element tags to have either a closing "/>" or a matching
+ * end element tag. Due to the difficulties of converting incorrect HTML
+ * to XHTML, the first option is used.
+ */
+ public void addDocumentation(ProgramElementDoc ped, int indent) {
+ String rct = ((Doc)ped).getRawCommentText();
+ if (rct != null) {
+ rct = stripNonPrintingChars(rct, (Doc)ped);
+ rct = rct.trim();
+ if (rct.compareTo("") != 0 &&
+ rct.indexOf(Comments.placeHolderText) == -1 &&
+ rct.indexOf("InsertOtherCommentsHere") == -1) {
+ int idx = endOfFirstSentence(rct);
+ if (idx == 0)
+ return;
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ outputFile.println("<doc>");
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ String firstSentence = null;
+ if (idx == -1)
+ firstSentence = rct;
+ else
+ firstSentence = rct.substring(0, idx+1);
+ boolean checkForAts = false;
+ if (checkForAts && firstSentence.indexOf("@") != -1 &&
+ firstSentence.indexOf("@link") == -1) {
+ System.out.println("Warning: @ tag seen in comment: " +
+ firstSentence);
+ }
+ String firstSentenceNoTags = API.stuffHTMLTags(firstSentence);
+ outputFile.println(firstSentenceNoTags);
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ outputFile.println("</doc>");
+ }
+ }
+ }
+
+ /**
+ * Add at least the first sentence from a doc block for a package to the API. This is
+ * used by the report generator if no comment is provided.
+ * The default source tree may not include the package.html files, so
+ * this may be unavailable in many cases.
+ * Need to make sure that HTML tags are not confused with XML tags.
+ * This could be done by stuffing the &lt; character to another string
+ * or by handling HTML in the parser. This second option is neater. Note that
+ * XML expects all element tags to have either a closing "/>" or a matching
+ * end element tag. Due to the difficulties of converting incorrect HTML
+ * to XHTML, the first option is used.
+ */
+ public void addPkgDocumentation(RootDoc root, PackageDoc pd, int indent) {
+ String rct = null;
+ String filename = pd.name();
+ try {
+ // See if the source path was specified as part of the
+ // options and prepend it if it was.
+ String srcLocation = null;
+ String[][] options = root.options();
+ for (int opt = 0; opt < options.length; opt++) {
+ if ((options[opt][0]).compareTo("-sourcepath") == 0) {
+ srcLocation = options[opt][1];
+ break;
+ }
+ }
+ filename = filename.replace('.', JDiff.DIR_SEP.charAt(0));
+ if (srcLocation != null) {
+ // Make a relative location absolute
+ if (srcLocation.startsWith("..")) {
+ String curDir = System.getProperty("user.dir");
+ while (srcLocation.startsWith("..")) {
+ srcLocation = srcLocation.substring(3);
+ int idx = curDir.lastIndexOf(JDiff.DIR_SEP);
+ curDir = curDir.substring(0, idx+1);
+ }
+ srcLocation = curDir + srcLocation;
+ }
+ filename = srcLocation + JDiff.DIR_SEP + filename;
+ }
+ // Try both ".htm" and ".html"
+ filename += JDiff.DIR_SEP + "package.htm";
+ File f2 = new File(filename);
+ if (!f2.exists()) {
+ filename += "l";
+ }
+ FileInputStream f = new FileInputStream(filename);
+ BufferedReader d = new BufferedReader(new InputStreamReader(f));
+ String str = d.readLine();
+ // Ignore everything except the lines between <body> elements
+ boolean inBody = false;
+ while(str != null) {
+ if (!inBody) {
+ if (str.toLowerCase().trim().startsWith("<body")) {
+ inBody = true;
+ }
+ str = d.readLine(); // Get the next line
+ continue; // Ignore the line
+ } else {
+ if (str.toLowerCase().trim().startsWith("</body")) {
+ inBody = false;
+ continue; // Ignore the line
+ }
+ }
+ if (rct == null)
+ rct = str + "\n";
+ else
+ rct += str + "\n";
+ str = d.readLine();
+ }
+ } catch(java.io.FileNotFoundException e) {
+ // If it doesn't exist, that's fine
+ if (trace)
+ System.out.println("No package level documentation file at '" + filename + "'");
+ } catch(java.io.IOException e) {
+ System.out.println("Error reading file \"" + filename + "\": " + e.getMessage());
+ System.exit(5);
+ }
+ if (rct != null) {
+ rct = stripNonPrintingChars(rct, (Doc)pd);
+ rct = rct.trim();
+ if (rct.compareTo("") != 0 &&
+ rct.indexOf(Comments.placeHolderText) == -1 &&
+ rct.indexOf("InsertOtherCommentsHere") == -1) {
+ int idx = endOfFirstSentence(rct);
+ if (idx == 0)
+ return;
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ outputFile.println("<doc>");
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ String firstSentence = null;
+ if (idx == -1)
+ firstSentence = rct;
+ else
+ firstSentence = rct.substring(0, idx+1);
+ String firstSentenceNoTags = API.stuffHTMLTags(firstSentence);
+ outputFile.println(firstSentenceNoTags);
+ for (int i = 0; i < indent; i++) outputFile.print(" ");
+ outputFile.println("</doc>");
+ }
+ }
+ }
+
+ /**
+ * Find the index of the end of the first sentence in the given text,
+ * when writing out to an XML file.
+ * This is an extended version of the algorithm used by the DocCheck
+ * Javadoc doclet. It checks for @tags too.
+ *
+ * @param text The text to be searched.
+ * @return The index of the end of the first sentence. If there is no
+ * end, return -1. If there is no useful text, return 0.
+ * If the whole doc block comment is wanted (default), return -1.
+ */
+ public static int endOfFirstSentence(String text) {
+ return endOfFirstSentence(text, true);
+ }
+
+ /**
+ * Find the index of the end of the first sentence in the given text.
+ * This is an extended version of the algorithm used by the DocCheck
+ * Javadoc doclet. It checks for &#064;tags too.
+ *
+ * @param text The text to be searched.
+ * @param writingToXML Set to true when writing out XML.
+ * @return The index of the end of the first sentence. If there is no
+ * end, return -1. If there is no useful text, return 0.
+ * If the whole doc block comment is wanted (default), return -1.
+ */
+ public static int endOfFirstSentence(String text, boolean writingToXML) {
+ if (saveAllDocs && writingToXML)
+ return -1;
+ int textLen = text.length();
+ if (textLen == 0)
+ return 0;
+ int index = -1;
+ // Handle some special cases
+ int fromindex = 0;
+ int ellipsis = text.indexOf(". . ."); // Handles one instance of this
+ if (ellipsis != -1)
+ fromindex = ellipsis + 5;
+ // If the first non-whitespace character is an @, go beyond it
+ int i = 0;
+ while (i < textLen && text.charAt(i) == ' ') {
+ i++;
+ }
+ if (text.charAt(i) == '@' && fromindex < textLen-1)
+ fromindex = i + 1;
+ // Use the brute force approach.
+ index = minIndex(index, text.indexOf("? ", fromindex));
+ index = minIndex(index, text.indexOf("?\t", fromindex));
+ index = minIndex(index, text.indexOf("?\n", fromindex));
+ index = minIndex(index, text.indexOf("?\r", fromindex));
+ index = minIndex(index, text.indexOf("?\f", fromindex));
+ index = minIndex(index, text.indexOf("! ", fromindex));
+ index = minIndex(index, text.indexOf("!\t", fromindex));
+ index = minIndex(index, text.indexOf("!\n", fromindex));
+ index = minIndex(index, text.indexOf("!\r", fromindex));
+ index = minIndex(index, text.indexOf("!\f", fromindex));
+ index = minIndex(index, text.indexOf(". ", fromindex));
+ index = minIndex(index, text.indexOf(".\t", fromindex));
+ index = minIndex(index, text.indexOf(".\n", fromindex));
+ index = minIndex(index, text.indexOf(".\r", fromindex));
+ index = minIndex(index, text.indexOf(".\f", fromindex));
+ index = minIndex(index, text.indexOf("@param", fromindex));
+ index = minIndex(index, text.indexOf("@return", fromindex));
+ index = minIndex(index, text.indexOf("@throw", fromindex));
+ index = minIndex(index, text.indexOf("@serial", fromindex));
+ index = minIndex(index, text.indexOf("@exception", fromindex));
+ index = minIndex(index, text.indexOf("@deprecate", fromindex));
+ index = minIndex(index, text.indexOf("@author", fromindex));
+ index = minIndex(index, text.indexOf("@since", fromindex));
+ index = minIndex(index, text.indexOf("@see", fromindex));
+ index = minIndex(index, text.indexOf("@version", fromindex));
+ if (doExclude && excludeTag != null)
+ index = minIndex(index, text.indexOf(excludeTag));
+ index = minIndex(index, text.indexOf("@vtexclude", fromindex));
+ index = minIndex(index, text.indexOf("@vtinclude", fromindex));
+ index = minIndex(index, text.indexOf("<p>", 2)); // Not at start
+ index = minIndex(index, text.indexOf("<P>", 2)); // Not at start
+ index = minIndex(index, text.indexOf("<blockquote", 2)); // Not at start
+ index = minIndex(index, text.indexOf("<pre", fromindex)); // May contain anything!
+ // Avoid the char at the start of a tag in some cases
+ if (index != -1 &&
+ (text.charAt(index) == '@' || text.charAt(index) == '<')) {
+ if (index != 0)
+ index--;
+ }
+
+/* Not used for jdiff, since tags are explicitly checked for above.
+ // Look for a sentence terminated by an HTML tag.
+ index = minIndex(index, text.indexOf(".<", fromindex));
+ if (index == -1) {
+ // If period-whitespace etc was not found, check to see if
+ // last character is a period,
+ int endIndex = text.length()-1;
+ if (text.charAt(endIndex) == '.' ||
+ text.charAt(endIndex) == '?' ||
+ text.charAt(endIndex) == '!')
+ index = endIndex;
+ }
+*/
+ return index;
+ }
+
+ /**
+ * Return the minimum of two indexes if > -1, and return -1
+ * only if both indexes = -1.
+ * @param i an int index
+ * @param j an int index
+ * @return an int equal to the minimum index > -1, or -1
+ */
+ public static int minIndex(int i, int j) {
+ if (i == -1) return j;
+ if (j == -1) return i;
+ return Math.min(i,j);
+ }
+
+ /**
+ * The name of the file where the XML representing the API will be
+ * stored.
+ */
+ public static String outputFileName = null;
+
+ /**
+ * The identifier of the API being written out in XML, e.g.
+ * &quotSuperProduct 1.3&quot;.
+ */
+ public static String apiIdentifier = null;
+
+ /**
+ * The file where the XML representing the API will be stored.
+ */
+ private static PrintWriter outputFile = null;
+
+ /**
+ * The name of the directory where the XML representing the API will be
+ * stored.
+ */
+ public static String outputDirectory = null;
+
+ /**
+ * Do not display a class with a lower level of visibility than this.
+ * Default is to display all public and protected classes.
+ */
+ public static String classVisibilityLevel = "protected";
+
+ /**
+ * Do not display a member with a lower level of visibility than this.
+ * Default is to display all public and protected members
+ * (constructors, methods, fields).
+ */
+ public static String memberVisibilityLevel = "protected";
+
+ /**
+ * If set, then save the entire contents of a doc block comment in the
+ * API file. If not set, then just save the first sentence. Default is
+ * that this is set.
+ */
+ public static boolean saveAllDocs = true;
+
+ /**
+ * If set, exclude program elements marked with whatever the exclude tag
+ * is specified as, e.g. "@exclude".
+ */
+ public static boolean doExclude = false;
+
+ /**
+ * Exclude program elements marked with this String, e.g. "@exclude".
+ */
+ public static String excludeTag = null;
+
+ /**
+ * The base URI for locating necessary DTDs and Schemas. By default, this
+ * is "http://www.w3.org". A typical value to use local copies of DTD files
+ * might be "file:///C:/jdiff/lib"
+ */
+ public static String baseURI = "http://www.w3.org";
+
+ /**
+ * If set, then strip out non-printing characters from documentation.
+ * Default is that this is set.
+ */
+ static boolean stripNonPrintables = true;
+
+ /**
+ * If set, then add the information about the source file and line number
+ * which is available in J2SE1.4. Default is that this is not set.
+ */
+ static boolean addSrcInfo = false;
+
+ /**
+ * If set, scan classes with no packages.
+ * If the source is a jar file this may duplicates classes, so
+ * disable it using the -packagesonly option. Default is that this is
+ * not set.
+ */
+ static boolean packagesOnly = false;
+
+ /** Set to enable increased logging verbosity for debugging. */
+ private static boolean trace = false;
+
+} //RootDocToXML