1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package com.sun.tlddoc;
33
34 import java.io.CharArrayReader;
35 import java.io.File;
36 import java.io.FileInputStream;
37 import java.io.FileNotFoundException;
38 import java.io.FileOutputStream;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.util.ArrayList;
43 import java.util.Enumeration;
44 import java.util.HashMap;
45 import java.util.Iterator;
46 import java.util.Map;
47 import java.util.jar.JarEntry;
48 import java.util.jar.JarFile;
49 import java.util.jar.JarInputStream;
50
51 import javax.xml.parsers.DocumentBuilder;
52 import javax.xml.parsers.DocumentBuilderFactory;
53 import javax.xml.parsers.ParserConfigurationException;
54 import javax.xml.transform.Transformer;
55 import javax.xml.transform.TransformerConfigurationException;
56 import javax.xml.transform.TransformerException;
57 import javax.xml.transform.TransformerFactory;
58 import javax.xml.transform.dom.DOMResult;
59 import javax.xml.transform.dom.DOMSource;
60 import javax.xml.transform.stream.StreamResult;
61 import javax.xml.transform.stream.StreamSource;
62
63 import org.apache.xalan.processor.TransformerFactoryImpl;
64 import org.w3c.dom.Document;
65 import org.w3c.dom.Element;
66 import org.w3c.dom.Node;
67 import org.w3c.dom.NodeList;
68 import org.xml.sax.EntityResolver;
69 import org.xml.sax.InputSource;
70 import org.xml.sax.SAXException;
71
72 import com.sun.tlddoc.tagfileparser.Attribute;
73 import com.sun.tlddoc.tagfileparser.Directive;
74 import com.sun.tlddoc.tagfileparser.ParseException;
75 import com.sun.tlddoc.tagfileparser.TagFile;
76 import com.sun.tlddoc.tagfileparser.TokenMgrError;
77
78
79 /***
80 * TLDDoc Generator. Takes a set of TLD files and generates a set of javadoc-style HTML pages that describe the various
81 * components of the input tag libraries.
82 * @author Mark Roth
83 */
84 public class TLDDocGenerator
85 {
86
87 /***
88 * The set of tag libraries we are parsing. Each element is a TagLibrary instance.
89 */
90 private ArrayList tagLibraries = new ArrayList();
91
92 /***
93 * The directory containing the stylesheets, or null if the default stylesheets are to be used.
94 */
95 private File xsltDirectory = null;
96
97 /*** The output directory for generated files */
98 private File outputDirectory = new File("out");
99
100 /*** The browser window title for the documentation */
101 private String windowTitle = Constants.DEFAULT_WINDOW_TITLE;
102
103 /*** The title for the TLD index (first) page. */
104 private String docTitle = Constants.DEFAULT_DOC_TITLE;
105
106 /*** True if no stdout is to be produced during generation */
107 private boolean quiet;
108
109 /*** The summary TLD document, used as input into XSLT */
110 private Document summaryTLD;
111
112 /*** Path to tlddoc resources */
113 private static final String RESOURCE_PATH = "/com/sun/tlddoc/resources";
114
115 /***
116 * Helps uniquely generate subsitutute prefixes in the case of missing or duplicate short-names
117 */
118 private int substitutePrefix = 1;
119
120 /***
121 * Creates a new TLDDocGenerator.
122 */
123 public TLDDocGenerator()
124 {
125 }
126
127 /***
128 * Adds the given Tag Library to the list of Tag Libraries to generate documentation for.
129 * @param tagLibrary The tag library to add.
130 */
131 public void addTagLibrary(TagLibrary tagLibrary)
132 {
133 tagLibraries.add(tagLibrary);
134 }
135
136 /***
137 * Adds the given individual TLD file
138 * @param tld The TLD file to add
139 */
140 public void addTLD(File tld)
141 {
142 addTagLibrary(new TLDFileTagLibrary(tld));
143 }
144
145 /***
146 * Adds all the tag libraries found in the given web application.
147 * @param path The path to the root of the web application.
148 */
149 public void addWebApp(File path)
150 {
151 File webinf = new File(path, "WEB-INF");
152
153
154 addWebAppTLDsIn(webinf);
155
156
157
158 addWebAppJARsIn(new File(webinf, "lib"));
159
160
161 addWebAppTagDirsIn(new File(webinf, "tags"));
162 }
163
164 /***
165 * Adds all TLD files under the given directory, recursively.
166 * @param path The path to search (recursively) for TLDs in.
167 */
168 private void addWebAppTLDsIn(File path)
169 {
170 File[] files = path.listFiles();
171 for (int i = 0; i < files.length; i++)
172 {
173 if (files[i].isDirectory())
174 {
175 addWebAppTLDsIn(files[i]);
176 }
177 else if (files[i].getName().toLowerCase().endsWith(".tld"))
178 {
179 addTLD(files[i]);
180 }
181 }
182 }
183
184 /***
185 * Adds all TLD files under the given directory, recursively.
186 * @param war The WAR file to search
187 * @param path The path to search (recursively) for TLDs in.
188 */
189 private void addWARTLDsIn(File war, String path) throws IOException
190 {
191 JarFile warFile = new JarFile(war);
192
193 Enumeration entries = warFile.entries();
194 while (entries.hasMoreElements())
195 {
196 JarEntry jarEntry = (JarEntry) entries.nextElement();
197 String entryName = jarEntry.getName();
198 if (entryName.startsWith(path) && entryName.toLowerCase().endsWith(".tld"))
199 {
200 addTagLibrary(new JARTLDFileTagLibrary(war, jarEntry.getName()));
201 }
202 }
203
204 warFile.close();
205 }
206
207 /***
208 * Adds all JAR files under the given directory, recursively.
209 * @param path The path to search (recursively) for JARs in.
210 */
211 private void addWebAppJARsIn(File path)
212 {
213 File[] files = path.listFiles();
214 for (int i = 0; i < files.length; i++)
215 {
216 if (files[i].isDirectory())
217 {
218 addWebAppJARsIn(files[i]);
219 }
220 else if (files[i].getName().toLowerCase().endsWith(".jar"))
221 {
222 addJAR(files[i]);
223 }
224 }
225 }
226
227 /***
228 * Adds all JAR files under the given directory, recursively.
229 * @param war The WAR file to search
230 * @param path The path to search (recursively) for JARs in.
231 */
232 private void addWARJARsIn(File war, String path) throws IOException
233 {
234 JarFile warFile = new JarFile(war);
235
236 Enumeration entries = warFile.entries();
237 while (entries.hasMoreElements())
238 {
239 JarEntry warEntry = (JarEntry) entries.nextElement();
240 String entryName = warEntry.getName();
241 if (entryName.startsWith(path) && entryName.toLowerCase().endsWith(".jar"))
242 {
243
244
245 try
246 {
247
248 JarInputStream in = new JarInputStream(warFile.getInputStream(warEntry));
249
250 JarEntry jarEntry;
251 while ((jarEntry = in.getNextJarEntry()) != null)
252 {
253 if (jarEntry.getName().toLowerCase().endsWith(".tld"))
254 {
255 addTagLibrary(new WARJARTLDFileTagLibrary(war, entryName, jarEntry.getName()));
256 }
257 }
258 in.close();
259 }
260 catch (IOException e)
261 {
262 println("WARNING: Could not access one or more "
263 + "entries in "
264 + war.getAbsolutePath()
265 + " entry "
266 + entryName
267 + ". Skipping JAR. Reason: "
268 + e.getMessage());
269 }
270 }
271 }
272
273 warFile.close();
274 }
275
276 /***
277 * Adds all implicit tag libraries under the given directory, recursively.
278 * @param path The path to search (recursively) for tag file directories in
279 */
280 private void addWebAppTagDirsIn(File path)
281 {
282 if (path.exists() && path.isDirectory())
283 {
284 addTagDir(path);
285
286 File[] files = path.listFiles();
287 if (files != null)
288 {
289 for (int i = 0; i < files.length; i++)
290 {
291 if (files[i].isDirectory())
292 {
293 addWebAppTagDirsIn(files[i]);
294 }
295 }
296 }
297 }
298 }
299
300 /***
301 * Adds all implicit tag libraries under the given directory, recursively.
302 * @param war The WAR file to search
303 * @param path The path to search (recursively) for tag file directories in
304 */
305 private void addWARTagDirsIn(File war, String path) throws IOException
306 {
307 JarFile warFile = new JarFile(war);
308
309 Enumeration entries = warFile.entries();
310 while (entries.hasMoreElements())
311 {
312 JarEntry entry = (JarEntry) entries.nextElement();
313 if (entry.getName().startsWith(path) && entry.isDirectory())
314 {
315 addTagLibrary(new WARTagDirImplicitTagLibrary(war, entry.getName()));
316 }
317 }
318
319 warFile.close();
320 }
321
322 /***
323 * Adds all the tag libraries found in the given JAR.
324 * @param jar The JAR file to add.
325 */
326 public void addJAR(File jar)
327 {
328 try
329 {
330
331 JarFile jarFile = new JarFile(jar);
332 Enumeration entries = jarFile.entries();
333 while (entries.hasMoreElements())
334 {
335 JarEntry jarEntry = (JarEntry) entries.nextElement();
336 if (jarEntry.getName().toLowerCase().endsWith(".tld"))
337 {
338 addTagLibrary(new JARTLDFileTagLibrary(jar, jarEntry.getName()));
339 }
340 }
341 jarFile.close();
342 }
343 catch (IOException e)
344 {
345 println("WARNING: Could not access one or more entries in "
346 + jar.getAbsolutePath()
347 + ". Skipping JAR. Reason: "
348 + e.getMessage());
349 }
350 }
351
352 /***
353 * Adds all the tag libraries found in the given web application packaged as a WAR file.
354 * @param war The war containing the web application
355 */
356 public void addWAR(File path)
357 {
358 try
359 {
360
361 addWARTLDsIn(path, "WEB-INF/");
362
363
364
365 addWARJARsIn(path, "WEB-INF/lib/");
366
367
368 addWARTagDirsIn(path, "WEB-INF/tags/");
369 }
370 catch (IOException e)
371 {
372 println("WARNING: Could not access one or more entries in "
373 + path.getAbsolutePath()
374 + ". Skipping WAR. Reason: "
375 + e.getMessage());
376 }
377 }
378
379 /***
380 * Adds the given directory of tag files.
381 * @param tld The tag directory to add
382 */
383 public void addTagDir(File tagdir)
384 {
385 addTagLibrary(new TagDirImplicitTagLibrary(tagdir));
386 }
387
388 /***
389 * Sets the directory from which to obtain the XSLT stylesheets. This allows the user to change the look and feel of
390 * the generated output. If not specified, the default stylesheets are used.
391 * @param dir The base directory for the stylesheets
392 */
393 public void setXSLTDirectory(File dir)
394 {
395 this.xsltDirectory = dir;
396 }
397
398 /***
399 * Sets the output directory for generated files. If not specified, defaults to "."
400 * @param dir The base directory for generated files.
401 */
402 public void setOutputDirectory(File dir)
403 {
404 this.outputDirectory = dir;
405 }
406
407 /***
408 * Sets the browser window title for the documentation
409 * @param title The browser window title
410 */
411 public void setWindowTitle(String title)
412 {
413 this.windowTitle = title;
414 }
415
416 /***
417 * Sets the title for the TLD index (first) page.
418 * @param title The title for the TLD index
419 */
420 public void setDocTitle(String title)
421 {
422 this.docTitle = title;
423 }
424
425 /***
426 * Sets quiet mode (produce no stdout during generation)
427 * @param quiet True if no output is to be produced, false otherwise.
428 */
429 public void setQuiet(boolean quiet)
430 {
431 this.quiet = quiet;
432 }
433
434 /***
435 * Returns true if the generator is in quiet mode or false if not.
436 */
437
438 /***
439 * Commences documentation generation.
440 */
441 public void generate() throws GeneratorException, TransformerException
442 {
443 try
444 {
445 this.outputDirectory.mkdirs();
446 copyStaticFiles();
447 createTLDSummaryDoc();
448 generateOverview();
449 generateTLDDetail();
450 outputSuccessMessage();
451 }
452 catch (IOException e)
453 {
454 throw new GeneratorException(e);
455 }
456 catch (SAXException e)
457 {
458 throw new GeneratorException(e);
459 }
460 catch (ParserConfigurationException e)
461 {
462 throw new GeneratorException(e);
463 }
464 catch (TransformerConfigurationException e)
465 {
466 throw new GeneratorException(e);
467 }
468 catch (TransformerException e)
469 {
470 throw new GeneratorException(e);
471 }
472 }
473
474
475
476 /***
477 * Copies all static files to target directory.
478 */
479 private void copyStaticFiles() throws IOException
480 {
481 copyResourceToFile(new File(this.outputDirectory, "stylesheet.css"), RESOURCE_PATH + "/stylesheet.css");
482 }
483
484 /***
485 * Creates a summary document, comprising all input TLDs. This document is later used as input into XSLT to generate
486 * all non-static output pages. Stores the result as a DOM tree in the summaryTLD attribute.
487 */
488 private void createTLDSummaryDoc() throws IOException, SAXException, ParserConfigurationException,
489 TransformerConfigurationException, TransformerException, GeneratorException
490 {
491 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
492 factory.setValidating(false);
493 factory.setNamespaceAware(true);
494 factory.setExpandEntityReferences(false);
495 DocumentBuilder documentBuilder = factory.newDocumentBuilder();
496 documentBuilder.setEntityResolver(new EntityResolver()
497 {
498
499 public InputSource resolveEntity(String publicId, String systemId)
500 {
501 return new InputSource(new CharArrayReader(new char[0]));
502 }
503 });
504 summaryTLD = documentBuilder.newDocument();
505
506
507 Element rootElement = summaryTLD.createElementNS(Constants.NS_J2EE, "tlds");
508 summaryTLD.appendChild(rootElement);
509
510 rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", Constants.NS_J2EE);
511
512
513 Element configElement = summaryTLD.createElementNS(Constants.NS_J2EE, "config");
514 rootElement.appendChild(configElement);
515
516 Element windowTitle = summaryTLD.createElementNS(Constants.NS_J2EE, "window-title");
517 windowTitle.appendChild(summaryTLD.createTextNode(this.windowTitle));
518 configElement.appendChild(windowTitle);
519
520 Element docTitle = summaryTLD.createElementNS(Constants.NS_J2EE, "doc-title");
521 docTitle.appendChild(summaryTLD.createTextNode(this.docTitle));
522 configElement.appendChild(docTitle);
523
524
525 Iterator iter = tagLibraries.iterator();
526 println("Loading and translating "
527 + tagLibraries.size()
528 + " Tag Librar"
529 + ((tagLibraries.size() == 1) ? "y" : "ies")
530 + "...");
531 while (iter.hasNext())
532 {
533 TagLibrary tagLibrary = (TagLibrary) iter.next();
534 Document doc = tagLibrary.getTLDDocument(documentBuilder);
535
536
537 doc = upgradeTLD(doc);
538
539
540
541 int numTags = doc.getDocumentElement().getElementsByTagNameNS("*", "tag").getLength()
542 + doc.getDocumentElement().getElementsByTagNameNS("*", "tag-file").getLength()
543 + doc.getDocumentElement().getElementsByTagNameNS("*", "validator").getLength()
544 + doc.getDocumentElement().getElementsByTagNameNS("*", "function").getLength();
545 if (numTags > 0)
546 {
547
548 populateTLD(tagLibrary, doc);
549
550 Element taglibNode = (Element) summaryTLD.importNode(doc.getDocumentElement(), true);
551 if (!taglibNode.getNamespaceURI().equals(Constants.NS_J2EE))
552 {
553 throw new GeneratorException("Error: "
554 + tagLibrary.getPathDescription()
555 + " does not have xmlns=\""
556 + Constants.NS_J2EE
557 + "\"");
558 }
559 if (!taglibNode.getLocalName().equals("taglib"))
560 {
561 throw new GeneratorException("Error: "
562 + tagLibrary.getPathDescription()
563 + " does not have <taglib> as root.");
564 }
565 rootElement.appendChild(taglibNode);
566 }
567 }
568
569
570 if (Constants.DEBUG_INPUT_DOCUMENT)
571 {
572
573
574
575 Transformer transformer = getTransformer(null);
576 transformer.transform(new DOMSource(summaryTLD), new StreamResult(System.out));
577 }
578 }
579
580 /***
581 * Maven 1.0x doesn't play well with jdk 1.5 tranformer... and maven 1.1x doesn't play well with the maven 1.0 hack :/
582 * @param source
583 * @return
584 * @throws TransformerConfigurationException
585 */
586 private Transformer getTransformer(StreamSource source) throws TransformerConfigurationException
587 {
588 Transformer transformer = null;
589 try
590 {
591 transformer = source == null ? TransformerFactory.newInstance().newTransformer() : TransformerFactory
592 .newInstance()
593 .newTransformer(source);
594 }
595 catch (Exception e)
596 {
597
598 }
599 if (transformer == null)
600 {
601 transformer = source == null ? new TransformerFactoryImpl().newTransformer() : new TransformerFactoryImpl()
602 .newTransformer(source);
603 }
604
605 return transformer;
606 }
607
608 /***
609 * Converts the given TLD to a JSP 2.0 TLD
610 */
611 private Document upgradeTLD(Document doc) throws TransformerConfigurationException, ParserConfigurationException,
612 TransformerException
613 {
614 Element root = doc.getDocumentElement();
615
616
617
618 if (root.getElementsByTagName("jspversion").getLength() > 0)
619 {
620
621 doc = convertTLD(doc, RESOURCE_PATH + "/tld1_1-tld1_2.xsl");
622 root = doc.getDocumentElement();
623 }
624
625
626
627 if (root.getElementsByTagName("jsp-version").getLength() > 0)
628 {
629
630 doc = convertTLD(doc, RESOURCE_PATH + "/tld1_2-tld2_0.xsl");
631 root = doc.getDocumentElement();
632 }
633
634
635 doc = convertTLD(doc, RESOURCE_PATH + "/tld2_0-tld2_0.xsl");
636
637
638 return doc;
639 }
640
641 /***
642 * Converts the given TLD using the given stylesheet
643 */
644 private Document convertTLD(Document doc, String stylesheet) throws TransformerConfigurationException,
645 ParserConfigurationException, TransformerException
646 {
647 InputStream xsl = getResourceAsStream(stylesheet);
648
649
650
651 Transformer transformer = getTransformer(new StreamSource(xsl));
652
653 Document result = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
654 transformer.transform(new DOMSource(doc), new DOMResult(result));
655 return result;
656 }
657
658 /***
659 * Populates the root element with any additional information needed before adding this to our tree.
660 * @param tagLibrary The tag library being populated
661 * @param doc The TLD DOM to populate.
662 */
663 private void populateTLD(TagLibrary tagLibrary, Document doc)
664 {
665 Element root = doc.getDocumentElement();
666
667 checkOrAddShortName(tagLibrary, doc, root);
668 checkOrAddAttributeType(tagLibrary, doc, root);
669 populateTagFileDetails(tagLibrary, doc, root);
670 }
671
672 /***
673 * Find all tag-file elements and populate them with the actual parsed meta information, found in the tag file's
674 * attributes:
675 * @param tagLibrary The tag library being populated
676 * @param doc The document we're populating
677 * @param root The root element of the TLD DOM being populated.
678 */
679 private void populateTagFileDetails(TagLibrary tagLibrary, Document doc, Element root)
680 {
681 NodeList tagFileNodes = root.getElementsByTagNameNS("*", "tag-file");
682 for (int i = 0; i < tagFileNodes.getLength(); i++)
683 {
684 Element tagFileNode = (Element) tagFileNodes.item(i);
685 String path = findElementValue(tagFileNode, "path");
686 if (path == null)
687 {
688 println("WARNING: "
689 + tagLibrary.getPathDescription()
690 + " contains a tag-file element with no path. Skipping.");
691 }
692 else
693 {
694 try
695 {
696 InputStream tagFileIn = tagLibrary.getResource(path);
697 if (tagFileIn == null)
698 {
699 println("WARNING: Could not find tag file '"
700 + path
701 + "' for tag library "
702 + tagLibrary.getPathDescription()
703 + ". Data will be incomplete for this tag.");
704 }
705 else
706 {
707 println("Parsing tag file: " + path);
708 TagFile tagFile = TagFile.parse(tagFileIn);
709 ArrayList directives = tagFile.getDirectives();
710 for (int j = 0; j < directives.size(); j++)
711 {
712 Directive directive = (Directive) directives.get(j);
713 String name = directive.getDirectiveName();
714 if (name.equals("tag"))
715 {
716 populateTagFileDetailsTagDirective(tagFileNode, doc, directive);
717 }
718 else if (name.equals("attribute"))
719 {
720 populateTagFileDetailsAttributeDirective(tagFileNode, doc, directive);
721 }
722 else if (name.equals("variable"))
723 {
724 populateTagFileDetailsVariableDirective(tagFileNode, doc, directive);
725 }
726 }
727
728 populateTagFileDetailsTagDefaults(tagFileNode, doc, path);
729 }
730 }
731 catch (IOException e)
732 {
733 println("WARNING: Could not read tag file '"
734 + path
735 + "' for tag library "
736 + tagLibrary.getPathDescription()
737 + ". Data will be incomplete for this tag."
738 + " Reason: "
739 + e.getMessage());
740 }
741 catch (ParseException e)
742 {
743 println("WARNING: Could not parse tag file '"
744 + path
745 + "' for tag library "
746 + tagLibrary.getPathDescription()
747 + ". Data will be incomplete for this tag."
748 + " Reason: "
749 + e.getMessage());
750 }
751 catch (TokenMgrError e)
752 {
753 println("WARNING: Could not parse tag file '"
754 + path
755 + "' for tag library "
756 + tagLibrary.getPathDescription()
757 + ". Data will be incomplete for this tag."
758 + " Reason: "
759 + e.getMessage());
760 }
761 }
762 }
763 }
764
765 /***
766 * Populates the given tag-file node with information from the given tag directive.
767 * @param tagFileNode The tag-file element
768 * @param doc The document we're populating
769 * @param directive The tag directive to process.
770 */
771 private void populateTagFileDetailsTagDirective(Element tagFileNode, Document doc, Directive directive)
772 {
773 Iterator attributes = directive.getAttributes();
774 while (attributes.hasNext())
775 {
776 Attribute attribute = (Attribute) attributes.next();
777 String name = attribute.getName();
778 String value = attribute.getValue();
779 Element element;
780 if (name.equals("display-name")
781 || name.equals("body-content")
782 || name.equals("dynamic-attributes")
783 || name.equals("description")
784 || name.equals("example"))
785 {
786 element = doc.createElementNS(Constants.NS_J2EE, name);
787 element.appendChild(doc.createTextNode(value));
788 tagFileNode.appendChild(element);
789 }
790 else if (name.equals("small-icon") || name.equals("large-icon"))
791 {
792 NodeList icons = tagFileNode.getElementsByTagNameNS("*", "icon");
793 Element icon;
794 if (icons.getLength() == 0)
795 {
796 icon = doc.createElementNS(Constants.NS_J2EE, "icon");
797 tagFileNode.appendChild(icon);
798 }
799 else
800 {
801 icon = (Element) icons.item(0);
802 }
803 element = doc.createElementNS(Constants.NS_J2EE, name);
804 element.appendChild(doc.createTextNode(value));
805 icon.appendChild(element);
806 }
807 }
808 }
809
810 /***
811 * Populates the given tag-file node with default values, for those values not specified via tag directives.
812 * @param tagFileNode The tag-file element
813 * @param doc The document we're populating
814 * @param path The path to the tag file
815 */
816 private void populateTagFileDetailsTagDefaults(Element tagFileNode, Document doc, String path)
817 {
818 String displayName = path.substring(path.lastIndexOf('/') + 1);
819 displayName = displayName.substring(0, displayName.lastIndexOf('.'));
820 populateDefault(doc, tagFileNode, "display-name", displayName);
821 populateDefault(doc, tagFileNode, "body-content", "scriptless");
822 populateDefault(doc, tagFileNode, "dynamic-attributes", "false");
823 }
824
825 /***
826 * Searches for the value of the given element. If no value is found, a default value is inserted.
827 * @param doc The document we're populating
828 * @param parent The element to examine
829 * @param tagName The name of the element we're looking for
830 * @param defaultValue The default value to insert, if not found.
831 */
832 private void populateDefault(Document doc, Element parent, String tagName, String defaultValue)
833 {
834 if (findElementValue(parent, tagName) == null)
835 {
836 Element element = doc.createElementNS(Constants.NS_J2EE, tagName);
837 element.appendChild(doc.createTextNode(defaultValue));
838 parent.appendChild(element);
839 }
840 }
841
842 /***
843 * Populates the given tag-file node with information from the given attribute directive.
844 * @param tagFileNode The tag-file element
845 * @param doc The document we're populating
846 * @param directive The attribute directive to process.
847 */
848 private void populateTagFileDetailsAttributeDirective(Element tagFileNode, Document doc, Directive directive)
849 {
850 Iterator attributes = directive.getAttributes();
851 Element attributeNode = doc.createElementNS(Constants.NS_J2EE, "attribute");
852 tagFileNode.appendChild(attributeNode);
853 while (attributes.hasNext())
854 {
855 Attribute attribute = (Attribute) attributes.next();
856 String name = attribute.getName();
857 String value = attribute.getValue();
858 Element element;
859 if (name.equals("name")
860 || name.equals("required")
861 || name.equals("fragment")
862 || name.equals("rtexprvalue")
863 || name.equals("type")
864 || name.equals("description"))
865 {
866 element = doc.createElementNS(Constants.NS_J2EE, name);
867 element.appendChild(doc.createTextNode(value));
868 attributeNode.appendChild(element);
869 }
870 }
871 populateDefault(doc, attributeNode, "required", "false");
872 populateDefault(doc, attributeNode, "fragment", "false");
873 populateDefault(doc, attributeNode, "rtexprvalue", "false");
874
875
876
877
878 String fragmentValue = findElementValue(attributeNode, "fragment");
879 boolean fragment = !((fragmentValue == null) || fragmentValue.equalsIgnoreCase("false"));
880 populateDefault(doc, attributeNode, "type", fragment
881 ? "javax.servlet.jsp.tagext.JspFragment"
882 : "java.lang.String");
883 }
884
885 /***
886 * Populates the given tag-file node with information from the given variable directive.
887 * @param tagFileNode The tag-file element
888 * @param doc The document we're populating
889 * @param directive The variable directive to process.
890 */
891 private void populateTagFileDetailsVariableDirective(Element tagFileNode, Document doc, Directive directive)
892 {
893 Iterator attributes = directive.getAttributes();
894 Element variableNode = doc.createElementNS(Constants.NS_J2EE, "variable");
895 tagFileNode.appendChild(variableNode);
896 while (attributes.hasNext())
897 {
898 Attribute attribute = (Attribute) attributes.next();
899 String name = attribute.getName();
900 String value = attribute.getValue();
901 Element element;
902 if (name.equals("name-given")
903 || name.equals("name-from-attribute")
904 || name.equals("variable-class")
905 || name.equals("declare")
906 || name.equals("scope")
907 || name.equals("description"))
908 {
909 element = doc.createElementNS(Constants.NS_J2EE, name);
910 element.appendChild(doc.createTextNode(value));
911 variableNode.appendChild(element);
912 }
913 }
914 populateDefault(doc, variableNode, "variable-class", "java.lang.String");
915 populateDefault(doc, variableNode, "declare", "true");
916 populateDefault(doc, variableNode, "scope", "NESTED");
917 }
918
919 /***
920 * Check to see if there's a short-name element. If not, the TLD is technically invalid, but supply one anyway, and
921 * give a warning.
922 * @param tagLibrary The tag library being populated
923 * @param doc The TLD DOM to populate.
924 * @param root The root element of the TLD DOM being populated.
925 */
926 private void checkOrAddShortName(TagLibrary tagLibrary, Document doc, Element root)
927 {
928 if (root.getElementsByTagNameNS("*", "short-name").getLength() == 0)
929 {
930 String prefix = "prefix" + substitutePrefix;
931 substitutePrefix++;
932 Element shortName = doc.createElementNS(Constants.NS_J2EE, "short-name");
933 shortName.appendChild(doc.createTextNode(prefix));
934 root.appendChild(shortName);
935 println("WARNING: "
936 + tagLibrary.getPathDescription()
937 + " is missing a short-name element. Using "
938 + prefix
939 + ".");
940 }
941 }
942
943 /***
944 * Check for empty attribute types and fill in the correct value. This is more difficult for the XSLT transform to
945 * do since the default is different depending on whether it is a fragment attribute or not.
946 * @param tagLibrary The tag library being populated
947 * @param doc The TLD DOM to populate.
948 * @param root The root element of the TLD DOM being populated.
949 */
950 private void checkOrAddAttributeType(TagLibrary tagLibrary, Document doc, Element root)
951 {
952 NodeList tagNodes = root.getElementsByTagNameNS("*", "tag");
953 for (int i = 0; i < tagNodes.getLength(); i++)
954 {
955 Element tagElement = (Element) tagNodes.item(i);
956 NodeList attributeNodes = tagElement.getElementsByTagNameNS("*", "attribute");
957 for (int j = 0; j < attributeNodes.getLength(); j++)
958 {
959 Element attributeElement = (Element) attributeNodes.item(j);
960 if (attributeElement.getElementsByTagNameNS("*", "type").getLength() == 0)
961 {
962
963 String defaultType = "java.lang.String";
964
965
966 String fragment = findElementValue(attributeElement, "fragment");
967 if ((fragment != null)
968 && (fragment.trim().equalsIgnoreCase("true") || fragment.trim().equalsIgnoreCase("yes")))
969 {
970 defaultType = "javax.servlet.jsp.tagext.JspFragment";
971 }
972
973
974 Element typeElement = doc.createElementNS(Constants.NS_J2EE, "type");
975 typeElement.appendChild(doc.createTextNode(defaultType));
976 attributeElement.appendChild(typeElement);
977 }
978 }
979 }
980 }
981
982 /***
983 * Generates all overview files, summarizing all TLDs.
984 */
985 private void generateOverview() throws IOException, TransformerException
986 {
987 generatePage(new File(this.outputDirectory, "index.html"), RESOURCE_PATH + "/index.html.xsl");
988 generatePage(new File(this.outputDirectory, "help-doc.html"), RESOURCE_PATH + "/help-doc.html.xsl");
989 generatePage(new File(this.outputDirectory, "overview-frame.html"), RESOURCE_PATH + "/overview-frame.html.xsl");
990 generatePage(new File(this.outputDirectory, "alltags-frame.html"), RESOURCE_PATH + "/alltags-frame.html.xsl");
991 generatePage(new File(this.outputDirectory, "alltags-noframe.html"), RESOURCE_PATH
992 + "/alltags-noframe.html.xsl");
993 generatePage(new File(this.outputDirectory, "overview-summary.html"), RESOURCE_PATH
994 + "/overview-summary.html.xsl");
995 }
996
997 /***
998 * Generates all the detail folders for each TLD
999 */
1000 private void generateTLDDetail() throws IOException, GeneratorException, TransformerException
1001 {
1002 ArrayList shortNames = new ArrayList();
1003 Element root = summaryTLD.getDocumentElement();
1004 NodeList taglibs = root.getElementsByTagNameNS("*", "taglib");
1005 int size = taglibs.getLength();
1006 for (int i = 0; i < size; i++)
1007 {
1008 Element taglib = (Element) taglibs.item(i);
1009 String shortName = findElementValue(taglib, "short-name");
1010 String displayName = findElementValue(taglib, "display-name");
1011 if (shortNames.contains(shortName))
1012 {
1013 throw new GeneratorException("Two tag libraries exist with "
1014 + "the same short-name '"
1015 + shortName
1016 + "'. This is not yet supported.");
1017 }
1018 String name = displayName;
1019 if (name == null)
1020 name = shortName;
1021 println("Generating docs for " + name + "...");
1022 shortNames.add(shortName);
1023 File outDir = new File(this.outputDirectory, shortName);
1024 outDir.mkdir();
1025
1026
1027 generateTLDDetail(outDir, shortName);
1028
1029
1030 NodeList tags = taglib.getElementsByTagNameNS("*", "tag");
1031 int numTags = tags.getLength();
1032 for (int j = 0; j < numTags; j++)
1033 {
1034 Element tag = (Element) tags.item(j);
1035 String tagName = findElementValue(tag, "name");
1036 generateTagDetail(outDir, shortName, tagName);
1037 }
1038
1039
1040 NodeList tagFiles = taglib.getElementsByTagNameNS("*", "tag-file");
1041 int numTagFiles = tagFiles.getLength();
1042 for (int j = 0; j < numTagFiles; j++)
1043 {
1044 Element tagFile = (Element) tagFiles.item(j);
1045 String tagFileName = findElementValue(tagFile, "name");
1046 generateTagDetail(outDir, shortName, tagFileName);
1047 }
1048
1049
1050 NodeList functions = taglib.getElementsByTagNameNS("*", "function");
1051 int numFunctions = functions.getLength();
1052 for (int j = 0; j < numFunctions; j++)
1053 {
1054 Element function = (Element) functions.item(j);
1055 String functionName = findElementValue(function, "name");
1056 generateFunctionDetail(outDir, shortName, functionName);
1057 }
1058 }
1059 }
1060
1061 /***
1062 * Generates the detail content for the tag library with the given short-name. Files will be placed in outdir.
1063 */
1064 private void generateTLDDetail(File outDir, String shortName) throws IOException, TransformerException
1065 {
1066 HashMap parameters = new HashMap();
1067 parameters.put("tlddoc-shortName", shortName);
1068
1069 generatePage(new File(outDir, "tld-frame.html"), RESOURCE_PATH + "/tld-frame.html.xsl", parameters);
1070 generatePage(new File(outDir, "tld-summary.html"), RESOURCE_PATH + "/tld-summary.html.xsl", parameters);
1071 }
1072
1073 /***
1074 * Generates the detail content for the tag with the given name in the tag library with the given short-name. Files
1075 * will be placed in outdir.
1076 */
1077 private void generateTagDetail(File outDir, String shortName, String tagName) throws IOException,
1078 TransformerException
1079 {
1080 HashMap parameters = new HashMap();
1081 parameters.put("tlddoc-shortName", shortName);
1082 parameters.put("tlddoc-tagName", tagName);
1083
1084 generatePage(new File(outDir, tagName + ".html"), RESOURCE_PATH + "/tag.html.xsl", parameters);
1085 }
1086
1087 /***
1088 * Generates the detail content for the function with the given name in the tag library with the given short-name.
1089 * Files will be placed in outdir.
1090 */
1091 private void generateFunctionDetail(File outDir, String shortName, String functionName) throws IOException,
1092 TransformerException
1093 {
1094 HashMap parameters = new HashMap();
1095 parameters.put("tlddoc-shortName", shortName);
1096 parameters.put("tlddoc-functionName", functionName);
1097
1098 generatePage(new File(outDir, functionName + ".fn.html"), RESOURCE_PATH + "/function.html.xsl", parameters);
1099 }
1100
1101 /***
1102 * Searches through the given element and returns the value of the body of the element. Returns null if the element
1103 * was not found.
1104 */
1105 private String findElementValue(Element parent, String tagName)
1106 {
1107 String result = null;
1108 NodeList elements = parent.getElementsByTagNameNS("*", tagName);
1109 if (elements.getLength() >= 1)
1110 {
1111 Element child = (Element) elements.item(0);
1112 Node body = child.getFirstChild();
1113 if (body.getNodeType() == Node.TEXT_NODE)
1114 {
1115 result = body.getNodeValue();
1116 }
1117 }
1118 return result;
1119 }
1120
1121 /***
1122 * Generates the given page dynamically, by running the summary document through the given XSLT transform. Assumes
1123 * no parameters.
1124 * @param outFile The target file
1125 * @param inputXSL The stylesheet to use for the transformation
1126 */
1127 private void generatePage(File outFile, String inputXSL) throws IOException, TransformerException
1128 {
1129 generatePage(outFile, inputXSL, null);
1130 }
1131
1132 /***
1133 * Generates the given page dynamically, by running the summary document through the given XSLT transform.
1134 * @param outFile The target file
1135 * @param inputXSL The stylesheet to use for the transformation
1136 * @param parameters String key and Object value pairs to pass to the transformation.
1137 */
1138 private void generatePage(File outFile, String inputXSL, Map parameters) throws IOException, TransformerException
1139 {
1140 InputStream xsl = getResourceAsStream(inputXSL);
1141
1142
1143
1144 Transformer transformer = getTransformer(new StreamSource(xsl));
1145
1146 if (parameters != null)
1147 {
1148 Iterator params = parameters.keySet().iterator();
1149 while (params.hasNext())
1150 {
1151 String key = (String) params.next();
1152 Object value = parameters.get(key);
1153 transformer.setParameter(key, value);
1154 }
1155 }
1156 transformer.transform(new DOMSource(summaryTLD), new StreamResult(outFile));
1157 }
1158
1159 /***
1160 * Copy the given resource to the given output file. The classloader that loaded TLDDocGenerator will be used to
1161 * find the resource. If xsltDirectory is not null, the files are copied from that directory instead.
1162 * @param outputFile The destination file
1163 * @param resource The resource to copy, starting with '/'
1164 */
1165 private void copyResourceToFile(File outputFile, String resource) throws IOException
1166 {
1167 InputStream in = getResourceAsStream(resource);
1168 OutputStream out = new FileOutputStream(outputFile);
1169 byte[] buffer = new byte[1024];
1170 int len;
1171 while ((len = in.read(buffer)) != -1)
1172 {
1173 out.write(buffer, 0, len);
1174 }
1175 out.close();
1176 in.close();
1177 }
1178
1179 /***
1180 * Outputs the given message to stdout, only if !quiet
1181 */
1182 private void println(String message)
1183 {
1184 if (!quiet)
1185 {
1186 System.out.println(message);
1187 }
1188 }
1189
1190 /***
1191 * If xsltDirectory is null, obtains an InputStream of the given resource from RESOURCE_PATH, using the class loader
1192 * that loaded TLDDocGenerator. Otherwise, finds the file in xsltDirectory and defaults to the default stylesheet if
1193 * it has not been overridden.
1194 * @param resource, must start with RESOURCE_PATH.
1195 * @return An InputStream for the given resource.
1196 */
1197 private InputStream getResourceAsStream(String resource)
1198 {
1199 InputStream result = null;
1200
1201 if (xsltDirectory != null)
1202 {
1203 File resourceFile = new File(xsltDirectory, resource.substring(RESOURCE_PATH.length() + 1));
1204 try
1205 {
1206 result = new FileInputStream(resourceFile);
1207 }
1208 catch (FileNotFoundException e)
1209 {
1210
1211 }
1212 }
1213
1214 if (result == null)
1215 {
1216 result = TLDDocGenerator.class.getResourceAsStream(resource);
1217 }
1218
1219 return result;
1220 }
1221
1222 /***
1223 * Displays a "success" message and plugs the tag library registry on java.net.
1224 */
1225 private void outputSuccessMessage()
1226 {
1227 println("\nDocumentation generated. If your tag library is "
1228 + "intended for public use, \n"
1229 + "please add it to the repository at "
1230 + "http://taglibrarydoc.dev.java.net/");
1231 }
1232 }