• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package jdiff;
2 
3 import com.sun.javadoc.*;
4 import com.sun.javadoc.Parameter;
5 import com.sun.javadoc.ParameterizedType;
6 import com.sun.javadoc.Type;
7 
8 import java.util.*;
9 import java.io.*;
10 import java.lang.reflect.*;
11 
12 /**
13  * Converts a Javadoc RootDoc object into a representation in an
14  * XML file.
15  *
16  * See the file LICENSE.txt for copyright details.
17  * @author Matthew Doar, mdoar@pobox.com
18  */
19 public class RootDocToXML {
20 
21     /** Default constructor. */
RootDocToXML()22     public RootDocToXML() {
23     }
24 
25     /**
26      * Write the XML representation of the API to a file.
27      *
28      * @param root  the RootDoc object passed by Javadoc
29      * @return true if no problems encountered
30      */
writeXML(RootDoc root)31     public static boolean writeXML(RootDoc root) {
32     	String tempFileName = outputFileName;
33     	if (outputDirectory != null) {
34 	    tempFileName = outputDirectory;
35 	    if (!tempFileName.endsWith(JDiff.DIR_SEP))
36 		tempFileName += JDiff.DIR_SEP;
37 	    tempFileName += outputFileName;
38     	}
39 
40         try {
41             FileOutputStream fos = new FileOutputStream(tempFileName);
42             outputFile = new PrintWriter(fos);
43             System.out.println("JDiff: writing the API to file '" + tempFileName + "'...");
44             if (root.specifiedPackages().length != 0 || root.specifiedClasses().length != 0) {
45                 RootDocToXML apiWriter = new RootDocToXML();
46                 apiWriter.emitXMLHeader();
47                 apiWriter.logOptions();
48                 apiWriter.processPackages(root);
49                 apiWriter.emitXMLFooter();
50             }
51             outputFile.close();
52         } catch(IOException e) {
53             System.out.println("IO Error while attempting to create " + tempFileName);
54             System.out.println("Error: " +  e.getMessage());
55             System.exit(1);
56         }
57         // If validation is desired, write out the appropriate api.xsd file
58         // in the same directory as the XML file.
59         if (XMLToAPI.validateXML) {
60             writeXSD();
61         }
62         return true;
63     }
64 
65     /**
66      * Write the XML Schema file used for validation.
67      */
writeXSD()68     public static void writeXSD() {
69         String xsdFileName = outputFileName;
70         if (outputDirectory == null) {
71 	    int idx = xsdFileName.lastIndexOf('\\');
72 	    int idx2 = xsdFileName.lastIndexOf('/');
73 	    if (idx == -1 && idx2 == -1) {
74 		xsdFileName = "";
75 	    } else if (idx == -1 && idx2 != -1) {
76 		xsdFileName = xsdFileName.substring(0, idx2);
77 	    } else if (idx != -1  && idx2 == -1) {
78 		xsdFileName = xsdFileName.substring(0, idx);
79 	    } else if (idx != -1  && idx2 != -1) {
80 		int max = idx2 > idx ? idx2 : idx;
81 		xsdFileName = xsdFileName.substring(0, max);
82 	    }
83 	} else {
84 	    xsdFileName = outputDirectory;
85 	    if (!xsdFileName.endsWith(JDiff.DIR_SEP))
86 		 xsdFileName += JDiff.DIR_SEP;
87 	}
88         xsdFileName += "api.xsd";
89         try {
90             FileOutputStream fos = new FileOutputStream(xsdFileName);
91             PrintWriter xsdFile = new PrintWriter(fos);
92             // The contents of the api.xsd file
93             xsdFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
94             xsdFile.println("<xsd:schema xmlns:xsd=\"https://www.w3.org/2001/XMLSchema\">");
95             xsdFile.println("");
96             xsdFile.println("<xsd:annotation>");
97             xsdFile.println("  <xsd:documentation>");
98             xsdFile.println("  Schema for JDiff API representation.");
99             xsdFile.println("  </xsd:documentation>");
100             xsdFile.println("</xsd:annotation>");
101             xsdFile.println();
102             xsdFile.println("<xsd:element name=\"api\" type=\"apiType\"/>");
103             xsdFile.println("");
104             xsdFile.println("<xsd:complexType name=\"apiType\">");
105             xsdFile.println("  <xsd:sequence>");
106             xsdFile.println("    <xsd:element name=\"package\" type=\"packageType\" minOccurs='1' maxOccurs='unbounded'/>");
107             xsdFile.println("  </xsd:sequence>");
108             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
109             xsdFile.println("  <xsd:attribute name=\"jdversion\" type=\"xsd:string\"/>");
110             xsdFile.println("</xsd:complexType>");
111             xsdFile.println();
112             xsdFile.println("<xsd:complexType name=\"packageType\">");
113             xsdFile.println("  <xsd:sequence>");
114             xsdFile.println("    <xsd:choice maxOccurs='unbounded'>");
115             xsdFile.println("      <xsd:element name=\"class\" type=\"classType\"/>");
116             xsdFile.println("      <xsd:element name=\"interface\" type=\"classType\"/>");
117             xsdFile.println("    </xsd:choice>");
118             xsdFile.println("    <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
119             xsdFile.println("  </xsd:sequence>");
120             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
121             xsdFile.println("</xsd:complexType>");
122             xsdFile.println();
123             xsdFile.println("<xsd:complexType name=\"classType\">");
124             xsdFile.println("  <xsd:sequence>");
125             xsdFile.println("    <xsd:element name=\"implements\" type=\"interfaceTypeName\" minOccurs='0' maxOccurs='unbounded'/>");
126             xsdFile.println("    <xsd:element name=\"constructor\" type=\"constructorType\" minOccurs='0' maxOccurs='unbounded'/>");
127             xsdFile.println("    <xsd:element name=\"method\" type=\"methodType\" minOccurs='0' maxOccurs='unbounded'/>");
128             xsdFile.println("    <xsd:element name=\"field\" type=\"fieldType\" minOccurs='0' maxOccurs='unbounded'/>");
129             xsdFile.println("    <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
130             xsdFile.println("  </xsd:sequence>");
131             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
132             xsdFile.println("  <xsd:attribute name=\"extends\" type=\"xsd:string\" use='optional'/>");
133             xsdFile.println("  <xsd:attribute name=\"abstract\" type=\"xsd:boolean\"/>");
134             xsdFile.println("  <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
135             xsdFile.println("  <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
136             xsdFile.println("  <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
137             xsdFile.println("  <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
138             xsdFile.println("  <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
139             xsdFile.println("</xsd:complexType>");
140             xsdFile.println();
141             xsdFile.println("<xsd:complexType name=\"interfaceTypeName\">");
142             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
143             xsdFile.println("</xsd:complexType>");
144             xsdFile.println();
145             xsdFile.println("<xsd:complexType name=\"constructorType\">");
146             xsdFile.println("  <xsd:sequence>");
147             xsdFile.println("    <xsd:element name=\"exception\" type=\"exceptionType\" minOccurs='0' maxOccurs='unbounded'/>");
148             xsdFile.println("    <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
149             xsdFile.println("  </xsd:sequence>");
150             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
151             xsdFile.println("  <xsd:attribute name=\"type\" type=\"xsd:string\" use='optional'/>");
152             xsdFile.println("  <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
153             xsdFile.println("  <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
154             xsdFile.println("  <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
155             xsdFile.println("  <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
156             xsdFile.println("  <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
157             xsdFile.println("</xsd:complexType>");
158             xsdFile.println();
159             xsdFile.println("<xsd:complexType name=\"paramsType\">");
160             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
161             xsdFile.println("  <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
162             xsdFile.println("</xsd:complexType>");
163             xsdFile.println();
164             xsdFile.println("<xsd:complexType name=\"exceptionType\">");
165             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
166             xsdFile.println("  <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
167             xsdFile.println("</xsd:complexType>");
168             xsdFile.println();
169             xsdFile.println("<xsd:complexType name=\"methodType\">");
170             xsdFile.println("  <xsd:sequence>");
171             xsdFile.println("    <xsd:element name=\"param\" type=\"paramsType\" minOccurs='0' maxOccurs='unbounded'/>");
172             xsdFile.println("    <xsd:element name=\"exception\" type=\"exceptionType\" minOccurs='0' maxOccurs='unbounded'/>");
173             xsdFile.println("    <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
174             xsdFile.println("  </xsd:sequence>");
175             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
176             xsdFile.println("  <xsd:attribute name=\"return\" type=\"xsd:string\" use='optional'/>");
177             xsdFile.println("  <xsd:attribute name=\"abstract\" type=\"xsd:boolean\"/>");
178             xsdFile.println("  <xsd:attribute name=\"native\" type=\"xsd:boolean\"/>");
179             xsdFile.println("  <xsd:attribute name=\"synchronized\" type=\"xsd:boolean\"/>");
180             xsdFile.println("  <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
181             xsdFile.println("  <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
182             xsdFile.println("  <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
183             xsdFile.println("  <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
184             xsdFile.println("  <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
185             xsdFile.println("</xsd:complexType>");
186             xsdFile.println();
187             xsdFile.println("<xsd:complexType name=\"fieldType\">");
188             xsdFile.println("  <xsd:sequence>");
189             xsdFile.println("    <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
190             xsdFile.println("  </xsd:sequence>");
191             xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
192             xsdFile.println("  <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
193             xsdFile.println("  <xsd:attribute name=\"transient\" type=\"xsd:boolean\"/>");
194             xsdFile.println("  <xsd:attribute name=\"volatile\" type=\"xsd:boolean\"/>");
195             xsdFile.println("  <xsd:attribute name=\"value\" type=\"xsd:string\" use='optional'/>");
196             xsdFile.println("  <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
197             xsdFile.println("  <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
198             xsdFile.println("  <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
199             xsdFile.println("  <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
200             xsdFile.println("  <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
201             xsdFile.println("</xsd:complexType>");
202             xsdFile.println();
203             xsdFile.println("</xsd:schema>");
204             xsdFile.close();
205         } catch(IOException e) {
206             System.out.println("IO Error while attempting to create " + xsdFileName);
207             System.out.println("Error: " +  e.getMessage());
208             System.exit(1);
209         }
210     }
211 
212     /**
213      * Write the options which were used to generate this XML file
214      * out as XML comments.
215      */
logOptions()216     public void logOptions() {
217         // TODO: Get options from javadoc if possible.
218         // outputFile.print("<!-- ");
219         // outputFile.print(" Command line arguments = " + Options.cmdOptions);
220         // outputFile.println(" -->");
221     }
222 
223     /**
224      * Process each package and the classes/interfaces within it.
225      *
226      * @param pd  an array of PackageDoc objects
227      */
processPackages(RootDoc root)228     public void processPackages(RootDoc root) {
229         PackageDoc[] specified_pd = root.specifiedPackages();
230 	Map pdl = new TreeMap();
231         for (int i = 0; specified_pd != null && i < specified_pd.length; i++) {
232 	    pdl.put(specified_pd[i].name(), specified_pd[i]);
233 	}
234 
235 	// Classes may be specified separately, so merge their packages into the
236 	// list of specified packages.
237         ClassDoc[] cd = root.specifiedClasses();
238 	// This is lists of the specific classes to document
239 	Map classesToUse = new HashMap();
240         for (int i = 0; cd != null && i < cd.length; i++) {
241 	    PackageDoc cpd = cd[i].containingPackage();
242 	    if (cpd == null && !packagesOnly) {
243 		// If the RootDoc object has been created from a jar file
244 		// this duplicates classes, so we have to be able to disable it.
245 		// TODO this is still null?
246 		cpd = root.packageNamed("anonymous");
247 	    }
248             String pkgName = cpd.name();
249             String className = cd[i].name();
250 	    if (trace) System.out.println("Found package " + pkgName + " for class " + className);
251 	    if (!pdl.containsKey(pkgName)) {
252 		if (trace) System.out.println("Adding new package " + pkgName);
253 		pdl.put(pkgName, cpd);
254 	    }
255 
256 	    // Keep track of the specific classes to be used for this package
257 	    List classes;
258 	    if (classesToUse.containsKey(pkgName)) {
259 		classes = (ArrayList) classesToUse.get(pkgName);
260 	    } else {
261 		classes = new ArrayList();
262 	    }
263 	    classes.add(cd[i]);
264 	    classesToUse.put(pkgName, classes);
265 	}
266 
267 	PackageDoc[] pd = (PackageDoc[]) pdl.values().toArray(new PackageDoc[0]);
268         for (int i = 0; pd != null && i < pd.length; i++) {
269             String pkgName = pd[i].name();
270 
271             // Check for an exclude tag in the package doc block, but not
272 	    // in the package.htm[l] file.
273             if (!shownElement(pd[i], null))
274                 continue;
275 
276             if (trace) System.out.println("PROCESSING PACKAGE: " + pkgName);
277             outputFile.println("<package name=\"" + pkgName + "\">");
278 
279             int tagCount = pd[i].tags().length;
280             if (trace) System.out.println("#tags: " + tagCount);
281 
282             List classList;
283 	    if (classesToUse.containsKey(pkgName)) {
284 		// Use only the specified classes in the package
285 		System.out.println("Using the specified classes");
286 		classList = (ArrayList) classesToUse.get(pkgName);
287 	    } else {
288 		// Use all classes in the package
289 		classList = new LinkedList(Arrays.asList(pd[i].allClasses()));
290 	    }
291             Collections.sort(classList);
292             ClassDoc[] classes = new ClassDoc[classList.size()];
293             classes = (ClassDoc[])classList.toArray(classes);
294             processClasses(classes, pkgName);
295 
296             addPkgDocumentation(root, pd[i], 2);
297 
298             outputFile.println("</package>");
299         }
300     } // processPackages
301 
302     /**
303      * Process classes and interfaces.
304      *
305      * @param cd An array of ClassDoc objects.
306      */
processClasses(ClassDoc[] cd, String pkgName)307     public void processClasses(ClassDoc[] cd, String pkgName) {
308         if (cd.length == 0)
309             return;
310         if (trace) System.out.println("PROCESSING CLASSES, number=" + cd.length);
311         for (int i = 0; i < cd.length; i++) {
312             String className = cd[i].name();
313             if (trace) System.out.println("PROCESSING CLASS/IFC: " + className);
314             // Only save the shown elements
315             if (!shownElement(cd[i], classVisibilityLevel))
316                 continue;
317             boolean isInterface = false;
318             if (cd[i].isInterface())
319                 isInterface = true;
320             if (isInterface) {
321                 outputFile.println("  <!-- start interface " + pkgName + "." + className + " -->");
322                 outputFile.print("  <interface name=\"" + className + "\"");
323             } else {
324                 outputFile.println("  <!-- start class " + pkgName + "." + className + " -->");
325                 outputFile.print("  <class name=\"" + className + "\"");
326             }
327             // Add attributes to the class element
328             Type parent = cd[i].superclassType();
329             if (parent != null)
330                 outputFile.println(" extends=\"" + buildEmittableTypeString(parent) + "\"");
331             outputFile.println("    abstract=\"" + cd[i].isAbstract() + "\"");
332             addCommonModifiers(cd[i], 4);
333             outputFile.println(">");
334             // Process class members. (Treat inner classes as members.)
335             processInterfaces(cd[i].interfaceTypes());
336             processConstructors(cd[i].constructors());
337             processMethods(cd[i], cd[i].methods());
338             processFields(cd[i].fields());
339 
340             addDocumentation(cd[i], 4);
341 
342             if (isInterface) {
343                 outputFile.println("  </interface>");
344                 outputFile.println("  <!-- end interface " + pkgName + "." + className + " -->");
345             } else {
346                 outputFile.println("  </class>");
347                 outputFile.println("  <!-- end class " + pkgName + "." + className + " -->");
348             }
349             // Inner classes have already been added.
350             /*
351               ClassDoc[] ic = cd[i].innerClasses();
352               for (int k = 0; k < ic.length; k++) {
353               System.out.println("Inner class " + k + ", name = " + ic[k].name());
354               }
355             */
356         }//for
357     }//processClasses()
358 
359     /**
360      * Add qualifiers for the program element as attributes.
361      *
362      * @param ped The given program element.
363      */
addCommonModifiers(ProgramElementDoc ped, int indent)364     public void addCommonModifiers(ProgramElementDoc ped, int indent) {
365         addSourcePosition(ped, indent);
366         // Static and final and visibility on one line
367         for (int i = 0; i < indent; i++) outputFile.print(" ");
368         outputFile.print("static=\"" + ped.isStatic() + "\"");
369         outputFile.print(" final=\"" + ped.isFinal() + "\"");
370         // Visibility
371         String visibility = null;
372         if (ped.isPublic())
373             visibility = "public";
374         else if (ped.isProtected())
375             visibility = "protected";
376         else if (ped.isPackagePrivate())
377             visibility = "package";
378         else if (ped.isPrivate())
379             visibility = "private";
380         outputFile.println(" visibility=\"" + visibility + "\"");
381 
382         // Deprecation on its own line
383         for (int i = 0; i < indent; i++) outputFile.print(" ");
384         boolean isDeprecated = false;
385         Tag[] ta = ((Doc)ped).tags("deprecated");
386         if (ta.length != 0) {
387             isDeprecated = true;
388         }
389         if (ta.length > 1) {
390             System.out.println("JDiff: warning: multiple @deprecated tags found in comments for " + ped.name() + ". Using the first one only.");
391             System.out.println("Text is: " + ((Doc)ped).getRawCommentText());
392         }
393         if (isDeprecated) {
394             String text = ta[0].text(); // Use only one @deprecated tag
395             if (text != null && text.compareTo("") != 0) {
396                 int idx = endOfFirstSentence(text);
397                 if (idx == 0) {
398                     // No useful comment
399                     outputFile.print("deprecated=\"deprecated, no comment\"");
400                 } else {
401                     String fs = null;
402                     if (idx == -1)
403                         fs = text;
404                     else
405                         fs = text.substring(0, idx+1);
406                     String st = API.hideHTMLTags(fs);
407                     outputFile.print("deprecated=\"" + st + "\"");
408                 }
409             } else {
410                 outputFile.print("deprecated=\"deprecated, no comment\"");
411             }
412         } else {
413             outputFile.print("deprecated=\"not deprecated\"");
414         }
415 
416     } //addQualifiers()
417 
418     /**
419      * Insert the source code details, if available.
420      *
421      * @param ped The given program element.
422      */
addSourcePosition(ProgramElementDoc ped, int indent)423     public void addSourcePosition(ProgramElementDoc ped, int indent) {
424         if (!addSrcInfo)
425             return;
426         if (JDiff.javaVersion.startsWith("1.1") ||
427             JDiff.javaVersion.startsWith("1.2") ||
428             JDiff.javaVersion.startsWith("1.3")) {
429             return; // position() only appeared in J2SE1.4
430         }
431         try {
432             // Could cache the method for improved performance
433             Class c = ProgramElementDoc.class;
434             Method m = c.getMethod("position", (Class[]) null);
435             Object sp = m.invoke(ped, (Object[]) null);
436             if (sp != null) {
437                 for (int i = 0; i < indent; i++) outputFile.print(" ");
438                 outputFile.println("src=\"" + sp + "\"");
439             }
440         } catch (NoSuchMethodException e2) {
441             System.err.println("Error: method \"position\" not found");
442             e2.printStackTrace();
443         } catch (IllegalAccessException e4) {
444             System.err.println("Error: class not permitted to be instantiated");
445             e4.printStackTrace();
446         } catch (InvocationTargetException e5) {
447             System.err.println("Error: method \"position\" could not be invoked");
448             e5.printStackTrace();
449         } catch (Exception e6) {
450             System.err.println("Error: ");
451             e6.printStackTrace();
452         }
453     }
454 
455     /**
456      * Process the interfaces implemented by the class.
457      *
458      * @param ifaces An array of ClassDoc objects
459      */
processInterfaces(Type[] ifaces)460     public void processInterfaces(Type[] ifaces) {
461         if (trace) System.out.println("PROCESSING INTERFACES, number=" + ifaces.length);
462         for (int i = 0; i < ifaces.length; i++) {
463             String ifaceName = buildEmittableTypeString(ifaces[i]);
464             if (trace) System.out.println("PROCESSING INTERFACE: " + ifaceName);
465             outputFile.println("    <implements name=\"" + ifaceName + "\"/>");
466         }//for
467     }//processInterfaces()
468 
469     /**
470      * Process the constructors in the class.
471      *
472      * @param ct An array of ConstructorDoc objects
473      */
processConstructors(ConstructorDoc[] ct)474     public void processConstructors(ConstructorDoc[] ct) {
475         if (trace) System.out.println("PROCESSING CONSTRUCTORS, number=" + ct.length);
476         for (int i = 0; i < ct.length; i++) {
477             String ctorName = ct[i].name();
478             if (trace) System.out.println("PROCESSING CONSTRUCTOR: " + ctorName);
479             // Only save the shown elements
480             if (!shownElement(ct[i], memberVisibilityLevel))
481                 continue;
482             outputFile.print("    <constructor name=\"" + ctorName + "\"");
483 
484             Parameter[] params = ct[i].parameters();
485             boolean first = true;
486             if (params.length != 0) {
487                 outputFile.print(" type=\"");
488                 for (int j = 0; j < params.length; j++) {
489                     if (!first)
490                         outputFile.print(", ");
491                     emitType(params[j].type());
492                     first = false;
493                 }
494                 outputFile.println("\"");
495             } else
496                 outputFile.println();
497             addCommonModifiers(ct[i], 6);
498             outputFile.println(">");
499 
500             // Generate the exception elements if any exceptions are thrown
501             processExceptions(ct[i].thrownExceptions());
502 
503             addDocumentation(ct[i], 6);
504 
505             outputFile.println("    </constructor>");
506         }//for
507     }//processConstructors()
508 
509     /**
510      * Process all exceptions thrown by a constructor or method.
511      *
512      * @param cd An array of ClassDoc objects
513      */
processExceptions(ClassDoc[] cd)514     public void processExceptions(ClassDoc[] cd) {
515         if (trace) System.out.println("PROCESSING EXCEPTIONS, number=" + cd.length);
516         for (int i = 0; i < cd.length; i++) {
517             String exceptionName = cd[i].name();
518             if (trace) System.out.println("PROCESSING EXCEPTION: " + exceptionName);
519             outputFile.print("      <exception name=\"" + exceptionName + "\" type=\"");
520             emitType(cd[i]);
521             outputFile.println("\"/>");
522         }//for
523     }//processExceptions()
524 
525     /**
526      * Process the methods in the class.
527      *
528      * @param md An array of MethodDoc objects
529      */
processMethods(ClassDoc cd, MethodDoc[] md)530     public void processMethods(ClassDoc cd, MethodDoc[] md) {
531         if (trace) System.out.println("PROCESSING " +cd.name()+" METHODS, number = " + md.length);
532         for (int i = 0; i < md.length; i++) {
533             String methodName = md[i].name();
534             if (trace) System.out.println("PROCESSING METHOD: " + methodName);
535             // Skip <init> and <clinit>
536             if (methodName.startsWith("<"))
537                 continue;
538             // Only save the shown elements
539             if (!shownElement(md[i], memberVisibilityLevel))
540                 continue;
541             outputFile.print("    <method name=\"" + methodName + "\"");
542             com.sun.javadoc.Type retType = md[i].returnType();
543             if (retType.qualifiedTypeName().compareTo("void") == 0) {
544                 // Don't add a return attribute if the return type is void
545                 outputFile.println();
546             } else {
547                 outputFile.print(" return=\"");
548                 emitType(retType);
549                 outputFile.println("\"");
550             }
551             outputFile.print("      abstract=\"" + md[i].isAbstract() + "\"");
552             outputFile.print(" native=\"" + md[i].isNative() + "\"");
553             outputFile.println(" synchronized=\"" + md[i].isSynchronized() + "\"");
554             addCommonModifiers(md[i], 6);
555             outputFile.println(">");
556             // Generate the parameter elements, if any
557             Parameter[] params = md[i].parameters();
558             for (int j = 0; j < params.length; j++) {
559                 outputFile.print("      <param name=\"" + params[j].name() + "\"");
560                 outputFile.print(" type=\"");
561                 emitType(params[j].type());
562                 outputFile.println("\"/>");
563             }
564 
565             // Generate the exception elements if any exceptions are thrown
566             processExceptions(md[i].thrownExceptions());
567 
568             addDocumentation(md[i], 6);
569 
570             outputFile.println("    </method>");
571         }//for
572     }//processMethods()
573 
574     /**
575      * Process the fields in the class.
576      *
577      * @param fd An array of FieldDoc objects
578      */
processFields(FieldDoc[] fd)579     public void processFields(FieldDoc[] fd) {
580         if (trace) System.out.println("PROCESSING FIELDS, number=" + fd.length);
581         for (int i = 0; i < fd.length; i++) {
582             String fieldName = fd[i].name();
583             if (trace) System.out.println("PROCESSING FIELD: " + fieldName);
584             // Only save the shown elements
585             if (!shownElement(fd[i], memberVisibilityLevel))
586                 continue;
587             outputFile.print("    <field name=\"" + fieldName + "\"");
588             outputFile.print(" type=\"");
589             emitType(fd[i].type());
590             outputFile.println("\"");
591             outputFile.print("      transient=\"" + fd[i].isTransient() + "\"");
592             outputFile.println(" volatile=\"" + fd[i].isVolatile() + "\"");
593 /* JDK 1.4 and later */
594 /*
595             String value = fd[i].constantValueExpression();
596             if (value != null)
597                 outputFile.println(" value=\"" + value + "\"");
598 */
599             addCommonModifiers(fd[i], 6);
600             outputFile.println(">");
601 
602             addDocumentation(fd[i], 6);
603 
604             outputFile.println("    </field>");
605 
606         }//for
607     }//processFields()
608 
609     /**
610      * Emit the type name. Removed any prefixed warnings about ambiguity.
611      * The type maybe an array.
612      *
613      * @param type A Type object.
614      */
emitType(com.sun.javadoc.Type type)615     public void emitType(com.sun.javadoc.Type type) {
616         String name = buildEmittableTypeString(type);
617         if (name == null)
618             return;
619         outputFile.print(name);
620     }
621 
622     /**
623      * Build the emittable type name. The type may be an array and/or
624      * a generic type.
625      *
626      * @param type A Type object
627      * @return The emittable type name
628      */
buildEmittableTypeString(com.sun.javadoc.Type type)629     private String buildEmittableTypeString(com.sun.javadoc.Type type) {
630         if (type == null) {
631     	    return null;
632         }
633       // type.toString() returns the fully qualified name of the type
634       // including the dimension and the parameters we just need to
635       // escape the generic parameters brackets so that the XML
636       // generated is correct
637       String name = type.toString().replaceAll("<", "&lt;").replaceAll(">", "&gt;");
638       if (name.startsWith("<<ambiguous>>")) {
639           name = name.substring(13);
640       }
641       return name;
642     }
643 
644     /**
645      * Emit the XML header.
646      */
emitXMLHeader()647     public void emitXMLHeader() {
648         outputFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
649         outputFile.println("<!-- Generated by the JDiff Javadoc doclet -->");
650         outputFile.println("<!-- (" + JDiff.jDiffLocation + ") -->");
651         outputFile.println("<!-- on " + new Date() + " -->");
652         outputFile.println();
653 /* No need for this any longer, since doc block text is in an CDATA element
654         outputFile.println("<!-- XML Schema is used, but XHTML transitional DTD is needed for nbsp -->");
655         outputFile.println("<!-- entity definitions etc.-->");
656         outputFile.println("<!DOCTYPE api");
657         outputFile.println("     PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
658         outputFile.println("     \"" + baseURI + "/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
659 */
660         outputFile.println("<api");
661         outputFile.println("  xmlns:xsi='" + baseURI + "/2001/XMLSchema-instance'");
662         outputFile.println("  xsi:noNamespaceSchemaLocation='api.xsd'");
663         outputFile.println("  name=\"" + apiIdentifier + "\"");
664         outputFile.println("  jdversion=\"" + JDiff.version + "\">");
665         outputFile.println();
666     }
667 
668     /**
669      * Emit the XML footer.
670      */
emitXMLFooter()671     public void emitXMLFooter() {
672         outputFile.println();
673         outputFile.println("</api>");
674     }
675 
676     /**
677      * Determine if the program element is shown, according to the given
678      * level of visibility.
679      *
680      * @param ped The given program element.
681      * @param visLevel The desired visibility level; "public", "protected",
682      *   "package" or "private". If null, only check for an exclude tag.
683      * @return boolean Set if this element is shown.
684      */
shownElement(Doc doc, String visLevel)685     public boolean shownElement(Doc doc, String visLevel) {
686         // If a doc block contains @exclude or a similar such tag,
687         // then don't display it.
688 	if (doExclude && excludeTag != null && doc != null) {
689             String rct = doc.getRawCommentText();
690             if (rct != null && rct.indexOf(excludeTag) != -1) {
691                 return false;
692 	    }
693 	}
694 	if (visLevel == null) {
695 	    return true;
696 	}
697 	ProgramElementDoc ped = null;
698 	if (doc instanceof ProgramElementDoc) {
699 	    ped = (ProgramElementDoc)doc;
700 	}
701         if (visLevel.compareTo("private") == 0)
702             return true;
703         // Show all that is not private
704         if (visLevel.compareTo("package") == 0)
705             return !ped.isPrivate();
706         // Show all that is not private or package
707         if (visLevel.compareTo("protected") == 0)
708             return !(ped.isPrivate() || ped.isPackagePrivate());
709         // Show all that is not private or package or protected,
710         // i.e. all that is public
711         if (visLevel.compareTo("public") == 0)
712             return ped.isPublic();
713         return false;
714     } //shownElement()
715 
716     /**
717      * Strip out non-printing characters, replacing them with a character
718      * which will not change where the end of the first sentence is found.
719      * This character is the hash mark, '&#035;'.
720      */
stripNonPrintingChars(String s, Doc doc)721     public String stripNonPrintingChars(String s, Doc doc) {
722         if (!stripNonPrintables)
723             return s;
724         char[] sa = s.toCharArray();
725         for (int i = 0; i < sa.length; i++) {
726             char c = sa[i];
727             // TODO still have an issue with Unicode: 0xfc in java.lang.String.toUpperCase comments
728 //            if (Character.isDefined(c))
729             if (Character.isLetterOrDigit(c))
730                 continue;
731             // There must be a better way that is still platform independent!
732             if (c == ' ' ||
733                 c == '.' ||
734                 c == ',' ||
735                 c == '\r' ||
736                 c == '\t' ||
737                 c == '\n' ||
738                 c == '!' ||
739                 c == '?' ||
740                 c == ';' ||
741                 c == ':' ||
742                 c == '[' ||
743                 c == ']' ||
744                 c == '(' ||
745                 c == ')' ||
746                 c == '~' ||
747                 c == '@' ||
748                 c == '#' ||
749                 c == '$' ||
750                 c == '%' ||
751                 c == '^' ||
752                 c == '&' ||
753                 c == '*' ||
754                 c == '-' ||
755                 c == '=' ||
756                 c == '+' ||
757                 c == '_' ||
758                 c == '|' ||
759                 c == '\\' ||
760                 c == '/' ||
761                 c == '\'' ||
762                 c == '}' ||
763                 c == '{' ||
764                 c == '"' ||
765                 c == '<' ||
766                 c == '>' ||
767                 c == '`'
768                 )
769                 continue;
770 /* Doesn't seem to return the expected values?
771             int val = Character.getNumericValue(c);
772 //            if (s.indexOf("which is also a test for non-printable") != -1)
773 //                System.out.println("** Char " + i + "[" + c + "], val =" + val); //DEBUG
774             // Ranges from https://www.unicode.org/unicode/reports/tr20/
775             // Should really replace 0x2028 and  0x2029 with <br/>
776             if (val == 0x0 ||
777                 inRange(val, 0x2028, 0x2029) ||
778                 inRange(val, 0x202A, 0x202E) ||
779                 inRange(val, 0x206A, 0x206F) ||
780                 inRange(val, 0xFFF9, 0xFFFC) ||
781                 inRange(val, 0xE0000, 0xE007F)) {
782                 if (trace) {
783                     System.out.println("Warning: changed non-printing character  " + sa[i] + " in " + doc.name());
784                 }
785                 sa[i] = '#';
786             }
787 */
788             // Replace the non-printable character with a printable character
789             // which does not change the end of the first sentence
790             sa[i] = '#';
791         }
792         return new String(sa);
793     }
794 
795     /** Return true if val is in the range [min|max], inclusive. */
inRange(int val, int min, int max)796     public boolean inRange(int val, int min, int max) {
797         if (val < min)
798             return false;
799         if (val > max)
800             return false;
801         return true;
802     }
803 
804     /**
805      * Add at least the first sentence from a doc block to the API. This is
806      * used by the report generator if no comment is provided.
807      * Need to make sure that HTML tags are not confused with XML tags.
808      * This could be done by stuffing the &lt; character to another string
809      * or by handling HTML in the parser. This second option seems neater. Note that
810      * XML expects all element tags to have either a closing "/>" or a matching
811      * end element tag. Due to the difficulties of converting incorrect HTML
812      * to XHTML, the first option is used.
813      */
addDocumentation(ProgramElementDoc ped, int indent)814     public void addDocumentation(ProgramElementDoc ped, int indent) {
815         String rct = ((Doc)ped).getRawCommentText();
816         if (rct != null) {
817             rct = stripNonPrintingChars(rct, (Doc)ped);
818             rct = rct.trim();
819             if (rct.compareTo("") != 0 &&
820                 rct.indexOf(Comments.placeHolderText) == -1 &&
821                 rct.indexOf("InsertOtherCommentsHere") == -1) {
822                 int idx = endOfFirstSentence(rct);
823                 if (idx == 0)
824                     return;
825                 for (int i = 0; i < indent; i++) outputFile.print(" ");
826                 outputFile.println("<doc>");
827                 for (int i = 0; i < indent; i++) outputFile.print(" ");
828                 String firstSentence = null;
829                 if (idx == -1)
830                     firstSentence = rct;
831                 else
832                     firstSentence = rct.substring(0, idx+1);
833                 boolean checkForAts = false;
834                 if (checkForAts && firstSentence.indexOf("@") != -1 &&
835                     firstSentence.indexOf("@link") == -1) {
836                     System.out.println("Warning: @ tag seen in comment: " +
837                                        firstSentence);
838                 }
839                 String firstSentenceNoTags = API.stuffHTMLTags(firstSentence);
840                 outputFile.println(firstSentenceNoTags);
841                 for (int i = 0; i < indent; i++) outputFile.print(" ");
842                 outputFile.println("</doc>");
843             }
844         }
845     }
846 
847     /**
848      * Add at least the first sentence from a doc block for a package to the API. This is
849      * used by the report generator if no comment is provided.
850      * The default source tree may not include the package.html files, so
851      * this may be unavailable in many cases.
852      * Need to make sure that HTML tags are not confused with XML tags.
853      * This could be done by stuffing the &lt; character to another string
854      * or by handling HTML in the parser. This second option is neater. Note that
855      * XML expects all element tags to have either a closing "/>" or a matching
856      * end element tag.  Due to the difficulties of converting incorrect HTML
857      * to XHTML, the first option is used.
858      */
addPkgDocumentation(RootDoc root, PackageDoc pd, int indent)859     public void addPkgDocumentation(RootDoc root, PackageDoc pd, int indent) {
860         String rct = null;
861         String filename = pd.name();
862         try {
863             // See if the source path was specified as part of the
864             // options and prepend it if it was.
865             String srcLocation = null;
866             String[][] options = root.options();
867             for (int opt = 0; opt < options.length; opt++) {
868                 if ((options[opt][0]).compareTo("-sourcepath") == 0) {
869                     srcLocation = options[opt][1];
870                     break;
871                 }
872             }
873             filename = filename.replace('.', JDiff.DIR_SEP.charAt(0));
874             if (srcLocation != null) {
875                 // Make a relative location absolute
876                 if (srcLocation.startsWith("..")) {
877                     String curDir = System.getProperty("user.dir");
878                     while (srcLocation.startsWith("..")) {
879                         srcLocation = srcLocation.substring(3);
880                         int idx = curDir.lastIndexOf(JDiff.DIR_SEP);
881                         curDir = curDir.substring(0, idx+1);
882                     }
883                     srcLocation = curDir + srcLocation;
884                 }
885                 filename = srcLocation + JDiff.DIR_SEP + filename;
886             }
887             // Try both ".htm" and ".html"
888             filename += JDiff.DIR_SEP + "package.htm";
889             File f2 = new File(filename);
890             if (!f2.exists()) {
891                 filename += "l";
892             }
893             FileInputStream f = new FileInputStream(filename);
894             BufferedReader d = new BufferedReader(new InputStreamReader(f));
895             String str = d.readLine();
896  	    // Ignore everything except the lines between <body> elements
897 	    boolean inBody = false;
898 	    while(str != null) {
899                 if (!inBody) {
900 		    if (str.toLowerCase().trim().startsWith("<body")) {
901 			inBody = true;
902 		    }
903 		    str = d.readLine(); // Get the next line
904 		    continue; // Ignore the line
905 		} else {
906 		    if (str.toLowerCase().trim().startsWith("</body")) {
907 			inBody = false;
908 			continue; // Ignore the line
909 		    }
910 		}
911                 if (rct == null)
912                     rct = str + "\n";
913                 else
914                     rct += str + "\n";
915                 str = d.readLine();
916             }
917         }  catch(java.io.FileNotFoundException e) {
918             // If it doesn't exist, that's fine
919             if (trace)
920                 System.out.println("No package level documentation file at '" + filename + "'");
921         } catch(java.io.IOException e) {
922             System.out.println("Error reading file \"" + filename + "\": " + e.getMessage());
923             System.exit(5);
924         }
925         if (rct != null) {
926             rct = stripNonPrintingChars(rct, (Doc)pd);
927             rct = rct.trim();
928             if (rct.compareTo("") != 0 &&
929                 rct.indexOf(Comments.placeHolderText) == -1 &&
930                 rct.indexOf("InsertOtherCommentsHere") == -1) {
931                 int idx = endOfFirstSentence(rct);
932                 if (idx == 0)
933                     return;
934                 for (int i = 0; i < indent; i++) outputFile.print(" ");
935                 outputFile.println("<doc>");
936                 for (int i = 0; i < indent; i++) outputFile.print(" ");
937                 String firstSentence = null;
938                 if (idx == -1)
939                     firstSentence = rct;
940                 else
941                     firstSentence = rct.substring(0, idx+1);
942                 String firstSentenceNoTags = API.stuffHTMLTags(firstSentence);
943                 outputFile.println(firstSentenceNoTags);
944                 for (int i = 0; i < indent; i++) outputFile.print(" ");
945                 outputFile.println("</doc>");
946             }
947         }
948     }
949 
950     /**
951      * Find the index of the end of the first sentence in the given text,
952      * when writing out to an XML file.
953      * This is an extended version of the algorithm used by the DocCheck
954      * Javadoc doclet. It checks for @tags too.
955      *
956      * @param text The text to be searched.
957      * @return The index of the end of the first sentence. If there is no
958      *         end, return -1. If there is no useful text, return 0.
959      *         If the whole doc block comment is wanted (default), return -1.
960      */
endOfFirstSentence(String text)961     public static int endOfFirstSentence(String text) {
962         return endOfFirstSentence(text, true);
963     }
964 
965     /**
966      * Find the index of the end of the first sentence in the given text.
967      * This is an extended version of the algorithm used by the DocCheck
968      * Javadoc doclet. It checks for &#064;tags too.
969      *
970      * @param text The text to be searched.
971      * @param writingToXML Set to true when writing out XML.
972      * @return The index of the end of the first sentence. If there is no
973      *         end, return -1. If there is no useful text, return 0.
974      *         If the whole doc block comment is wanted (default), return -1.
975      */
endOfFirstSentence(String text, boolean writingToXML)976     public static int endOfFirstSentence(String text, boolean writingToXML) {
977         if (saveAllDocs && writingToXML)
978             return -1;
979 	int textLen = text.length();
980 	if (textLen == 0)
981 	    return 0;
982         int index = -1;
983         // Handle some special cases
984         int fromindex = 0;
985         int ellipsis = text.indexOf(". . ."); // Handles one instance of this
986         if (ellipsis != -1)
987             fromindex = ellipsis + 5;
988         // If the first non-whitespace character is an @, go beyond it
989         int i = 0;
990         while (i < textLen && text.charAt(i) == ' ') {
991             i++;
992         }
993         if (text.charAt(i) == '@' && fromindex < textLen-1)
994             fromindex = i + 1;
995         // Use the brute force approach.
996         index = minIndex(index, text.indexOf("? ", fromindex));
997         index = minIndex(index, text.indexOf("?\t", fromindex));
998         index = minIndex(index, text.indexOf("?\n", fromindex));
999         index = minIndex(index, text.indexOf("?\r", fromindex));
1000         index = minIndex(index, text.indexOf("?\f", fromindex));
1001         index = minIndex(index, text.indexOf("! ", fromindex));
1002         index = minIndex(index, text.indexOf("!\t", fromindex));
1003         index = minIndex(index, text.indexOf("!\n", fromindex));
1004         index = minIndex(index, text.indexOf("!\r", fromindex));
1005         index = minIndex(index, text.indexOf("!\f", fromindex));
1006         index = minIndex(index, text.indexOf(". ", fromindex));
1007         index = minIndex(index, text.indexOf(".\t", fromindex));
1008         index = minIndex(index, text.indexOf(".\n", fromindex));
1009         index = minIndex(index, text.indexOf(".\r", fromindex));
1010         index = minIndex(index, text.indexOf(".\f", fromindex));
1011         index = minIndex(index, text.indexOf("@param", fromindex));
1012         index = minIndex(index, text.indexOf("@return", fromindex));
1013         index = minIndex(index, text.indexOf("@throw", fromindex));
1014         index = minIndex(index, text.indexOf("@serial", fromindex));
1015         index = minIndex(index, text.indexOf("@exception", fromindex));
1016         index = minIndex(index, text.indexOf("@deprecate", fromindex));
1017         index = minIndex(index, text.indexOf("@author", fromindex));
1018         index = minIndex(index, text.indexOf("@since", fromindex));
1019         index = minIndex(index, text.indexOf("@see", fromindex));
1020         index = minIndex(index, text.indexOf("@version", fromindex));
1021         if (doExclude && excludeTag != null)
1022             index = minIndex(index, text.indexOf(excludeTag));
1023         index = minIndex(index, text.indexOf("@vtexclude", fromindex));
1024         index = minIndex(index, text.indexOf("@vtinclude", fromindex));
1025         index = minIndex(index, text.indexOf("<p>", 2)); // Not at start
1026         index = minIndex(index, text.indexOf("<P>", 2)); // Not at start
1027         index = minIndex(index, text.indexOf("<blockquote", 2));  // Not at start
1028         index = minIndex(index, text.indexOf("<pre", fromindex)); // May contain anything!
1029         // Avoid the char at the start of a tag in some cases
1030         if (index != -1 &&
1031             (text.charAt(index) == '@' || text.charAt(index) == '<')) {
1032             if (index != 0)
1033                 index--;
1034         }
1035 
1036 /* Not used for jdiff, since tags are explicitly checked for above.
1037         // Look for a sentence terminated by an HTML tag.
1038         index = minIndex(index, text.indexOf(".<", fromindex));
1039         if (index == -1) {
1040             // If period-whitespace etc was not found, check to see if
1041             // last character is a period,
1042             int endIndex = text.length()-1;
1043             if (text.charAt(endIndex) == '.' ||
1044                 text.charAt(endIndex) == '?' ||
1045                 text.charAt(endIndex) == '!')
1046                 index = endIndex;
1047         }
1048 */
1049         return index;
1050     }
1051 
1052     /**
1053      * Return the minimum of two indexes if > -1, and return -1
1054      * only if both indexes = -1.
1055      * @param i an int index
1056      * @param j an int index
1057      * @return an int equal to the minimum index > -1, or -1
1058      */
minIndex(int i, int j)1059     public static int minIndex(int i, int j) {
1060         if (i == -1) return j;
1061         if (j == -1) return i;
1062         return Math.min(i,j);
1063     }
1064 
1065     /**
1066      * The name of the file where the XML representing the API will be
1067      * stored.
1068      */
1069     public static String outputFileName = null;
1070 
1071     /**
1072      * The identifier of the API being written out in XML, e.g.
1073      * &quotSuperProduct 1.3&quot;.
1074      */
1075     public static String apiIdentifier = null;
1076 
1077     /**
1078      * The file where the XML representing the API will be stored.
1079      */
1080     private static PrintWriter outputFile = null;
1081 
1082     /**
1083      * The name of the directory where the XML representing the API will be
1084      * stored.
1085      */
1086     public static String outputDirectory = null;
1087 
1088     /**
1089      * Do not display a class  with a lower level of visibility than this.
1090      * Default is to display all public and protected classes.
1091      */
1092     public static String classVisibilityLevel = "protected";
1093 
1094     /**
1095      * Do not display a member with a lower level of visibility than this.
1096      * Default is to display all public and protected members
1097      * (constructors, methods, fields).
1098      */
1099     public static String memberVisibilityLevel = "protected";
1100 
1101     /**
1102      * If set, then save the entire contents of a doc block comment in the
1103      * API file. If not set, then just save the first sentence. Default is
1104      * that this is set.
1105      */
1106     public static boolean saveAllDocs = true;
1107 
1108     /**
1109      * If set, exclude program elements marked with whatever the exclude tag
1110      * is specified as, e.g. "@exclude".
1111      */
1112     public static boolean doExclude = false;
1113 
1114     /**
1115      * Exclude program elements marked with this String, e.g. "@exclude".
1116      */
1117     public static String excludeTag = null;
1118 
1119     /**
1120      * The base URI for locating necessary DTDs and Schemas. By default, this
1121      * is "https://www.w3.org". A typical value to use local copies of DTD files
1122      * might be "file:///C:/jdiff/lib"
1123      */
1124     public static String baseURI = "https://www.w3.org";
1125 
1126     /**
1127      * If set, then strip out non-printing characters from documentation.
1128      * Default is that this is set.
1129      */
1130     static boolean stripNonPrintables = true;
1131 
1132     /**
1133      * If set, then add the information about the source file and line number
1134      * which is available in J2SE1.4. Default is that this is not set.
1135      */
1136     static boolean addSrcInfo = false;
1137 
1138     /**
1139      * If set, scan classes with no packages.
1140      * If the source is  a jar file this may duplicates classes, so
1141      * disable it using the -packagesonly option. Default is that this is
1142      * not set.
1143      */
1144     static boolean packagesOnly = false;
1145 
1146     /** Set to enable increased logging verbosity for debugging. */
1147     private static boolean trace = false;
1148 
1149 } //RootDocToXML
1150