1 package org.apache.maven.jellydoc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import java.io.IOException;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.util.Enumeration;
24 import java.util.Vector;
25
26 import com.sun.javadoc.ClassDoc;
27 import com.sun.javadoc.ConstructorDoc;
28 import com.sun.javadoc.Doc;
29 import com.sun.javadoc.DocErrorReporter;
30 import com.sun.javadoc.Doclet;
31 import com.sun.javadoc.ExecutableMemberDoc;
32 import com.sun.javadoc.FieldDoc;
33 import com.sun.javadoc.MethodDoc;
34 import com.sun.javadoc.PackageDoc;
35 import com.sun.javadoc.Parameter;
36 import com.sun.javadoc.RootDoc;
37 import com.sun.javadoc.Tag;
38 import com.sun.javadoc.ThrowsTag;
39 import com.sun.javadoc.Type;
40
41 import org.dom4j.io.OutputFormat;
42 import org.dom4j.io.XMLWriter;
43
44 import org.xml.sax.Attributes;
45 import org.xml.sax.ContentHandler;
46 import org.xml.sax.SAXException;
47 import org.xml.sax.helpers.AttributesImpl;
48
49 /**
50 * Main Doclet class to generate JavaDocXML. This doclet generates the
51 * document conforming to DTD specified in javadoc-v04draft.dtd.
52 * @author <a href="mailto:gopi@aztecsoft.com">Gopinath M.R.</a>
53 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
54 */
55
56 public class XMLDoclet extends Doclet
57 {
58
59 private String xmlns = "jvx";
60 private String encodingFormat="UTF-8";
61 private String localName = "javadoc";
62 private ContentHandler cm = null;
63 private String targetFileName = null;
64 private Attributes emptyAtts = new AttributesImpl();
65
66 public XMLDoclet (RootDoc root) throws Exception {
67 readOptions(root);
68 File targetFile = new File(targetFileName);
69 targetFile.getParentFile().mkdirs();
70 FileOutputStream writer = new FileOutputStream(targetFileName);
71 OutputFormat format = OutputFormat.createPrettyPrint();
72 format.setEncoding(encodingFormat);
73 XMLWriter xmlWriter = new XMLWriter(writer, format);
74 try
75 {
76 cm = xmlWriter;
77 cm.startDocument();
78 javadocXML(root);
79 cm.endDocument();
80 xmlWriter.close();
81 }
82 catch (IOException e)
83 {
84 xmlWriter.close();
85 throw e;
86 }
87 }
88
89 /**
90 * Generates the xml data for the top element.
91 * <xmp><!ELEMENT javadoc (package*, class*, interface*)></xmp>
92 */
93 private void javadocXML(RootDoc root) throws SAXException {
94 cm.startElement(xmlns, localName, "javadoc", emptyAtts);
95 PackageDoc[] packageArray = root.specifiedPackages();
96
97
98 for (int i = 0; i < packageArray.length; ++i) {
99 packageXML(packageArray[i]);
100 }
101
102
103 ClassDoc[] classArray = root.specifiedClasses();
104 Vector interfaceVector = new Vector();
105 for (int i = 0; i < classArray.length; ++i) {
106 if (classArray[i].isInterface()) {
107 interfaceVector.addElement(classArray[i]);
108 } else {
109 classXML(classArray[i]);
110 }
111 }
112
113
114 Enumeration interfaceEnum = interfaceVector.elements();
115 if (interfaceEnum.hasMoreElements()) {
116 ClassDoc interfaceDoc = (ClassDoc)interfaceEnum.nextElement();
117 interfaceXML(interfaceDoc);
118 }
119 cm.endElement(xmlns, localName, "javadoc");
120 }
121
122 /**
123 * Generates doc for "package".
124 * <xmp><!ELEMENT package (doc?, package*, class*, interface*)>
125 *<!ATTLIST package
126 * name CDATA #REQUIRED></xmp>
127 */
128 private void packageXML(PackageDoc packageDoc) throws SAXException {
129 AttributesImpl atts = new AttributesImpl();
130 atts.addAttribute(xmlns, localName, "name", "String", packageDoc.name());
131 cm.startElement(xmlns, localName, "package", atts);
132
133
134 docXML(packageDoc);
135
136
137
138
139
140
141 ClassDoc[] classArray = packageDoc.ordinaryClasses();
142 for (int i = 0; i < classArray.length; ++i) {
143 classXML(classArray[i]);
144 }
145
146 classArray = packageDoc.exceptions();
147 for (int i = 0; i < classArray.length; ++i) {
148 classXML(classArray[i]);
149 }
150
151 classArray = packageDoc.errors();
152 for (int i = 0; i < classArray.length; ++i) {
153 classXML(classArray[i]);
154 }
155
156
157 ClassDoc[] interfaceArray = packageDoc.interfaces();
158 for (int i = 0; i < interfaceArray.length; ++i) {
159 interfaceXML(interfaceArray[i]);
160 }
161
162 cm.endElement(xmlns, localName, "package");
163 }
164
165 /**
166 * Generates doc for element "class".
167 * <xmp> <!ELEMENT class (doc?,
168 * extends_class?,
169 * implements?,
170 * constructor*,
171 * method*,
172 * innerclass*)>
173 * <!ATTLIST class
174 * %name;
175 * %extensibility;
176 * %class.access;></xmp>
177 */
178 private void classXML(ClassDoc classDoc) throws SAXException {
179 AttributesImpl atts = new AttributesImpl();
180 atts.addAttribute(xmlns, localName, "name", "String", classDoc.name());
181
182 String extensibility = "default";
183 if (classDoc.isAbstract()) {
184 extensibility = "abstract";
185 } else if (classDoc.isFinal()) {
186 extensibility = "final";
187 }
188 atts.addAttribute(xmlns, localName, "extensibility", "String", extensibility);
189 String access = "package";
190 if (classDoc.isPublic()) {
191 access = "public";
192 }
193 atts.addAttribute(xmlns, localName, "access", "String", access);
194 cm.startElement(xmlns, localName, "class", atts);
195
196
197 docXML(classDoc);
198
199
200 extendsXML(classDoc);
201
202
203 implementsXML(classDoc);
204
205
206 FieldDoc[] fieldArray = classDoc.fields();
207 for (int i = 0; i < fieldArray.length; ++i) {
208 fieldXML(fieldArray[i]);
209 }
210
211
212 ConstructorDoc[] constructorArray = classDoc.constructors();
213 for (int i = 0; i < constructorArray.length; ++i) {
214 constructorXML(constructorArray[i]);
215 }
216
217
218 MethodDoc[] methodArray = classDoc.methods();
219 for (int i = 0; i < methodArray.length; ++i) {
220 methodXML(methodArray[i]);
221 }
222
223
224 ClassDoc[] innerClassArray = classDoc.innerClasses();
225 for (int i = 0; i < innerClassArray.length; ++i) {
226 innerClassXML(innerClassArray[i]);
227 }
228
229 cm.endElement(xmlns, localName, "class");
230 }
231
232 /**
233 * Generates doc for element "extends_class"
234 * <xmp><!ELEMENT extends_class (classref+)></xmp>
235 */
236 private void extendsXML(ClassDoc classDoc) throws SAXException {
237 if (classDoc.superclass() != null) {
238 cm.startElement(xmlns, localName, "extends_class", emptyAtts);
239 createRefXML("classref", classDoc.superclass().qualifiedName());
240 cm.endElement(xmlns, localName, "extends_class");
241 }
242 }
243
244 /**
245 * Generates doc for element "innerclass"
246 * <xmp> <!ELEMENT innerclass (doc?,
247 * extends?,
248 * implements?,
249 * field*,
250 * constructor*,
251 * method*)>
252 * <!ATTLIST innerclass
253 * %name;
254 * %access;
255 * %abstract;
256 * %anonymous;
257 * %final;
258 * %static;></xmp>
259 */
260 private void innerClassXML(ClassDoc classDoc) throws SAXException {
261 AttributesImpl atts = new AttributesImpl();
262 atts.addAttribute(xmlns, localName, "name", "String", classDoc.name());
263 String access = "package";
264 if (classDoc.isPublic()) {
265 access = "public";
266 }
267 atts.addAttribute(xmlns, localName, "access", "String", access);
268 atts.addAttribute(xmlns, localName, "abstract", "String", ""+ classDoc.isAbstract());
269 String anonymous = "false";
270 if (classDoc.name().equals("")) {
271 anonymous = "true";
272 }
273 atts.addAttribute(xmlns, localName, "anonymous", "String", ""+ anonymous);
274 atts.addAttribute(xmlns, localName, "final", "String", ""+ "" + classDoc.isFinal());
275 atts.addAttribute(xmlns, localName, "static", "String", ""+ "" + classDoc.isStatic());
276 cm.startElement(xmlns, localName, "innerclass", atts);
277
278
279 docXML(classDoc);
280
281
282 extendsXML(classDoc);
283
284
285 implementsXML(classDoc);
286
287
288 FieldDoc[] fieldArray = classDoc.fields();
289 for (int i = 0; i < fieldArray.length; ++i) {
290 fieldXML(fieldArray[i]);
291 }
292
293
294 ConstructorDoc[] constructorArray = classDoc.constructors();
295 for (int i = 0; i < constructorArray.length; ++i) {
296 constructorXML(constructorArray[i]);
297 }
298
299
300 MethodDoc[] methodArray = classDoc.methods();
301 for (int i = 0; i < methodArray.length; ++i) {
302 methodXML(methodArray[i]);
303 }
304
305 cm.endElement(xmlns, localName,"innerclass");
306 }
307
308 /**
309 * Generates doc for element "interface"
310 * <xmp><!ELEMENT interface (doc?,
311 * extends_interface?,
312 * field*,
313 * method*)>
314 * <!ATTLIST interface
315 * %name;
316 * %access;></xmp>
317 */
318 private void interfaceXML(ClassDoc interfaceDoc) throws SAXException {
319 AttributesImpl atts = new AttributesImpl();
320 atts.addAttribute(xmlns, localName, "name", "String", interfaceDoc.name());
321 String access = "package";
322 if (interfaceDoc.isPublic()) {
323 access = "public";
324 }
325 atts.addAttribute(xmlns, localName, "access", "String", access);
326 cm.startElement(xmlns, localName, "interface", atts);
327
328
329 docXML(interfaceDoc);
330
331
332 extends_interfaceXML(interfaceDoc);
333
334
335 FieldDoc[] fieldArray = interfaceDoc.fields();
336 for (int i = 0; i < fieldArray.length; ++i) {
337 fieldXML(fieldArray[i]);
338 }
339
340
341 MethodDoc[] methodArray = interfaceDoc.methods();
342 for (int i = 0; i < methodArray.length; ++i) {
343 methodXML(methodArray[i]);
344 }
345 cm.endElement(xmlns, localName, "interface");
346 }
347
348 /**
349 * Generates doc for element "extends_interface"
350 * <xmp><!ELEMENT extends_interface (interfaceref+)></xmp>
351 */
352 private void extends_interfaceXML(ClassDoc interfaceDoc) throws SAXException {
353 ClassDoc[] interfaceArray = interfaceDoc.interfaces();
354 if (interfaceArray.length > 0) {
355 cm.startElement(xmlns, localName, "extends_interface", emptyAtts);
356 for (int i = 0; i < interfaceArray.length; ++i) {
357 createRefXML("interfaceref", interfaceArray[i].qualifiedName());
358 }
359 cm.endElement(xmlns, localName, "extends_interface");
360 }
361 }
362
363 /**
364 * Generates doc for element "implements"
365 * <xmp><!ELEMENT implements (interfaceref+)></xmp>
366 */
367 private void implementsXML(ClassDoc classDoc) throws SAXException {
368 ClassDoc[] interfaceArray = classDoc.interfaces();
369 if (interfaceArray.length > 0) {
370 cm.startElement(xmlns, localName, "implements", emptyAtts);
371 for (int i = 0; i < interfaceArray.length; ++i) {
372 createRefXML("interfaceref", interfaceArray[i].qualifiedName());
373 }
374 cm.endElement(xmlns, localName, "implements");
375 }
376 if (classDoc.superclass() != null) {
377 implementsXML(classDoc.superclass());
378 }
379 }
380
381 /**
382 * Generates doc for element "throws"
383 * <xmp><!ELEMENT throws (classref)+></xmp>
384 */
385 private void throwsXML(ExecutableMemberDoc member) throws SAXException {
386 ThrowsTag[] tagArray = member.throwsTags();
387 if(tagArray.length > 0) {
388 cm.startElement(xmlns, localName, "throws", emptyAtts);
389 for (int i = 0; i < tagArray.length; ++i) {
390 ClassDoc exceptionClass = tagArray[i].exception();
391 String name = null;
392 if (exceptionClass == null) {
393 name = tagArray[i].exceptionName();
394 } else {
395 name = tagArray[i].exception().qualifiedName();
396 }
397 createRefXML("classref", name);
398 }
399 cm.endElement(xmlns, localName, "throws");
400 }
401 }
402
403 /**
404 * Generates doc for following elements
405 * <xmp> <!ELEMENT classref EMPTY>
406 * <!ATTLIST classref %name;>
407 * <!ELEMENT interfaceref EMPTY>
408 * <!ATTLIST interfaceref %name;>
409 * <!ELEMENT methodref EMPTY>
410 * <!ATTLIST methodref %name;>
411 * <!ELEMENT packageref EMPTY>
412 * <!ATTLIST packageref %name;></xmp>
413 */
414 private void createRefXML(String elementName, String nameValue) throws SAXException {
415 AttributesImpl atts = new AttributesImpl();
416 atts.addAttribute(xmlns, localName, "name", "String", nameValue);
417 cm.startElement(xmlns, localName, elementName, atts);
418 cm.endElement(xmlns, localName, elementName);
419 }
420
421 /**
422 * Generates doc for "(classref|interfaceref|primitive)" sub-element
423 */
424 private void createTypeRef(Type type) throws SAXException {
425 String qualifiedName = type.qualifiedTypeName();
426 ClassDoc fieldType = type.asClassDoc();
427 if (fieldType == null) {
428
429 AttributesImpl subElmAtts = new AttributesImpl();
430 subElmAtts.addAttribute(xmlns, localName, "type", "String", qualifiedName);
431 cm.startElement(xmlns, localName, "primitive", subElmAtts);
432 cm.endElement(xmlns, localName, "primitive");
433 } else if (fieldType.isInterface()) {
434
435 createRefXML("interfaceref", qualifiedName);
436 } else {
437
438 createRefXML("classref", qualifiedName);
439 }
440 }
441
442 /**
443 * Generates doc for element "field"
444 * <xmp> <!ELEMENT field (doc?, (classref | interfaceref | primitive))>
445 * <!ATTLIST field
446 * %name;
447 * %access;
448 * %dimension;
449 * %synthetic;
450 * %static;
451 * %final;
452 * %transient;
453 * %volatile;></xmp>
454 */
455 private void fieldXML(FieldDoc field) throws SAXException {
456 AttributesImpl atts = new AttributesImpl();
457
458 atts.addAttribute(xmlns, localName, "name", "String", field.name());
459
460 String access = "package";
461 if (field.isPrivate()) {
462 access = "private";
463 } else if (field.isProtected()) {
464 access = "protected";
465 } else if (field.isPublic()) {
466 access = "public";
467 }
468 atts.addAttribute(xmlns, localName, "access", "String", access);
469
470 atts.addAttribute(xmlns, localName, "dimension", "String", field.type().dimension());
471 atts.addAttribute(xmlns, localName, "synthetic", "String", "" + field.isSynthetic());
472 atts.addAttribute(xmlns, localName, "static", "String", "" + field.isStatic());
473 atts.addAttribute(xmlns, localName, "final", "String", "" + field.isFinal());
474 atts.addAttribute(xmlns, localName, "transient", "String", "" + field.isTransient());
475 atts.addAttribute(xmlns, localName, "volatile", "String", "" + field.isVolatile());
476 cm.startElement(xmlns, localName, "field", atts);
477
478
479 docXML(field);
480
481
482 createTypeRef(field.type());
483
484 cm.endElement(xmlns, localName, "field");
485 }
486
487 /**
488 * Generates doc for element "constructor"
489 * <xmp><!ELEMENT constructor (doc?, parameter*, throws*)>
490 * <!ATTLIST constructor
491 * %name;
492 * %access;
493 * %synthetic;></xmp>
494 */
495 private void constructorXML(ConstructorDoc constrDoc) throws SAXException {
496 AttributesImpl atts = new AttributesImpl();
497 atts.addAttribute(xmlns, localName, "name", "String", constrDoc.qualifiedName());
498 String access = "package";
499 if (constrDoc.isPrivate()) {
500 access = "private";
501 } else if (constrDoc.isProtected()) {
502 access = "protected";
503 } else if (constrDoc.isPublic()) {
504 access = "public";
505 }
506 atts.addAttribute(xmlns, localName, "access", "String", access);
507 atts.addAttribute(xmlns, localName, "synthetic", "String", "" + constrDoc.isSynthetic());
508 cm.startElement(xmlns, localName, "constructor", atts);
509
510
511 docXML(constrDoc);
512
513
514 Parameter[] parameterArray = constrDoc.parameters();
515 for (int i = 0; i < parameterArray.length; ++i) {
516 parameterXML(parameterArray[i]);
517 }
518
519
520 throwsXML(constrDoc);
521
522 cm.endElement(xmlns, localName, "constructor");
523 }
524
525 /**
526 * Generates doc for element "method"
527 * <xmp> <!ELEMENT method (doc?, returns, parameter*, throws*)>
528 * <!ATTLIST method
529 * %name;
530 * %access;
531 * %extensibility;
532 * %native;
533 * %synthetic;
534 * %static;
535 * %synchronized;></xmp>
536 */
537 private void methodXML(MethodDoc methodDoc) throws SAXException {
538 AttributesImpl atts = new AttributesImpl();
539
540 atts.addAttribute(xmlns, localName, "name", "String", methodDoc.name());
541
542 String access = "package";
543 if (methodDoc.isPrivate()) {
544 access = "private";
545 } else if (methodDoc.isProtected()) {
546 access = "protected";
547 } else if (methodDoc.isPublic()) {
548 access = "public";
549 }
550 atts.addAttribute(xmlns, localName, "access", "String", access);
551
552 String extensibility = "default";
553 if (methodDoc.isAbstract()) {
554 extensibility = "abstract";
555 } else if (methodDoc.isFinal()) {
556 extensibility = "final";
557 }
558 atts.addAttribute(xmlns, localName, "extensiblity", "String", extensibility);
559
560 atts.addAttribute(xmlns, localName, "native", "String", ""+ methodDoc.isNative());
561 atts.addAttribute(xmlns, localName, "synthetic", "String", "" + methodDoc.isSynthetic());
562 atts.addAttribute(xmlns, localName, "static", "String", "" + methodDoc.isStatic());
563 atts.addAttribute(xmlns, localName, "synchronized", "String", ""+ methodDoc.isSynchronized());
564 cm.startElement(xmlns, localName, "method", atts);
565
566
567 docXML(methodDoc);
568
569
570 returnsXML(methodDoc.returnType());
571
572
573 Parameter[] parameterArray = methodDoc.parameters();
574 for (int i = 0; i < parameterArray.length; ++i) {
575 parameterXML(parameterArray[i]);
576 }
577
578
579 throwsXML(methodDoc);
580
581 cm.endElement(xmlns, localName, "method");
582 }
583
584 /**
585 * Generates doc for element "returns"
586 * <xmp> <!ELEMENT returns (classref | interfaceref | primitive)>
587 * <!ATTLIST returns %dimension;></xmp>
588 */
589 private void returnsXML(Type type) throws SAXException {
590 AttributesImpl atts = new AttributesImpl();
591 atts.addAttribute(xmlns, localName, "dimension", "String", type.dimension());
592 cm.startElement(xmlns, localName, "returns", atts);
593
594
595 createTypeRef(type);
596
597 cm.endElement(xmlns, localName, "returns");
598 }
599
600 /**
601 * Generates doc for element "parameter"
602 * <xmp> <!ELEMENT parameter (classref | interfaceref | primitive)>
603 * <!ATTLIST parameter
604 * %name;
605 * %final;
606 * %dimension;></xmp>
607 */
608 private void parameterXML(Parameter parameter) throws SAXException {
609 AttributesImpl atts = new AttributesImpl();
610 atts.addAttribute(xmlns, localName, "name", "String", parameter.name());
611 boolean isFinal = false;
612 Type type = parameter.type();
613 if (type.asClassDoc() == null) {
614 isFinal = true;
615 }
616 atts.addAttribute(xmlns, localName, "final", "String", ""+ "" + isFinal);
617 atts.addAttribute(xmlns, localName, "dimension", "String", parameter.type().dimension());
618 cm.startElement(xmlns, localName, "parameter", atts);
619
620
621 createTypeRef(parameter.type());
622
623 cm.endElement(xmlns, localName,"parameter");
624 }
625
626 /**
627 * Generates doc for element "doc"
628 * <xmp><!ELEMENT doc (#PCDATA |
629 * linktag |
630 * authortag |
631 * versiontag |
632 * paramtag |
633 * returntag |
634 * exceptiontag |
635 * throwstag |
636 * seetag |
637 * sincetag |
638 * deprecatedtag |
639 * serialtag |
640 * serialfieldtag |
641 * serialdatatag)*></xmp>
642 */
643 private void docXML(Doc doc) throws SAXException {
644 String commentText = "";
645 boolean createDoc = false;
646 commentText = doc.commentText();
647 if (! commentText.equals("")) {
648 createDoc = true;
649 }
650 Tag[] tags = doc.tags();
651 if (tags.length > 0) {
652 createDoc = true;
653 }
654 if (createDoc) {
655 cm.startElement(xmlns, localName, "doc", emptyAtts);
656 if (! commentText.equals("")) {
657 cm.characters(commentText.toCharArray(), 0, commentText.length());
658 }
659 for (int i = 0; i < tags.length; ++i) {
660 tagXML(tags[i]);
661 }
662 cm.endElement(xmlns, localName, "doc");
663 }
664 }
665
666 /**
667 * Generates doc for all tag elements.
668 */
669 private void tagXML(Tag tag) throws SAXException {
670 String name = tag.name().substring(1) + "tag";
671 if (! tag.text().equals("")) {
672 cm.startElement(xmlns, localName, name, emptyAtts);
673 cm.characters(tag.text().toCharArray(), 0, tag.text().length());
674 cm.endElement(xmlns, localName, name);
675 }
676 }
677
678 public static boolean start(RootDoc root) {
679 try {
680 new XMLDoclet(root);
681 return true;
682 } catch (Exception e) {
683 e.printStackTrace();
684 System.exit(1);
685 return false;
686 }
687 }
688
689 private void readOptions(RootDoc root)
690 {
691 String[][] options = root.options();
692 for (int i = 0; i < options.length; i++)
693 {
694 String[] opt = options[i];
695 if (opt[0].equals("-d"))
696 {
697 targetFileName = opt[1] + "/javadoc.xml";
698 }
699 if (opt[0].equals("-encoding"))
700 {
701 encodingFormat = opt[1];
702 }
703 }
704 }
705
706 public static int optionLength(String option)
707 {
708 if(option.equals("-d"))
709 {
710 return 2;
711 }
712 if(option.equals("-encoding"))
713 {
714 return 2;
715 }
716 return 0;
717 }
718
719 public static boolean validOptions(String options[][],
720 DocErrorReporter reporter)
721 {
722 boolean foundEncodingOption = false;
723 boolean foundDirOption = false;
724 for (int i = 0; i < options.length; i++)
725 {
726 String[] opt = options[i];
727 if (opt[0].equals("-d"))
728 {
729 if (foundDirOption)
730 {
731 reporter.printError("Only one -d option allowed.");
732 return false;
733 }
734 else
735 {
736 foundDirOption = true;
737 }
738 }
739 if (opt[0].equals("-encoding"))
740 {
741 if (foundEncodingOption)
742 {
743 reporter.printError("Only one -encoding option allowed.");
744 return false;
745 }
746 else
747 {
748 foundEncodingOption = true;
749 }
750 }
751 }
752 if (!foundDirOption)
753 {
754 reporter.printError("Usage: javadoc -d <directory> -doclet TagXMLDoclet ...");
755 return false;
756 }
757 return true;
758 }
759 }