1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package net.sf.xframe.xsddoc;
22
23 import java.io.File;
24 import java.util.Vector;
25
26 import javax.xml.parsers.DocumentBuilder;
27 import javax.xml.parsers.DocumentBuilderFactory;
28 import javax.xml.parsers.ParserConfigurationException;
29
30 import org.apache.tools.ant.BuildException;
31 import org.apache.tools.ant.DirectoryScanner;
32 import org.apache.tools.ant.Project;
33 import org.apache.tools.ant.taskdefs.MatchingTask;
34 import org.apache.tools.ant.types.FileSet;
35 import org.w3c.dom.Document;
36 import org.w3c.dom.Element;
37
38 /***
39 * Adapter for xsddoc processor to Apache Ant.
40 *
41 * <p>Generates XML Schema documentation using the xsddoc tool.</p>
42 * <h3>Parameters</h3>
43 * <p><table border="1" cellspacing="0" cellpadding="2">
44 * <tr><td><b>Attribute</b></td><td><b>Description</b></td><td><b>Required</b></td></tr>
45 * <tr><td>schemaLoaction</td><td><b>deprecated</b>, see <code>file</code> parameter</td>
46 * <td rowspan="3">Exactly one of the these or nested <fileset></td></tr>
47 * <tr><td>file</td><td>shortcut for specifying a single file fileset</td></tr>
48 * <tr><td>dir</td><td>shortcut for specifying a single folder fileset</td></tr>
49 * <tr><td>out</td><td>destination directory for output files</td><td>Yes</td></tr>
50 * <tr><td>xml</td><td>If output should be XML instead of HTML. (yes | no). Default is no</td><td>No</td></tr>
51 * <tr><td>css</td><td>provide external CSS file</td><td>&</td></tr>
52 * <tr><td>title</td><td><b>deprecated</b>, see <code>doctitle</code> parameter</td><td>No</td></tr>
53 * <tr><td>doctitle</td><td>Include title for the package index(first) page (html-code)</td><td>No</td></tr>
54 * <tr><td>header</td><td>Include header text for each page (html-code)</td><td>No</td></tr>
55 * <tr><td>footer</td><td>Include footer text for each page (html-code)</td><td>No</td></tr>
56 * <tr><td>bottom</td><td>Include bottom text for each page (html-code)</td><td>No</td></tr>
57 * <tr><td>failonerror</td><td>Log a warning message, but do not stop the
58 * build, when the file to copy does not exist or one of the nested filesets
59 * points to a directory that doesn't exist or an error occurs while copying.
60 * (yes | no). Default is no</td><td>No</td></tr>
61 * <tr><td>verbose</td><td>Output messages about what xsddoc is doing. (yes | no). Default is yes</td><td>No</td></tr>
62 * <tr><td>quiet</td><td>Be quiet about what xsddoc is doing. (yes | no). Default is no</td><td>No</td></tr>
63 * <tr><td>debug</td><td>Output internal messages about what xsddoc is doing. (yes | no). Default is no</td><td>No</td></tr>
64 * <tr><td>hideSubTypes</td><td>hide sub types references. (yes | no). Default is no</td><td>No</td></tr>
65 * <tr><td>hideLocalUsage</td><td>hide local usage references. (yes | no). Default is no</td><td>No</td></tr>
66 * <tr><td>hideTypes</td><td>hide types in overview pages. (yes | no). Default is no</td><td>No</td></tr>
67 * <tr><td>hideGroups</td><td>hide groups in overview pages. (yes | no). Default is no</td><td>No</td></tr>
68 * <tr><td>hideAttributes</td><td>hide attributes in overview pages. (yes | no). Default is no</td><td>No</td></tr>
69 * <tr>
70 * </table></p>
71 *
72 * <h3>Parameters specified as nested elements</h3>
73 *
74 * <h4>fileset</h4>
75 * <p>
76 * <a href="http://ant.apache.org/manual/CoreTypes/fileset.html">FileSets</a>
77 * are used to select sets of files to process. To use a fileset, the todir
78 * attribute must be set.</p>
79 *
80 * <h4>doctitle</h4>
81 * <p>Same as the <code>doctitle</code> attribute, but you can nest text
82 * inside the element this way.</p>
83 * <h4>header</h4>
84 * <p>Similar to <code><doctitle></code>.</p>
85 * <h4>footer</h4>
86 * <p>Similar to <code><doctitle></code>.</p>
87 * <h4>bottom</h4>
88 * <p>Similar to <code><doctitle></code>.</p>
89 *
90 * <dl><dt><b>Usage in Apache Ant build files:</b></dt><dd><pre>
91 *<!--
92 * Define xsddoc task.
93 *-->
94 *<taskdef name="xsddoc" classname="net.sf.xframe.xsddoc.Task"/>
95 *
96 *<!--
97 * Use xsddoc task.
98 *-->
99 *<xsddoc file="myschema.xsd"
100 * out="doc/schema/myschema">
101 * <doctitle><![CDATA[XML <code>Schema</code> for XML Schema]]></doctitle>
102 *</xsddoc>
103 * </pre></dl>
104 *
105 * @author <a href="mailto:kriede@users.sourceforge.net">Kurt Riede</a>
106 *
107 */
108 public final class Task extends MatchingTask {
109
110 /*** namespace of XML schema. */
111 private static final String SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
112
113 /*** documentation footer. */
114 private Html footer;
115
116 /*** Documentation Title. */
117 private Html doctitle;
118
119 /*** Documentation header. */
120 private Html header;
121
122 /*** Documentation bottom. */
123 private Html bottom;
124
125 /*** Folder of schema files in case of folder based schema localisation. */
126 private String dir = null;
127
128 /*** schema location in case of a single schema file.*/
129 private String file = null;
130
131 /*** Fileset in case of fileset based schema localisation. */
132 private Vector filesets = new Vector();
133
134 /*** Reference to the xsddoc processor. */
135 private Processor processor = null;
136
137 /***
138 * Whether to stop the build, when the file to process does not exist or
139 * one of the nested filesets points to a directory that doesn't exist or
140 * an error occurs while copying.
141 */
142 private boolean failonerror = true;
143
144 /*** if verbose mode or not. */
145 private boolean verbose = false;
146
147 /*** if debug mode or not. */
148 private boolean debug = false;
149
150 /*** Reference to a DOM document builder. */
151 private DocumentBuilder builder = null;
152
153 /***
154 * Default constructor.
155 */
156 public Task() {
157 processor = new Processor();
158 }
159
160 /***
161 * Execute xsddoc task.
162 *
163 * @throws BuildException if execution of xsddoc task failed
164 */
165 public void execute() throws BuildException {
166 try {
167 process();
168 } catch (Exception e) {
169 if (failonerror) {
170 if (e instanceof BuildException) {
171 throw (BuildException) e;
172 }
173 throw new BuildException(e);
174 }
175 log(e.getMessage(), Project.MSG_WARN);
176 }
177 }
178
179 /***
180 * Execute xsddoc task.
181 *
182 * @throws ProcessorException if error occured during execution
183 */
184 private void process() throws ProcessorException {
185 final int isFile = (file == null ? 0 : 1);
186 final int isDir = (dir == null ? 0 : 1);
187 final int isFileset = (filesets.size() == 0 ? 0 : 1);
188 if (isFile + isDir + isFileset != 1) {
189 throw new BuildException("Exactly one of the file or dir attributes, or a fileset element, must be set.");
190 }
191 if (doctitle != null) {
192 processor.setDoctitle(doctitle.getText());
193 }
194 if (header != null) {
195 processor.setHeader(header.getText());
196 }
197 if (footer != null) {
198 processor.setFooter(footer.getText());
199 }
200 if (bottom != null) {
201 processor.setBottom(bottom.getText());
202 }
203 if (file != null) {
204 processor.setSchemaLocation(file);
205 processor.execute();
206 }
207 if (dir != null) {
208 final Document mediatorSchema = createDocument();
209 final File folder = new File(dir);
210 if (!folder.exists()) {
211 throw new BuildException("not a directory: " + folder.getAbsolutePath());
212 }
213 final String[] files = folder.list();
214 for (int i = 0; i < files.length; i++) {
215 if (debug) {
216 System.out.println("found file " + files[i]);
217 }
218 final File theFile = new File(files[i]);
219 if (!theFile.isDirectory()) {
220 addSchema(mediatorSchema, folder.getAbsolutePath(), files[i]);
221 } else {
222 if (debug) {
223 System.out.println("Seems not to be a file " + files[i]);
224 }
225 }
226 }
227 processor.setSchemaLocation(folder.getAbsolutePath() + File.separator + "mediator-schema");
228 processor.setMainSchema(mediatorSchema);
229 processor.execute();
230 }
231 if (filesets.size() > 0) {
232 builder = getDocumentBuilder();
233 final Document mediatorSchema = builder.getDOMImplementation().createDocument(SCHEMA_NS, "schema", null);
234 for (int i = 0; i < filesets.size(); i++) {
235 final FileSet fileSet = (FileSet) filesets.elementAt(i);
236 final DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
237 addSchemas(mediatorSchema, scanner.getBasedir().getAbsolutePath(), scanner);
238 }
239 processor.setSchemaLocation(getProject().getBaseDir().getAbsolutePath() + File.separator + "todo");
240 processor.setMainSchema(mediatorSchema);
241 processor.execute();
242 }
243 }
244
245 /***
246 * Creates a new empty DOM document.
247 *
248 * @return mediator schema as a DOM document
249 */
250 private Document createDocument() {
251 final Document mediatorSchema = getDocumentBuilder().getDOMImplementation().createDocument(SCHEMA_NS, "schema", null);
252 return mediatorSchema;
253 }
254
255 /***
256 * Returns a DOM document builder.
257 *
258 * @return DOM document builder
259 * @throws BuildException if the DocumentBuilder cannot be created
260 */
261 private DocumentBuilder getDocumentBuilder() throws BuildException {
262 if (builder != null) {
263 return builder;
264 }
265 try {
266 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
267 builder = factory.newDocumentBuilder();
268 return builder;
269 } catch (ParserConfigurationException e) {
270 throw new BuildException(e);
271 }
272 }
273
274 /***
275 * Adds a set of schemas defined by the files in a DirectoryScanner to
276 * a mediator schema.
277 *
278 * <p>A mediator schema is a schema that does nothing else than import
279 * a set of other schemas.</p>
280 *
281 * @param mediatorSchema the mediator schema
282 * @param base base file to resolve relative paths
283 * @param scanner DirectoryScanner that defines the schema files to mediate
284 */
285 private void addSchemas(final Document mediatorSchema, final String base, final DirectoryScanner scanner) {
286 final String[] files = scanner.getIncludedFiles();
287 for (int i = 0; i < files.length; i++) {
288 addSchema(mediatorSchema, base, files[i]);
289 }
290 }
291
292 /***
293 * Adds a schema defined by a file name to a mediator schema.
294 *
295 * @param mediatorSchema the meditator schema
296 * @param base base file to resolve relative paths
297 * @param schemaLocation the schema to add
298 */
299 private void addSchema(final Document mediatorSchema, final String base, final String schemaLocation) {
300 if (verbose) {
301 System.out.println("found schema " + schemaLocation);
302 }
303 String namespace = null;
304 namespace = getNamespace(base, schemaLocation);
305 if (namespace == null) {
306 if (debug) {
307 System.out.println("Seems not to be a schema, ignoring " + schemaLocation);
308 }
309 return;
310 }
311 final Element importNode = mediatorSchema.createElementNS(SCHEMA_NS, "import");
312 final String projectDir = getProject().getBaseDir().getAbsolutePath() + File.separator;
313
314
315
316
317 final String schemaLoc;
318
319 if (base.length() > projectDir.length() && base.startsWith(projectDir)) {
320 String relativePath = base.substring(projectDir.length());
321 schemaLoc = relativePath + File.separator + schemaLocation;
322 } else {
323 schemaLoc = schemaLocation;
324 }
325 importNode.setAttribute("schemaLocation", schemaLoc);
326 importNode.setAttribute("namespace", namespace);
327 mediatorSchema.getDocumentElement().appendChild(importNode);
328 }
329
330 /***
331 * Extracts and returns the target namespace from a schema file.
332 *
333 * @param base base file name to resolve relative paths
334 * @param filename file name of schema file
335 * @return target namespace of schema
336 */
337 private String getNamespace(final String base, final String filename) {
338 try {
339 final Document schema = getDocumentBuilder().parse(new File(base, filename));
340 final Element root = schema.getDocumentElement();
341 return root.getAttribute("targetNamespace");
342 } catch (Exception e) {
343 return null;
344 }
345 }
346
347
348
349
350
351 /***
352 * Setter method for debug property.
353 *
354 * @param isDebug output debug information or not
355 */
356 public void setDebug(final boolean isDebug) {
357 debug = isDebug;
358 processor.setDebug(isDebug);
359 }
360
361 /***
362 * Setter method for out property.
363 *
364 * @param out output folder to use
365 */
366 public void setOut(final String out) {
367 processor.setOut(out);
368 }
369
370 /***
371 * Setter method for schemaLocation property.
372 *
373 * @param schemaLocation location of XML schema to use
374 * @deprecated use {@link #setFile(java.lang.String)} instead
375 * @see #setFile(java.lang.String)
376 */
377 public void setSchemalocation(final String schemaLocation) {
378 file = schemaLocation;
379 }
380
381 /***
382 * Setter method for file property.
383 *
384 * @param theFile location of XML schema
385 */
386 public void setFile(final String theFile) {
387 file = theFile;
388 }
389
390 /***
391 * Setter method for dir property.
392 *
393 * @param theDir folder to search for schemas
394 */
395 public void setDir(final String theDir) {
396 dir = theDir;
397 }
398
399 /***
400 * Setter method for title property.
401 *
402 * @param title title to use
403 * @deprecated use {@link #setDoctitle(java.lang.String)} instead
404 * @see #setDoctitle(java.lang.String)
405 */
406 public void setTitle(final String title) {
407 final Html html = new Html();
408 html.addText(title);
409 addDoctitle(html);
410 }
411
412 /***
413 * Setter method for css property.
414 *
415 * @param css location of XML schema
416 */
417 public void setCss(final String css) {
418 processor.setCss(css);
419 }
420
421 /***
422 * Setter method for verbose property.
423 *
424 * @param isVerbose be verbose or not
425 */
426 public void setVerbose(final boolean isVerbose) {
427 verbose = isVerbose;
428 processor.setVerbose(isVerbose);
429 }
430
431 /***
432 * Setter method for quiet property.
433 *
434 * @param isQuiet be quiet or not
435 */
436 public void setQuiet(final boolean isQuiet) {
437 verbose = !isQuiet;
438 processor.setVerbose(!isQuiet);
439 }
440
441 /***
442 * Setter method for hideLocalUsage property.
443 *
444 * @param hideLocalUsage if local usage should be hidden or not
445 */
446 public void setHidelocalusage(final boolean hideLocalUsage) {
447 processor.setHideLocalUsage(hideLocalUsage);
448 }
449
450 /***
451 * Setter method for hideSubTypes property.
452 *
453 * @param hideSubTypes if sub types should be hidden or not
454 */
455 public void setHidesubtypes(final boolean hideSubTypes) {
456 processor.setHideSubTypes(hideSubTypes);
457 }
458
459 /***
460 * Setter method for hideTypes property.
461 *
462 * @param hideTypes if types should be hidden or not
463 */
464 public void setHidetypes(final boolean hideTypes) {
465 processor.setHideTypes(hideTypes);
466 }
467
468 /***
469 * Setter method for hideAttributes property.
470 *
471 * @param hideAttributes if attributes should be hidden or not
472 */
473 public void setHideattributes(final boolean hideAttributes) {
474 processor.setHideAttributes(hideAttributes);
475 }
476
477 /***
478 * Setter method for hideGroups property.
479 *
480 * @param hideGroups if groups should be hidden or not
481 */
482 public void setHidegroups(final boolean hideGroups) {
483 processor.setHideGroups(hideGroups);
484 }
485
486 /***
487 * Setter method for xml attribute.
488 *
489 * @param isXml boolean
490 */
491 public void setXml(final boolean isXml) {
492 processor.setXml(isXml);
493 }
494
495 /***
496 * Setter method for failonerror attribute.
497 *
498 * @param isFailonerror if should fail on error or not
499 */
500 public void setFailonerror(final boolean isFailonerror) {
501 failonerror = isFailonerror;
502 }
503
504
505
506
507
508 /***
509 * Adds a set of files to be deleted.
510 * @param set the set of files to be deleted
511 */
512 public void addFileset(final FileSet set) {
513 filesets.addElement(set);
514 }
515
516 /***
517 * Add a document title to use for the overview page.
518 *
519 * @param text the HTML element containing the document title.
520 */
521 public void addDoctitle(final Html text) {
522 doctitle = text;
523 }
524
525 /***
526 * Set the title of the generated overview page.
527 *
528 * @param theDoctitle the Document title.
529 */
530 public void setDoctitle(final String theDoctitle) {
531 final Html html = new Html();
532 html.addText(theDoctitle);
533 addDoctitle(html);
534 }
535
536 /***
537 * Set the header text to be placed at the top of each output file.
538 *
539 * @param theHeader the header text
540 */
541 public void setHeader(final String theHeader) {
542 final Html html = new Html();
543 html.addText(theHeader);
544 addHeader(html);
545 }
546
547 /***
548 * Set the header text to be placed at the top of each output file.
549 *
550 * @param theHeader the header text
551 */
552 public void addHeader(final Html theHeader) {
553 header = theHeader;
554 }
555
556 /***
557 * Set the footer text to be placed at the bottom of each output file.
558 *
559 * @param text the footer text.
560 */
561 public void addFooter(final Html text) {
562 footer = text;
563 }
564
565 /***
566 * Set the footer text to be placed at the bottom of each output file.
567 *
568 * @param theFooter the footer text.
569 */
570 public void setFooter(final String theFooter) {
571 final Html html = new Html();
572 html.addText(theFooter);
573 addFooter(html);
574 }
575
576 /***
577 * Set the text to be placed at the bottom of each output file.
578 *
579 * @param theBottom the bottom text.
580 */
581 public void setBottom(final String theBottom) {
582 final Html html = new Html();
583 html.addText(theBottom);
584 addBottom(html);
585 }
586
587 /***
588 * Set the text to be placed at the bottom of each output file.
589 *
590 * @param theBottom the bottom text.
591 */
592 public void addBottom(final Html theBottom) {
593 bottom = theBottom;
594 }
595
596 /***
597 * An HTML fragment in a nested element of the xsddoc task.
598 *
599 * This class is used for those nested xsddoc elements which can contain
600 * HTML such as doctitle, footer or header.
601 */
602 public static final class Html {
603
604 /*** A buffer fot the text of the element. */
605 private StringBuffer text = new StringBuffer();
606
607 /***
608 * Add text to the element.
609 *
610 * @param theText the text to be added.
611 */
612 public void addText(final String theText) {
613 text.append(theText);
614 }
615
616 /***
617 * Get the current text for the element.
618 *
619 * @return the current text.
620 */
621 public String getText() {
622 return text.substring(0);
623 }
624 }
625 }