KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jdiff > RootDocToXML


1 package jdiff;
2
3 import com.sun.javadoc.*;
4
5 import java.util.*;
6 import java.io.*;
7 import java.lang.reflect.*;
8
9 /**
10  * Converts a Javadoc RootDoc object into a representation in an
11  * XML file.
12  *
13  * See the file LICENSE.txt for copyright details.
14  * @author Matthew Doar, doar@pobox.com
15  */

16 public class RootDocToXML {
17
18     /** Default constructor. */
19     public RootDocToXML() {
20     }
21
22     /**
23      * Write the XML representation of the API to a file.
24      *
25      * @param root the RootDoc object passed by Javadoc
26      * @return true if no problems encountered
27      */

28     public static boolean writeXML(RootDoc root) {
29         String JavaDoc tempFileName = outputFileName;
30         if (outputDirectory != null) {
31         tempFileName = outputDirectory;
32         if (!tempFileName.endsWith(JDiff.DIR_SEP))
33         tempFileName += JDiff.DIR_SEP;
34         tempFileName += outputFileName;
35         }
36
37         try {
38             FileOutputStream fos = new FileOutputStream(tempFileName);
39             outputFile = new PrintWriter(fos);
40             System.out.println("JDiff: writing the API to file '" + tempFileName + "'...");
41             if (root.specifiedPackages().length != 0) {
42                 RootDocToXML apiWriter = new RootDocToXML();
43                 apiWriter.emitXMLHeader();
44                 apiWriter.logOptions();
45                 apiWriter.processPackages(root);
46                 apiWriter.emitXMLFooter();
47             }
48             outputFile.close();
49         } catch(IOException e) {
50             System.out.println("IO Error while attempting to create " + tempFileName);
51             System.out.println("Error: " + e.getMessage());
52             System.exit(1);
53         }
54         // If validation is desired, write out the appropriate api.xsd file
55
// in the same directory as the XML file.
56
if (XMLToAPI.validateXML) {
57             writeXSD();
58         }
59         return true;
60     }
61
62     /**
63      * Write the XML Schema file used for validation.
64      */

65     public static void writeXSD() {
66         String JavaDoc xsdFileName = outputFileName;
67         if (outputDirectory == null) {
68         int idx = xsdFileName.lastIndexOf('\\');
69         int idx2 = xsdFileName.lastIndexOf('/');
70         if (idx == -1 && idx2 == -1) {
71         xsdFileName = "";
72         } else if (idx == -1 && idx2 != -1) {
73         xsdFileName = xsdFileName.substring(0, idx2);
74         } else if (idx != -1 && idx2 == -1) {
75         xsdFileName = xsdFileName.substring(0, idx);
76         } else if (idx != -1 && idx2 != -1) {
77         int max = idx2 > idx ? idx2 : idx;
78         xsdFileName = xsdFileName.substring(0, max);
79         }
80     } else {
81         xsdFileName = outputDirectory;
82         if (!xsdFileName.endsWith(JDiff.DIR_SEP))
83          xsdFileName += JDiff.DIR_SEP;
84     }
85         xsdFileName += "api.xsd";
86         try {
87             FileOutputStream fos = new FileOutputStream(xsdFileName);
88             PrintWriter xsdFile = new PrintWriter(fos);
89             // The contents of the api.xsd file
90
xsdFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
91             xsdFile.println("<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">");
92             xsdFile.println("");
93             xsdFile.println("<xsd:annotation>");
94             xsdFile.println(" <xsd:documentation>");
95             xsdFile.println(" Schema for JDiff API representation.");
96             xsdFile.println(" </xsd:documentation>");
97             xsdFile.println("</xsd:annotation>");
98             xsdFile.println();
99             xsdFile.println("<xsd:element name=\"api\" type=\"apiType\"/>");
100             xsdFile.println("");
101             xsdFile.println("<xsd:complexType name=\"apiType\">");
102             xsdFile.println(" <xsd:sequence>");
103             xsdFile.println(" <xsd:element name=\"package\" type=\"packageType\" minOccurs='1' maxOccurs='unbounded'/>");
104             xsdFile.println(" </xsd:sequence>");
105             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
106             xsdFile.println(" <xsd:attribute name=\"jdversion\" type=\"xsd:string\"/>");
107             xsdFile.println("</xsd:complexType>");
108             xsdFile.println();
109             xsdFile.println("<xsd:complexType name=\"packageType\">");
110             xsdFile.println(" <xsd:sequence>");
111             xsdFile.println(" <xsd:choice maxOccurs='unbounded'>");
112             xsdFile.println(" <xsd:element name=\"class\" type=\"classType\"/>");
113             xsdFile.println(" <xsd:element name=\"interface\" type=\"classType\"/>");
114             xsdFile.println(" </xsd:choice>");
115             xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
116             xsdFile.println(" </xsd:sequence>");
117             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
118             xsdFile.println("</xsd:complexType>");
119             xsdFile.println();
120             xsdFile.println("<xsd:complexType name=\"classType\">");
121             xsdFile.println(" <xsd:sequence>");
122             xsdFile.println(" <xsd:element name=\"implements\" type=\"interfaceTypeName\" minOccurs='0' maxOccurs='unbounded'/>");
123             xsdFile.println(" <xsd:element name=\"constructor\" type=\"constructorType\" minOccurs='0' maxOccurs='unbounded'/>");
124             xsdFile.println(" <xsd:element name=\"method\" type=\"methodType\" minOccurs='0' maxOccurs='unbounded'/>");
125             xsdFile.println(" <xsd:element name=\"field\" type=\"fieldType\" minOccurs='0' maxOccurs='unbounded'/>");
126             xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
127             xsdFile.println(" </xsd:sequence>");
128             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
129             xsdFile.println(" <xsd:attribute name=\"extends\" type=\"xsd:string\" use='optional'/>");
130             xsdFile.println(" <xsd:attribute name=\"abstract\" type=\"xsd:boolean\"/>");
131             xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
132             xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
133             xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
134             xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
135             xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
136             xsdFile.println("</xsd:complexType>");
137             xsdFile.println();
138             xsdFile.println("<xsd:complexType name=\"interfaceTypeName\">");
139             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
140             xsdFile.println("</xsd:complexType>");
141             xsdFile.println();
142             xsdFile.println("<xsd:complexType name=\"constructorType\">");
143             xsdFile.println(" <xsd:sequence>");
144             xsdFile.println(" <xsd:element name=\"exception\" type=\"exceptionType\" minOccurs='0' maxOccurs='unbounded'/>");
145             xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
146             xsdFile.println(" </xsd:sequence>");
147             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
148             xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\" use='optional'/>");
149             xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
150             xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
151             xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
152             xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
153             xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
154             xsdFile.println("</xsd:complexType>");
155             xsdFile.println();
156             xsdFile.println("<xsd:complexType name=\"paramsType\">");
157             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
158             xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
159             xsdFile.println("</xsd:complexType>");
160             xsdFile.println();
161             xsdFile.println("<xsd:complexType name=\"exceptionType\">");
162             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
163             xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
164             xsdFile.println("</xsd:complexType>");
165             xsdFile.println();
166             xsdFile.println("<xsd:complexType name=\"methodType\">");
167             xsdFile.println(" <xsd:sequence>");
168             xsdFile.println(" <xsd:element name=\"param\" type=\"paramsType\" minOccurs='0' maxOccurs='unbounded'/>");
169             xsdFile.println(" <xsd:element name=\"exception\" type=\"exceptionType\" minOccurs='0' maxOccurs='unbounded'/>");
170             xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
171             xsdFile.println(" </xsd:sequence>");
172             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
173             xsdFile.println(" <xsd:attribute name=\"return\" type=\"xsd:string\" use='optional'/>");
174             xsdFile.println(" <xsd:attribute name=\"abstract\" type=\"xsd:boolean\"/>");
175             xsdFile.println(" <xsd:attribute name=\"native\" type=\"xsd:boolean\"/>");
176             xsdFile.println(" <xsd:attribute name=\"synchronized\" type=\"xsd:boolean\"/>");
177             xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
178             xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
179             xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
180             xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
181             xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
182             xsdFile.println("</xsd:complexType>");
183             xsdFile.println();
184             xsdFile.println("<xsd:complexType name=\"fieldType\">");
185             xsdFile.println(" <xsd:sequence>");
186             xsdFile.println(" <xsd:element name=\"doc\" type=\"xsd:string\" minOccurs='0' maxOccurs='1'/>");
187             xsdFile.println(" </xsd:sequence>");
188             xsdFile.println(" <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
189             xsdFile.println(" <xsd:attribute name=\"type\" type=\"xsd:string\"/>");
190             xsdFile.println(" <xsd:attribute name=\"transient\" type=\"xsd:boolean\"/>");
191             xsdFile.println(" <xsd:attribute name=\"volatile\" type=\"xsd:boolean\"/>");
192             xsdFile.println(" <xsd:attribute name=\"value\" type=\"xsd:string\" use='optional'/>");
193             xsdFile.println(" <xsd:attribute name=\"src\" type=\"xsd:string\" use='optional'/>");
194             xsdFile.println(" <xsd:attribute name=\"static\" type=\"xsd:boolean\"/>");
195             xsdFile.println(" <xsd:attribute name=\"final\" type=\"xsd:boolean\"/>");
196             xsdFile.println(" <xsd:attribute name=\"deprecated\" type=\"xsd:string\"/>");
197             xsdFile.println(" <xsd:attribute name=\"visibility\" type=\"xsd:string\"/>");
198             xsdFile.println("</xsd:complexType>");
199             xsdFile.println();
200             xsdFile.println("</xsd:schema>");
201             xsdFile.close();
202         } catch(IOException e) {
203             System.out.println("IO Error while attempting to create " + xsdFileName);
204             System.out.println("Error: " + e.getMessage());
205             System.exit(1);
206         }
207     }
208
209     /**
210      * Write the options which were used to generate this XML file
211      * out as XML comments.
212      */

213     public void logOptions() {
214         outputFile.print("<!-- ");
215         outputFile.print(" Command line arguments = " + Options.cmdOptions);
216         outputFile.println(" -->");
217     }
218
219     /**
220      * Process each package and the classes/interfaces within it.
221      *
222      * @param pd an array of PackageDoc objects
223      */

224     public void processPackages(RootDoc root) {
225         PackageDoc[] pd = root.specifiedPackages();
226         for (int i = 0; pd != null && i < pd.length; i++) {
227             String JavaDoc pkgName = pd[i].name();
228             
229             // Check for an exclude tag in the package doc block, but not
230
// in the package.htm[l] file.
231
if (!shownElement(pd[i], null))
232                 continue;
233
234             if (trace) System.out.println("PROCESSING PACKAGE: " + pkgName);
235             outputFile.println("<package name=\"" + pkgName + "\">");
236
237             int tagCount = pd[i].tags().length;
238             if (trace) System.out.println("#tags: " + tagCount);
239             
240             List classList = new LinkedList(Arrays.asList(pd[i].allClasses()));
241             Collections.sort(classList);
242             ClassDoc[] classes = new ClassDoc[classList.size()];
243             classes = (ClassDoc[])classList.toArray(classes);
244             processClasses(classes, pkgName);
245
246             addPkgDocumentation(root, pd[i], 2);
247
248             outputFile.println("</package>");
249         }
250
251         // Deal with classes which have no package.
252
// If the RootDoc object has been created from a jar file
253
// this duplicates classes, so need to be disable it.
254
ClassDoc[] cd = root.specifiedClasses();
255         if (!packagesOnly && cd != null && cd.length != 0) {
256             String JavaDoc pkgName = "anonymous";
257             outputFile.println("<package name=\"" + pkgName + "\">");
258             List classList = new LinkedList(Arrays.asList(cd));
259             Collections.sort(classList);
260             ClassDoc[] classes = new ClassDoc[classList.size()];
261             classes = (ClassDoc[])classList.toArray(classes);
262             processClasses(classes, pkgName);
263             outputFile.println("</package>");
264         }
265     } // processPackages
266

267     /**
268      * Process classes and interfaces.
269      *
270      * @param cd An array of ClassDoc objects.
271      */

272     public void processClasses(ClassDoc[] cd, String JavaDoc pkgName) {
273         if (cd.length == 0)
274             return;
275         if (trace) System.out.println("PROCESSING CLASSES, number=" + cd.length);
276         for (int i = 0; i < cd.length; i++) {
277             String JavaDoc className = cd[i].name();
278             if (trace) System.out.println("PROCESSING CLASS/IFC: " + className);
279             // Only save the shown elements
280
if (!shownElement(cd[i], classVisibilityLevel))
281                 continue;
282             boolean isInterface = false;
283             if (cd[i].isInterface())
284                 isInterface = true;
285             if (isInterface) {
286                 outputFile.println(" <!-- start interface " + pkgName + "." + className + " -->");
287                 outputFile.print(" <interface name=\"" + className + "\"");
288             } else {
289                 outputFile.println(" <!-- start class " + pkgName + "." + className + " -->");
290                 outputFile.print(" <class name=\"" + className + "\"");
291             }
292             // Add attributes to the class element
293
ClassDoc parent = cd[i].superclass();
294             if (parent != null)
295                 outputFile.println(" extends=\"" + parent.qualifiedName() + "\"");
296             outputFile.println(" abstract=\"" + cd[i].isAbstract() + "\"");
297             addCommonModifiers(cd[i], 4);
298             outputFile.println(">");
299             // Process class members. (Treat inner classes as members.)
300
processInterfaces(cd[i].interfaces());
301             processConstructors(cd[i].constructors());
302             processMethods(cd[i], cd[i].methods());
303             processFields(cd[i].fields());
304
305             addDocumentation(cd[i], 4);
306
307             if (isInterface) {
308                 outputFile.println(" </interface>");
309                 outputFile.println(" <!-- end interface " + pkgName + "." + className + " -->");
310             } else {
311                 outputFile.println(" </class>");
312                 outputFile.println(" <!-- end class " + pkgName + "." + className + " -->");
313             }
314             // Inner classes have already been added.
315
/*
316               ClassDoc[] ic = cd[i].innerClasses();
317               for (int k = 0; k < ic.length; k++) {
318               System.out.println("Inner class " + k + ", name = " + ic[k].name());
319               }
320             */

321         }//for
322
}//processClasses()
323

324     /**
325      * Add qualifiers for the program element as attributes.
326      *
327      * @param ped The given program element.
328      */

329     public void addCommonModifiers(ProgramElementDoc ped, int indent) {
330         addSourcePosition(ped, indent);
331         // Static and final and visibility on one line
332
for (int i = 0; i < indent; i++) outputFile.print(" ");
333         outputFile.print("static=\"" + ped.isStatic() + "\"");
334         outputFile.print(" final=\"" + ped.isFinal() + "\"");
335         // Visibility
336
String JavaDoc visibility = null;
337         if (ped.isPublic())
338             visibility = "public";
339         else if (ped.isProtected())
340             visibility = "protected";
341         else if (ped.isPackagePrivate())
342             visibility = "package";
343         else if (ped.isPrivate())
344             visibility = "private";
345         outputFile.println(" visibility=\"" + visibility + "\"");
346
347         // Deprecation on its own line
348
for (int i = 0; i < indent; i++) outputFile.print(" ");
349         boolean isDeprecated = false;
350         Tag[] ta = ((Doc)ped).tags("deprecated");
351         if (ta.length != 0) {
352             isDeprecated = true;
353         }
354         if (ta.length > 1) {
355             System.out.println("JDiff: warning: multiple @deprecated tags found in comments for " + ped.name() + ". Using the first one only.");
356             System.out.println("Text is: " + ((Doc)ped).getRawCommentText());
357         }
358         if (isDeprecated) {
359             String JavaDoc text = ta[0].text(); // Use only one @deprecated tag
360
if (text != null && text.compareTo("") != 0) {
361                 int idx = endOfFirstSentence(text);
362                 if (idx == 0) {
363                     // No useful comment
364
outputFile.print("deprecated=\"deprecated, no comment\"");
365                 } else {
366                     String JavaDoc fs = null;
367                     if (idx == -1)
368                         fs = text;
369                     else
370                         fs = text.substring(0, idx+1);
371                     String JavaDoc st = API.hideHTMLTags(fs);
372                     outputFile.print("deprecated=\"" + st + "\"");
373                 }
374             } else {
375                 outputFile.print("deprecated=\"deprecated, no comment\"");
376             }
377         } else {
378             outputFile.print("deprecated=\"not deprecated\"");
379         }
380
381     } //addQualifiers()
382

383     /**
384      * Insert the source code details, if available.
385      *
386      * @param ped The given program element.
387      */

388     public void addSourcePosition(ProgramElementDoc ped, int indent) {
389         if (!addSrcInfo)
390             return;
391         if (JDiff.javaVersion.startsWith("1.1") ||
392             JDiff.javaVersion.startsWith("1.2") ||
393             JDiff.javaVersion.startsWith("1.3")) {
394             return; // position() only appeared in J2SE1.4
395
}
396         try {
397             // Could cache the method for improved performance
398
Class JavaDoc c = ProgramElementDoc.class;
399             Method m = c.getMethod("position", null);
400             Object JavaDoc sp = m.invoke(ped, null);
401             if (sp != null) {
402                 for (int i = 0; i < indent; i++) outputFile.print(" ");
403                 outputFile.println("src=\"" + sp + "\"");
404             }
405         } catch (NoSuchMethodException JavaDoc e2) {
406             System.err.println("Error: method \"position\" not found");
407             e2.printStackTrace();
408         } catch (IllegalAccessException JavaDoc e4) {
409             System.err.println("Error: class not permitted to be instantiated");
410             e4.printStackTrace();
411         } catch (InvocationTargetException e5) {
412             System.err.println("Error: method \"position\" could not be invoked");
413             e5.printStackTrace();
414         } catch (Exception JavaDoc e6) {
415             System.err.println("Error: ");
416             e6.printStackTrace();
417         }
418     }
419
420     /**
421      * Process the interfaces implemented by the class.
422      *
423      * @param ifaces An array of ClassDoc objects
424      */

425     public void processInterfaces(ClassDoc[] ifaces) {
426         if (trace) System.out.println("PROCESSING INTERFACES, number=" + ifaces.length);
427         for (int i = 0; i < ifaces.length; i++) {
428             String JavaDoc ifaceName = ifaces[i].qualifiedName();
429             if (trace) System.out.println("PROCESSING INTERFACE: " + ifaceName);
430             outputFile.println(" <implements name=\"" + ifaceName + "\"/>");
431         }//for
432
}//processInterfaces()
433

434     /**
435      * Process the constructors in the class.
436      *
437      * @param ct An array of ConstructorDoc objects
438      */

439     public void processConstructors(ConstructorDoc[] ct) {
440         if (trace) System.out.println("PROCESSING CONSTRUCTORS, number=" + ct.length);
441         for (int i = 0; i < ct.length; i++) {
442             String JavaDoc ctorName = ct[i].name();
443             if (trace) System.out.println("PROCESSING CONSTRUCTOR: " + ctorName);
444             // Only save the shown elements
445
if (!shownElement(ct[i], memberVisibilityLevel))
446                 continue;
447             outputFile.print(" <constructor name=\"" + ctorName + "\"");
448
449             Parameter[] params = ct[i].parameters();
450             boolean first = true;
451             if (params.length != 0) {
452                 outputFile.print(" type=\"");
453                 for (int j = 0; j < params.length; j++) {
454                     if (!first)
455                         outputFile.print(", ");
456                     emitType(params[j].type());
457                     first = false;
458                 }
459                 outputFile.println("\"");
460             } else
461                 outputFile.println();
462             addCommonModifiers(ct[i], 6);
463             outputFile.println(">");
464             
465             // Generate the exception elements if any exceptions are thrown
466
processExceptions(ct[i].thrownExceptions());
467
468             addDocumentation(ct[i], 6);
469
470             outputFile.println(" </constructor>");
471         }//for
472
}//processConstructors()
473

474     /**
475      * Process all exceptions thrown by a constructor or method.
476      *
477      * @param cd An array of ClassDoc objects
478      */

479     public void processExceptions(ClassDoc[] cd) {
480         if (trace) System.out.println("PROCESSING EXCEPTIONS, number=" + cd.length);
481         for (int i = 0; i < cd.length; i++) {
482             String JavaDoc exceptionName = cd[i].name();
483             if (trace) System.out.println("PROCESSING EXCEPTION: " + exceptionName);
484             outputFile.print(" <exception name=\"" + exceptionName + "\" type=\"");
485             emitType(cd[i]);
486             outputFile.println("\"/>");
487         }//for
488
}//processExceptions()
489

490     /**
491      * Process the methods in the class.
492      *
493      * @param md An array of MethodDoc objects
494      */

495     public void processMethods(ClassDoc cd, MethodDoc[] md) {
496         if (trace) System.out.println("PROCESSING " +cd.name()+" METHODS, number = " + md.length);
497         for (int i = 0; i < md.length; i++) {
498             String JavaDoc methodName = md[i].name();
499             if (trace) System.out.println("PROCESSING METHOD: " + methodName);
500             // Skip <init> and <clinit>
501
if (methodName.startsWith("<"))
502                 continue;
503             // Only save the shown elements
504
if (!shownElement(md[i], memberVisibilityLevel))
505                 continue;
506             outputFile.print(" <method name=\"" + methodName + "\"");
507             com.sun.javadoc.Type retType = md[i].returnType();
508             if (retType.qualifiedTypeName().compareTo("void") == 0) {
509                 // Don't add a return attribute if the return type is void
510
outputFile.println();
511             } else {
512                 outputFile.print(" return=\"");
513                 emitType(retType);
514                 outputFile.println("\"");
515             }
516             outputFile.print(" abstract=\"" + md[i].isAbstract() + "\"");
517             outputFile.print(" native=\"" + md[i].isNative() + "\"");
518             outputFile.println(" synchronized=\"" + md[i].isSynchronized() + "\"");
519             addCommonModifiers(md[i], 6);
520             outputFile.println(">");
521             // Generate the parameter elements, if any
522
Parameter[] params = md[i].parameters();
523             for (int j = 0; j < params.length; j++) {
524                 outputFile.print(" <param name=\"" + params[j].name() + "\"");
525                 outputFile.print(" type=\"");
526                 emitType(params[j].type());
527                 outputFile.println("\"/>");
528             }
529
530             // Generate the exception elements if any exceptions are thrown
531
processExceptions(md[i].thrownExceptions());
532
533             addDocumentation(md[i], 6);
534
535             outputFile.println(" </method>");
536         }//for
537
}//processMethods()
538

539     /**
540      * Process the fields in the class.
541      *
542      * @param fd An array of FieldDoc objects
543      */

544     public void processFields(FieldDoc[] fd) {
545         if (trace) System.out.println("PROCESSING FIELDS, number=" + fd.length);
546         for (int i = 0; i < fd.length; i++) {
547             String JavaDoc fieldName = fd[i].name();
548             if (trace) System.out.println("PROCESSING FIELD: " + fieldName);
549             // Only save the shown elements
550
if (!shownElement(fd[i], memberVisibilityLevel))
551                 continue;
552             outputFile.print(" <field name=\"" + fieldName + "\"");
553             outputFile.print(" type=\"");
554             emitType(fd[i].type());
555             outputFile.println("\"");
556             outputFile.print(" transient=\"" + fd[i].isTransient() + "\"");
557             outputFile.println(" volatile=\"" + fd[i].isVolatile() + "\"");
558 /* JDK 1.4 and later */
559 /*
560             String value = fd[i].constantValueExpression();
561             if (value != null)
562                 outputFile.println(" value=\"" + value + "\"");
563 */

564             addCommonModifiers(fd[i], 6);
565             outputFile.println(">");
566
567             addDocumentation(fd[i], 6);
568
569             outputFile.println(" </field>");
570
571         }//for
572
}//processFields()
573

574     /**
575      * Emit the type name. Removed any prefixed warnings about ambiguity.
576      * The type maybe an array.
577      *
578      * @param type A Type object.
579      */

580     public void emitType(com.sun.javadoc.Type type) {
581     if (type == null)
582         return;
583         String JavaDoc name = type.qualifiedTypeName();
584         if (name.startsWith("<<ambiguous>>"))
585             name = name.substring(13);
586         outputFile.print(name + type.dimension());
587     }
588
589     /**
590      * Emit the XML header.
591      */

592     public void emitXMLHeader() {
593         outputFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
594         outputFile.println("<!-- Generated by the JDiff Javadoc doclet -->");
595         outputFile.println("<!-- (" + JDiff.jDiffLocation + ") -->");
596         outputFile.println("<!-- on " + new Date() + " -->");
597         outputFile.println();
598 /* No need for this any longer, since doc block text is in an CDATA element
599         outputFile.println("<!-- XML Schema is used, but XHTML transitional DTD is needed for nbsp -->");
600         outputFile.println("<!-- entity definitions etc.-->");
601         outputFile.println("<!DOCTYPE api");
602         outputFile.println(" PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
603         outputFile.println(" \"" + baseURI + "/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
604 */

605         outputFile.println("<api");
606         outputFile.println(" xmlns:xsi='" + baseURI + "/2001/XMLSchema-instance'");
607         outputFile.println(" xsi:noNamespaceSchemaLocation='api.xsd'");
608         outputFile.println(" name=\"" + apiIdentifier + "\"");
609         outputFile.println(" jdversion=\"" + JDiff.version + "\">");
610         outputFile.println();
611     }
612
613     /**
614      * Emit the XML footer.
615      */

616     public void emitXMLFooter() {
617         outputFile.println();
618         outputFile.println("</api>");
619     }
620
621     /**
622      * Determine if the program element is shown, according to the given
623      * level of visibility.
624      *
625      * @param ped The given program element.
626      * @param visLevel The desired visibility level; "public", "protected",
627      * "package" or "private". If null, only check for an exclude tag.
628      * @return boolean Set if this element is shown.
629      */

630     public boolean shownElement(Doc doc, String JavaDoc visLevel) {
631         // If a doc block contains @exclude or a similar such tag,
632
// then don't display it.
633
if (doExclude && excludeTag != null && doc != null) {
634             String JavaDoc rct = doc.getRawCommentText();
635             if (rct != null && rct.indexOf(excludeTag) != -1) {
636                 return false;
637         }
638     }
639     if (visLevel == null) {
640         return true;
641     }
642     ProgramElementDoc ped = null;
643     if (doc instanceof ProgramElementDoc) {
644         ped = (ProgramElementDoc)doc;
645     }
646         if (visLevel.compareTo("private") == 0)
647             return true;
648         // Show all that is not private
649
if (visLevel.compareTo("package") == 0)
650             return !ped.isPrivate();
651         // Show all that is not private or package
652
if (visLevel.compareTo("protected") == 0)
653             return !(ped.isPrivate() || ped.isPackagePrivate());
654         // Show all that is not private or package or protected,
655
// i.e. all that is public
656
if (visLevel.compareTo("public") == 0)
657             return ped.isPublic();
658         return false;
659     } //shownElement()
660

661     /**
662      * Strip out non-printing characters, replacing them with a character
663      * which will not change where the end of the first sentence is found.
664      * This character is the hash mark, '&#035;'.
665      */

666     public String JavaDoc stripNonPrintingChars(String JavaDoc s, Doc doc) {
667         if (!stripNonPrintables)
668             return s;
669         char[] sa = s.toCharArray();
670         for (int i = 0; i < sa.length; i++) {
671             char c = sa[i];
672             // TODO still have an issue with Unicode: 0xfc in java.lang.String.toUpperCase comments "Fahrvergn" && c != '§' && c != ''
673
// if (Character.isDefined(c))
674
if (Character.isLetterOrDigit(c))
675                 continue;
676             // There must be a better way that is still platform independent!
677
if (c == ' ' ||
678                 c == '.' ||
679                 c == ',' ||
680                 c == '\r' ||
681                 c == '\t' ||
682                 c == '\n' ||
683                 c == '!' ||
684                 c == '?' ||
685                 c == ';' ||
686                 c == ':' ||
687                 c == '[' ||
688                 c == ']' ||
689                 c == '(' ||
690                 c == ')' ||
691                 c == '~' ||
692                 c == '@' ||
693                 c == '#' ||
694                 c == '$' ||
695                 c == '%' ||
696                 c == '^' ||
697                 c == '&' ||
698                 c == '*' ||
699                 c == '-' ||
700                 c == '=' ||
701                 c == '+' ||
702                 c == '_' ||
703                 c == '|' ||
704                 c == '\\' ||
705                 c == '/' ||
706                 c == '\'' ||
707                 c == '}' ||
708                 c == '{' ||
709                 c == '"' ||
710                 c == '<' ||
711                 c == '>' ||
712                 c == '`'
713                 )
714                 continue;
715 /* Doesn't seem to return the expected values?
716             int val = Character.getNumericValue(c);
717 // if (s.indexOf("which is also a test for non-printable") != -1)
718 // System.out.println("** Char " + i + "[" + c + "], val =" + val); //DEBUG
719             // Ranges from http://www.unicode.org/unicode/reports/tr20/
720             // Should really replace 0x2028 and 0x2029 with <br/>
721             if (val == 0x0 ||
722                 inRange(val, 0x2028, 0x2029) ||
723                 inRange(val, 0x202A, 0x202E) ||
724                 inRange(val, 0x206A, 0x206F) ||
725                 inRange(val, 0xFFF9, 0xFFFC) ||
726                 inRange(val, 0xE0000, 0xE007F)) {
727                 if (trace) {
728                     System.out.println("Warning: changed non-printing character " + sa[i] + " in " + doc.name());
729                 }
730                 sa[i] = '#';
731             }
732 */

733             // Replace the non-printable character with a printable character
734
// which does not change the end of the first sentence
735
sa[i] = '#';
736         }
737         return new String JavaDoc(sa);
738     }
739
740     /** Return true if val is in the range [min|max], inclusive. */
741     public boolean inRange(int val, int min, int max) {
742         if (val < min)
743             return false;
744         if (val > max)
745             return false;
746         return true;
747     }
748
749     /**
750      * Add at least the first sentence from a doc block to the API. This is
751      * used by the report generator if no comment is provided.
752      * Need to make sure that HTML tags are not confused with XML tags.
753      * This could be done by stuffing the &lt; character to another string
754      * or by handling HTML in the parser. This second option seems neater. Note that
755      * XML expects all element tags to have either a closing "/>" or a matching
756      * end element tag. Due to the difficulties of converting incorrect HTML
757      * to XHTML, the first option is used.
758      */

759     public void addDocumentation(ProgramElementDoc ped, int indent) {
760         String JavaDoc rct = ((Doc)ped).getRawCommentText();
761         if (rct != null) {
762             rct = stripNonPrintingChars(rct, (Doc)ped);
763             rct = rct.trim();
764             if (rct.compareTo("") != 0 &&
765                 rct.indexOf(Comments.placeHolderText) == -1 &&
766                 rct.indexOf("InsertOtherCommentsHere") == -1) {
767                 int idx = endOfFirstSentence(rct);
768                 if (idx == 0)
769                     return;
770                 for (int i = 0; i < indent; i++) outputFile.print(" ");
771                 outputFile.println("<doc>");
772                 for (int i = 0; i < indent; i++) outputFile.print(" ");
773                 String JavaDoc firstSentence = null;
774                 if (idx == -1)
775                     firstSentence = rct;
776                 else
777                     firstSentence = rct.substring(0, idx+1);
778                 boolean checkForAts = false;
779                 if (checkForAts && firstSentence.indexOf("@") != -1 &&
780                     firstSentence.indexOf("@link") == -1) {
781                     System.out.println("Warning: @ tag seen in comment: " +
782                                        firstSentence);
783                 }
784                 String JavaDoc firstSentenceNoTags = API.stuffHTMLTags(firstSentence);
785                 outputFile.println(firstSentenceNoTags);
786                 for (int i = 0; i < indent; i++) outputFile.print(" ");
787                 outputFile.println("</doc>");
788             }
789         }
790     }
791
792     /**
793      * Add at least the first sentence from a doc block for a package to the API. This is
794      * used by the report generator if no comment is provided.
795      * The default source tree may not include the package.html files, so
796      * this may be unavailable in many cases.
797      * Need to make sure that HTML tags are not confused with XML tags.
798      * This could be done by stuffing the &lt; character to another string
799      * or by handling HTML in the parser. This second option is neater. Note that
800      * XML expects all element tags to have either a closing "/>" or a matching
801      * end element tag. Due to the difficulties of converting incorrect HTML
802      * to XHTML, the first option is used.
803      */

804     public void addPkgDocumentation(RootDoc root, PackageDoc pd, int indent) {
805         String JavaDoc rct = null;
806         String JavaDoc filename = pd.name();
807         try {
808             // See if the source path was specified as part of the
809
// options and prepend it if it was.
810
String JavaDoc srcLocation = null;
811             String JavaDoc[][] options = root.options();
812             for (int opt = 0; opt < options.length; opt++) {
813                 if ((options[opt][0]).compareTo("-sourcepath") == 0) {
814                     srcLocation = options[opt][1];
815                     break;
816                 }
817             }
818             filename = filename.replace('.', JDiff.DIR_SEP.charAt(0));
819             if (srcLocation != null) {
820                 // Make a relative location absolute
821
if (srcLocation.startsWith("..")) {
822                     String JavaDoc curDir = System.getProperty("user.dir");
823                     while (srcLocation.startsWith("..")) {
824                         srcLocation = srcLocation.substring(3);
825                         int idx = curDir.lastIndexOf(JDiff.DIR_SEP);
826                         curDir = curDir.substring(0, idx+1);
827                     }
828                     srcLocation = curDir + srcLocation;
829                 }
830                 filename = srcLocation + JDiff.DIR_SEP + filename;
831             }
832             // Try both ".htm" and ".html"
833
filename += JDiff.DIR_SEP + "package.htm";
834             File f2 = new File(filename);
835             if (!f2.exists()) {
836                 filename += "l";
837             }
838             FileInputStream f = new FileInputStream(filename);
839             BufferedReader d = new BufferedReader(new InputStreamReader(f));
840             String JavaDoc str = d.readLine();
841         // Ignore everything except the lines between <body> elements
842
boolean inBody = false;
843         while(str != null) {
844                 if (!inBody) {
845             if (str.toLowerCase().trim().startsWith("<body")) {
846             inBody = true;
847             }
848             str = d.readLine(); // Get the next line
849
continue; // Ignore the line
850
} else {
851             if (str.toLowerCase().trim().startsWith("</body")) {
852             inBody = false;
853             continue; // Ignore the line
854
}
855         }
856                 if (rct == null)
857                     rct = str + "\n";
858                 else
859                     rct += str + "\n";
860                 str = d.readLine();
861             }
862         } catch(java.io.FileNotFoundException JavaDoc e) {
863             // If it doesn't exist, that's fine
864
if (trace)
865                 System.out.println("No package level documentation file at '" + filename + "'");
866         } catch(java.io.IOException JavaDoc e) {
867             System.out.println("Error reading file \"" + filename + "\": " + e.getMessage());
868             System.exit(5);
869         }
870         if (rct != null) {
871             rct = stripNonPrintingChars(rct, (Doc)pd);
872             rct = rct.trim();
873             if (rct.compareTo("") != 0 &&
874                 rct.indexOf(Comments.placeHolderText) == -1 &&
875                 rct.indexOf("InsertOtherCommentsHere") == -1) {
876                 int idx = endOfFirstSentence(rct);
877                 if (idx == 0)
878                     return;
879                 for (int i = 0; i < indent; i++) outputFile.print(" ");
880                 outputFile.println("<doc>");
881                 for (int i = 0; i < indent; i++) outputFile.print(" ");
882                 String JavaDoc firstSentence = null;
883                 if (idx == -1)
884                     firstSentence = rct;
885                 else
886                     firstSentence = rct.substring(0, idx+1);
887                 String JavaDoc firstSentenceNoTags = API.stuffHTMLTags(firstSentence);
888                 outputFile.println(firstSentenceNoTags);
889                 for (int i = 0; i < indent; i++) outputFile.print(" ");
890                 outputFile.println("</doc>");
891             }
892         }
893     }
894
895     /**
896      * Find the index of the end of the first sentence in the given text,
897      * when writing out to an XML file.
898      * This is an extended version of the algorithm used by the DocCheck
899      * Javadoc doclet. It checks for @tags too.
900      *
901      * @param text The text to be searched.
902      * @return The index of the end of the first sentence. If there is no
903      * end, return -1. If there is no useful text, return 0.
904      * If the whole doc block comment is wanted (default), return -1.
905      */

906     public static int endOfFirstSentence(String JavaDoc text) {
907         return endOfFirstSentence(text, true);
908     }
909
910     /**
911      * Find the index of the end of the first sentence in the given text.
912      * This is an extended version of the algorithm used by the DocCheck
913      * Javadoc doclet. It checks for &#064;tags too.
914      *
915      * @param text The text to be searched.
916      * @param writingToXML Set to true when writing out XML.
917      * @return The index of the end of the first sentence. If there is no
918      * end, return -1. If there is no useful text, return 0.
919      * If the whole doc block comment is wanted (default), return -1.
920      */

921     public static int endOfFirstSentence(String JavaDoc text, boolean writingToXML) {
922         if (saveAllDocs && writingToXML)
923             return -1;
924     int textLen = text.length();
925     if (textLen == 0)
926         return 0;
927         int index = -1;
928         // Handle some special cases
929
int fromindex = 0;
930         int ellipsis = text.indexOf(". . ."); // Handles one instance of this
931
if (ellipsis != -1)
932             fromindex = ellipsis + 5;
933         // If the first non-whitespace character is an @, go beyond it
934
int i = 0;
935         while (i < textLen && text.charAt(i) == ' ') {
936             i++;
937         }
938         if (text.charAt(i) == '@' && fromindex < textLen-1)
939             fromindex = i + 1;
940         // Use the brute force approach.
941
index = minIndex(index, text.indexOf("? ", fromindex));
942         index = minIndex(index, text.indexOf("?\t", fromindex));
943         index = minIndex(index, text.indexOf("?\n", fromindex));
944         index = minIndex(index, text.indexOf("?\r", fromindex));
945         index = minIndex(index, text.indexOf("?\f", fromindex));
946         index = minIndex(index, text.indexOf("! ", fromindex));
947         index = minIndex(index, text.indexOf("!\t", fromindex));
948         index = minIndex(index, text.indexOf("!\n", fromindex));
949         index = minIndex(index, text.indexOf("!\r", fromindex));
950         index = minIndex(index, text.indexOf("!\f", fromindex));
951         index = minIndex(index, text.indexOf(". ", fromindex));
952         index = minIndex(index, text.indexOf(".\t", fromindex));
953         index = minIndex(index, text.indexOf(".\n", fromindex));
954         index = minIndex(index, text.indexOf(".\r", fromindex));
955         index = minIndex(index, text.indexOf(".\f", fromindex));
956         index = minIndex(index, text.indexOf("@param", fromindex));
957         index = minIndex(index, text.indexOf("@return", fromindex));
958         index = minIndex(index, text.indexOf("@throw", fromindex));
959         index = minIndex(index, text.indexOf("@serial", fromindex));
960         index = minIndex(index, text.indexOf("@exception", fromindex));
961         index = minIndex(index, text.indexOf("@deprecate", fromindex));
962         index = minIndex(index, text.indexOf("@author", fromindex));
963         index = minIndex(index, text.indexOf("@since", fromindex));
964         index = minIndex(index, text.indexOf("@see", fromindex));
965         index = minIndex(index, text.indexOf("@version", fromindex));
966         if (doExclude && excludeTag != null)
967             index = minIndex(index, text.indexOf(excludeTag));
968         index = minIndex(index, text.indexOf("@vtexclude", fromindex));
969         index = minIndex(index, text.indexOf("@vtinclude", fromindex));
970         index = minIndex(index, text.indexOf("<p>", 2)); // Not at start
971
index = minIndex(index, text.indexOf("<P>", 2)); // Not at start
972
index = minIndex(index, text.indexOf("<blockquote", 2)); // Not at start
973
index = minIndex(index, text.indexOf("<pre", fromindex)); // May contain anything!
974
// Avoid the char at the start of a tag in some cases
975
if (index != -1 &&
976             (text.charAt(index) == '@' || text.charAt(index) == '<')) {
977             if (index != 0)
978                 index--;
979         }
980         
981 /* Not used for jdiff, since tags are explicitly checked for above.
982         // Look for a sentence terminated by an HTML tag.
983         index = minIndex(index, text.indexOf(".<", fromindex));
984         if (index == -1) {
985             // If period-whitespace etc was not found, check to see if
986             // last character is a period,
987             int endIndex = text.length()-1;
988             if (text.charAt(endIndex) == '.' ||
989                 text.charAt(endIndex) == '?' ||
990                 text.charAt(endIndex) == '!')
991                 index = endIndex;
992         }
993 */

994         return index;
995     }
996     
997     /**
998      * Return the minimum of two indexes if > -1, and return -1
999      * only if both indexes = -1.
1000     * @param i an int index
1001     * @param j an int index
1002     * @return an int equal to the minimum index > -1, or -1
1003     */

1004    public static int minIndex(int i, int j) {
1005        if (i == -1) return j;
1006        if (j == -1) return i;
1007        return Math.min(i,j);
1008    }
1009    
1010    /**
1011     * The name of the file where the XML representing the API will be
1012     * stored.
1013     */

1014    public static String JavaDoc outputFileName = null;
1015
1016    /**
1017     * The identifier of the API being written out in XML, e.g.
1018     * &quotSuperProduct 1.3&quot;.
1019     */

1020    public static String JavaDoc apiIdentifier = null;
1021
1022    /**
1023     * The file where the XML representing the API will be stored.
1024     */

1025    private static PrintWriter outputFile = null;
1026    
1027    /**
1028     * The name of the directory where the XML representing the API will be
1029     * stored.
1030     */

1031    public static String JavaDoc outputDirectory = null;
1032
1033    /**
1034     * Do not display a class with a lower level of visibility than this.
1035     * Default is to display all public and protected classes.
1036     */

1037    public static String JavaDoc classVisibilityLevel = "protected";
1038
1039    /**
1040     * Do not display a member with a lower level of visibility than this.
1041     * Default is to display all public and protected members
1042     * (constructors, methods, fields).
1043     */

1044    public static String JavaDoc memberVisibilityLevel = "protected";
1045
1046    /**
1047     * If set, then save the entire contents of a doc block comment in the
1048     * API file. If not set, then just save the first sentence. Default is
1049     * that this is set.
1050     */

1051    public static boolean saveAllDocs = true;
1052
1053    /**
1054     * If set, exclude program elements marked with whatever the exclude tag
1055     * is specified as, e.g. "@exclude".
1056     */

1057    public static boolean doExclude = false;
1058
1059    /**
1060     * Exclude program elements marked with this String, e.g. "@exclude".
1061     */

1062    public static String JavaDoc excludeTag = null;
1063
1064    /**
1065     * The base URI for locating necessary DTDs and Schemas. By default, this
1066     * is "http://www.w3.org". A typical value to use local copies of DTD files
1067     * might be "file:///C:/jdiff/lib"
1068     */

1069    public static String JavaDoc baseURI = "http://www.w3.org";
1070
1071    /**
1072     * If set, then strip out non-printing characters from documentation.
1073     * Default is that this is set.
1074     */

1075    static boolean stripNonPrintables = true;
1076
1077    /**
1078     * If set, then add the information about the source file and line number
1079     * which is available in J2SE1.4. Default is that this is not set.
1080     */

1081    static boolean addSrcInfo = false;
1082
1083    /**
1084     * If set, scan classes with no packages.
1085     * If the source is a jar file this may duplicates classes, so
1086     * disable it using the -packagesonly option. Default is that this is
1087     * not set.
1088     */

1089    static boolean packagesOnly = false;
1090
1091    /** Set to enable increased logging verbosity for debugging. */
1092    private static boolean trace = false;
1093
1094} //RootDocToXML
1095
Popular Tags