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.beans.Introspector;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.StringReader;
25
26 import org.cyberneko.html.parsers.SAXParser;
27 import org.dom4j.io.OutputFormat;
28 import org.dom4j.io.XMLWriter;
29 import org.xml.sax.Attributes;
30 import org.xml.sax.ContentHandler;
31 import org.xml.sax.InputSource;
32 import org.xml.sax.SAXException;
33 import org.xml.sax.helpers.AttributesImpl;
34 import org.xml.sax.helpers.DefaultHandler;
35
36 import com.sun.javadoc.ClassDoc;
37 import com.sun.javadoc.Doc;
38 import com.sun.javadoc.DocErrorReporter;
39 import com.sun.javadoc.Doclet;
40 import com.sun.javadoc.MethodDoc;
41 import com.sun.javadoc.PackageDoc;
42 import com.sun.javadoc.Parameter;
43 import com.sun.javadoc.RootDoc;
44 import com.sun.javadoc.SeeTag;
45 import com.sun.javadoc.Tag;
46
47 /**
48 * Main Doclet class to generate Tag Library ML.
49 *
50 * @author <a href="mailto:gopi@aztecsoft.com">Gopinath M.R.</a>
51 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
52 * @author Rodney Waldhoff
53 */
54
55
56
57 public class TagXMLDoclet extends Doclet {
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 TagXMLDoclet (RootDoc root) throws Exception
67 {
68 readOptions(root);
69 File targetFile = new File(targetFileName);
70 targetFile.getParentFile().mkdirs();
71 FileOutputStream writer = new FileOutputStream(targetFileName);
72 OutputFormat format = OutputFormat.createPrettyPrint();
73 format.setEncoding(encodingFormat);
74 XMLWriter xmlWriter = new XMLWriter(writer, format);
75 try {
76 cm = xmlWriter;
77 cm.startDocument();
78 javadocXML(root);
79 cm.endDocument();
80 xmlWriter.close();
81 }
82 catch (IOException e) {
83 xmlWriter.close();
84 throw e;
85 }
86 }
87
88 /**
89 * Generates the xml for the tag libraries
90 */
91 private void javadocXML(RootDoc root) throws SAXException {
92 cm.startElement(xmlns, localName, "tags", emptyAtts);
93 PackageDoc[] packageArray = root.specifiedPackages();
94
95
96 for (int i = 0; i < packageArray.length; ++i) {
97 packageXML(packageArray[i]);
98 }
99
100 cm.endElement(xmlns, localName, "tags");
101 }
102
103 /**
104 * Generates doc for a tag library
105 */
106 private void packageXML(PackageDoc packageDoc) throws SAXException {
107
108 System.out.println( "processing package: " + packageDoc.name());
109
110 ClassDoc[] classArray = packageDoc.ordinaryClasses();
111
112
113 boolean foundTag = false;
114 for (int i = 0; i < classArray.length; ++i) {
115 ClassDoc classDoc = classArray[i];
116 if ( isTag( classArray[i] ) ) {
117 foundTag = true;
118 break;
119 }
120 }
121 if ( ! foundTag ) {
122 System.out.println( "no tags found");
123 return;
124 }
125
126 AttributesImpl atts = new AttributesImpl();
127 atts.addAttribute(xmlns, localName, "name", "String", packageDoc.name());
128
129 String name = packageDoc.name();
130 int idx = name.lastIndexOf('.');
131 if ( idx > 0 ) {
132 name = name.substring(idx+1);
133 }
134 atts.addAttribute(xmlns, localName, "prefix", "String", name);
135
136 String uri = "jelly:" + name;
137
138 atts.addAttribute(xmlns, localName, "uri", "String", uri );
139 cm.startElement(xmlns, localName, "library", atts);
140
141
142 docXML(packageDoc);
143
144
145 for (int i = 0; i < classArray.length; ++i) {
146 if ( isTag( classArray[i] ) ) {
147 tagXML(classArray[i]);
148 }
149 }
150 cm.endElement(xmlns, localName, "library");
151 }
152
153 /**
154 * @return true if this class is a Jelly Tag
155 */
156 private boolean isTag(ClassDoc classDoc) {
157 ClassDoc[] interfaceArray = classDoc.interfaces();
158 for (int i = 0; i < interfaceArray.length; ++i) {
159 String name = interfaceArray[i].qualifiedName();
160 if ("org.apache.commons.jelly.Tag".equals(name)) {
161 return true;
162 }
163 }
164 ClassDoc base = classDoc.superclass();
165 if ( base != null ) {
166 return isTag(base);
167 }
168 return false;
169 }
170
171 /**
172 * Generates doc for a tag
173 */
174 private void tagXML(ClassDoc classDoc) throws SAXException {
175 if (classDoc.isAbstract()) {
176 return;
177 }
178
179 AttributesImpl atts = new AttributesImpl();
180 atts.addAttribute(xmlns, localName, "className", "String", classDoc.name());
181 String name = classDoc.name();
182 if ( name.endsWith( "Tag" ) ) {
183 name = name.substring(0, name.length() - 3 );
184 }
185 name = Introspector.decapitalize(name);
186
187
188 System.out.println( "processing tag: " + name);
189
190 atts.addAttribute(xmlns, localName, "name", "String", name);
191 cm.startElement(xmlns, localName, "tag", atts);
192
193
194 docXML(classDoc);
195
196
197 propertiesXML(classDoc);
198
199
200 cm.endElement(xmlns, localName, "tag");
201 }
202
203 /**
204 * Generates doc for a tag property
205 */
206 private void propertiesXML(ClassDoc classDoc) throws SAXException {
207 MethodDoc[] methodArray = classDoc.methods();
208 for (int i = 0; i < methodArray.length; ++i) {
209 propertyXML(methodArray[i]);
210 }
211 ClassDoc base = classDoc.superclass();
212 if ( base != null ) {
213 propertiesXML( base );
214 }
215 }
216
217
218 /**
219 * Generates doc for a tag property
220 */
221 private void propertyXML(MethodDoc methodDoc) throws SAXException {
222 if ( ! methodDoc.isPublic() || methodDoc.isStatic() ) {
223 return;
224 }
225 String name = methodDoc.name();
226 if ( ! name.startsWith( "set" ) ) {
227 return;
228 }
229 Parameter[] parameterArray = methodDoc.parameters();
230 if ( parameterArray == null || parameterArray.length != 1 ) {
231 return;
232 }
233 Parameter parameter = parameterArray[0];
234
235 name = name.substring(3);
236 name = Introspector.decapitalize(name);
237
238 if ( name.equals( "body") || name.equals( "context" ) || name.equals( "parent" ) ) {
239 return;
240 }
241 AttributesImpl atts = new AttributesImpl();
242 atts.addAttribute(xmlns, localName, "name", "String", name);
243 atts.addAttribute(xmlns, localName, "type", "String", parameter.typeName());
244
245 cm.startElement(xmlns, localName, "attribute", atts);
246
247
248
249
250 docXML(methodDoc);
251
252 cm.endElement(xmlns, localName, "attribute");
253 }
254
255 /**
256 * Generates doc for element "doc"
257 */
258 private void docXML(Doc doc) throws SAXException {
259 cm.startElement(xmlns, localName, "doc", emptyAtts);
260
261 {
262 Tag[] tags = doc.inlineTags();
263 for(int i=0;i<tags.length;i++) {
264
265 if(tags[i] instanceof SeeTag) {
266 String label = ((SeeTag)tags[i]).label();
267
268 if(null == label || "".equals(label)) {
269 StringBuffer buf = new StringBuffer();
270 String className = ((SeeTag)tags[i]).referencedClassName();
271 if("".equals(className)) { className = null; }
272 String memberName = ((SeeTag)tags[i]).referencedMemberName();
273 if("".equals(memberName)) { memberName = null; }
274 if(null != className) {
275 buf.append(className);
276 if(null != memberName) {
277 buf.append(".");
278 }
279 }
280 if(null != memberName) {
281 buf.append(memberName);
282 }
283 label = buf.toString();
284 }
285 parseHTML(label);
286 } else {
287 parseHTML(tags[i].text());
288 }
289 }
290 }
291
292 {
293 Tag[] tags = doc.tags();
294 for (int i = 0; i < tags.length; ++i) {
295 javadocTagXML(tags[i]);
296 }
297 }
298 cm.endElement(xmlns, localName, "doc");
299 }
300
301 protected void parseHTML(String text) throws SAXException {
302 SAXParser parser = new SAXParser();
303 parser.setProperty(
304 "http://cyberneko.org/html/properties/names/elems",
305 "lower"
306 );
307 parser.setProperty(
308 "http://cyberneko.org/html/properties/names/attrs",
309 "lower"
310 );
311 parser.setContentHandler(
312 new DefaultHandler() {
313 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
314 if ( validDocElementName( localName ) ) {
315 cm.startElement(namespaceURI, localName, qName, atts);
316 }
317 }
318 public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
319 if ( validDocElementName( localName ) ) {
320 cm.endElement(namespaceURI, localName, qName);
321 }
322 }
323 public void characters(char[] ch, int start, int length) throws SAXException {
324 cm.characters(ch, start, length);
325 }
326 }
327 );
328 try {
329 parser.parse( new InputSource(new StringReader( text )) );
330 }
331 catch (IOException e) {
332 System.err.println( "This should never happen!" + e );
333 }
334 }
335
336 /**
337 * @return true if the given name is a valid HTML markup element.
338 */
339 protected boolean validDocElementName(String name) {
340 return ! name.equalsIgnoreCase( "html" ) && ! name.equalsIgnoreCase( "body" );
341 }
342
343 /**
344 * Generates doc for all tag elements.
345 */
346 private void javadocTagXML(Tag tag) throws SAXException {
347 String name = tag.name().substring(1) + "tag";
348 if (! tag.text().equals("")) {
349 cm.startElement(xmlns, localName, name, emptyAtts);
350 cm.characters(tag.text().toCharArray(), 0, tag.text().length());
351 cm.endElement(xmlns, localName, name);
352 }
353 }
354
355 public static boolean start(RootDoc root) {
356 try {
357 new TagXMLDoclet(root);
358 return true;
359 } catch (Exception e) {
360 e.printStackTrace();
361 System.exit(1);
362 return false;
363 }
364 }
365
366 private void readOptions(RootDoc root)
367 {
368 String[][] options = root.options();
369 for (int i = 0; i < options.length; i++)
370 {
371 String[] opt = options[i];
372 if (opt[0].equals("-d"))
373 {
374 targetFileName = opt[1] + "/taglib.xml";
375 }
376 if (opt[0].equals("-encoding"))
377 {
378 encodingFormat = opt[1];
379 }
380 }
381 }
382
383 public static int optionLength(String option)
384 {
385 if(option.equals("-d"))
386 {
387 return 2;
388 }
389 if(option.equals("-encoding"))
390 {
391 return 2;
392 }
393 return 0;
394 }
395
396 public static boolean validOptions(String options[][],
397 DocErrorReporter reporter)
398 {
399 boolean foundEncodingOption = false;
400 boolean foundDirOption = false;
401 for (int i = 0; i < options.length; i++)
402 {
403 String[] opt = options[i];
404 if (opt[0].equals("-d"))
405 {
406 if (foundDirOption)
407 {
408 reporter.printError("Only one -d option allowed.");
409 return false;
410 }
411 else
412 {
413 foundDirOption = true;
414 }
415 }
416 if (opt[0].equals("-encoding"))
417 {
418 if (foundEncodingOption)
419 {
420 reporter.printError("Only one -encoding option allowed.");
421 return false;
422 }
423 else
424 {
425 foundEncodingOption = true;
426 }
427 }
428 }
429 if (!foundDirOption)
430 {
431 reporter.printError("Usage: javadoc -d <directory> -doclet TagXMLDoclet ...");
432 return false;
433 }
434 return true;
435 }
436 }