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