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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 package org.xrn.ant;
56
57 import org.apache.tools.ant.*;
58 import org.apache.tools.ant.taskdefs.email.EmailTask;
59 import org.apache.tools.ant.types.FileSet;
60 import org.apache.tools.ant.types.Path;
61 import org.apache.tools.ant.types.XMLCatalog;
62 import org.w3c.dom.*;
63 import org.xml.sax.SAXException;
64
65 import javax.xml.parsers.DocumentBuilder;
66 import javax.xml.parsers.DocumentBuilderFactory;
67 import javax.xml.parsers.FactoryConfigurationError;
68 import javax.xml.parsers.ParserConfigurationException;
69 import javax.xml.transform.*;
70 import javax.xml.transform.dom.DOMResult;
71 import javax.xml.transform.dom.DOMSource;
72 import javax.xml.transform.stream.StreamResult;
73 import javax.xml.transform.stream.StreamSource;
74 import java.io.*;
75 import java.lang.reflect.Constructor;
76 import java.util.ArrayList;
77 import java.util.StringTokenizer;
78 import java.util.Vector;
79
80 /***
81 * This task enables to use the XMLReleaseNotes framework via Ant.
82 * <div class="noteBlock">
83 * If you need to use the XRN task, this is place of reference that exhaustively shows all features.
84 * </div>
85 * <h2>Version</h2>
86 * <p>
87 * This manual has been generated from the source code of <b>XRN V@XRN_VERSION@</b>. This documentation is extracted from
88 * the source code, hence it should be up-to-date.
89 * </p>
90 * <div class="importantBlock">
91 * This manual just explains the Ant XRN task, but is not a documentation on the XRN framework. You will find
92 * such a documentation on the official web site of the XRN framework at
93 * <a href="http://xmlreleasenotes.free.fr">http://xmlreleasenotes.free.fr</a>.
94 * </div>
95 *
96 * <h2>A wrapper above XRN</h2>
97 * <p>
98 * The XMLReleaseNotes framework can also be used from the Ant world, thanks to this tiny wrapper task.
99 * </p>
100 * <h3>Declare the task</h3>
101 * <p>
102 * In order to use the Ant plug-in task, declare it via the
103 * <pre class="code">
104 * <taskdef
105 * classname="org.xrn.ant.XMLReleaseNotes"
106 * name="XMLReleaseNotes"
107 * />
108 * </pre>
109 * snippet. Later on, you will use the <code>XMLReleaseNotes</code> task.
110 * </p>
111 * <h3>Runtime requirements</h3>
112 * <p>
113 * There is a place where you will find this information back:
114 * <a href="http://xmlreleasenotes.free.fr/MultipleReleaseNotes_top.html">XRN release notes</a>,
115 * so you'd better to refer to them, in order to have fresh information. The "<i>requirements</i>" section of the
116 * XRN version that you are currently using should be self explanatory.
117 * </p>
118 * <p>
119 * However, here are roughly the components you need at runtime in order it to work properly (at least by version 0.17.2
120 * of the framework).
121 * <ul>
122 * <li>Ant V1.5.0+: older versions will not work because the <code>XMLCatalog</code> class is used.</li>
123 * <li>It is required that the Ant <code>lib</code> directory contains an XSLT implementation library
124 * (like Xalan or Saxon, for instance) of the <code>javax.xml.transform.TransformerFactory</code> Java interface.</li>
125 * </ul>
126 * </p>
127 *
128 * <h2>The two-phases</h2>
129 * <p>
130 * This task processes two steps:
131 * <ol>
132 * <li>the enrichment operated on the XML XRN file via optional XSL transformations,</li>
133 * <li>the rendering of the resulting XML into HTML.</li>
134 * </ol>
135 * The detailed description of this feature is beyond this manual. However, let's see to what elements these two
136 * phases correspond to.
137 * <a name="enrichment"/>
138 * <h3>Enrichment</h3>
139 * <p>
140 * This first step is optional, and is handled by the inner <a href="#model"><code>model</code></a> XML elements.
141 * You can define as many transformers that you want to.
142 * </p>
143 * <a name="rendering"/>
144 * <h3>Rendering</h3>
145 * <p>
146 * </p>
147 * <p>
148 *
149 * <a name="renderingFeature"/>
150 * <h2>Two different types of rendering</h2>
151 * <p>
152 * The HTML renders pages can be generated by using two distinct modes, either compact or expanded.
153 * </p>
154 * <a name="compactRendering"/>
155 * <h3>Compact rendering</h3>
156 * <p>
157 * In that mode, only one HTML file is rendered per component release notes. This is a compact mode.
158 * </p>
159 * <a name="expandedRendering"/>
160 * <h3>Expanded rendering</h3>
161 * <p>
162 * This mode renders the release notes in a very likely Javadoc way. This involves that a HTML page is rendered for
163 * every release of each component.
164 * </p>
165 * <div class="noteBlock">
166 * This feature is activated as soon as more one
167 * XRN file is provided to the task, or when the <a href="#singlePage"><code>singlePage</code><a> is disabled.
168 * </div>
169 *
170 * <a name="supportedHTML"/>
171 * <h2>HTML supported</h2>
172 * <p>
173 * Some attributes or elements of the task support textual information mixed with HTML tags. Every time this is the
174 * case, the end-user is directed here.
175 * </p>
176 *
177 * <a name="xsltResource"/>
178 * <h2>XSLT resource</h2>
179 * <p>
180 * Some inner XML elements of the XRN task enable to refer to a XSLT style sheet file. When this is the case, you
181 * have the choice to use:
182 * <ul>
183 * <li>either <code>xsl</code>: this specifies the path of this stylesheet file,</li>
184 * <li>or <code>plugin</code>: this specifies the name of the XSLT plug-in to be used, present in the XRN jar,
185 * under <code>XMLReleaseNotes/plugin</code> hierarchy.
186 * When you use it, provide the name of the plug-in XSLT file, without the file extension. For example, if you
187 * intend to use the version and type filter that is performed through the <code>XMLReleaseNotes/plugin/VersionAndTypeFilterModel.xsl</code>,
188 * set this attribute to <code>VersionAndTypeFilterModel</code>.</li>
189 * </ul>
190 * <span class="note">The attribute defined first takes precedence over the second one, in case when you define both.</span>
191 * </p>
192 *
193 * <h2><b>Important</b></h2>
194 * <div class="warningBlock">
195 * Here are some miscellaneous important matters.
196 * </div>
197 * <a name="xmlcatalog"/>
198 * <h3><code>XMLCatalog</code> feature</h3>
199 * <p>
200 * This is a rather advanced feature, so, unless you use XSLT plug-in feature, you do not need to care about it.
201 * </p>
202 * <p>
203 * This feature is used via in the following locations within the task:
204 * <ul>
205 * <li><a href="#model"><code>model</code></a>,</li>
206 * <li><a href="#layout"><code>layout</code></a>,</li>
207 * <li><a href="#pluginLayout"><code>pluginLayout</code></a>.</li>
208 * </ul>
209 * </p>
210 * <p>
211 * Said shortly, the <code>XMLCatalog</code> nested XML element acts like a URI resolver, which is very handy when it comes
212 * to the numerous XSLT that occur at runtime.
213 * </p>
214 * <p>
215 * In the previous XML blocks, you can add a single <code>XMLCatalog</code> inner XML element, so as to tell the XSLT
216 * transformations where to find resources according to their public ID. In that case, insert the following block:
217 * <pre class="code">
218 * <xmlcatalog>
219 * <entity
220 * publicId="SomePublicID"
221 * location="its_actual_URI"
222 * />
223 * ...
224 * </xmlcatalog>
225 * </pre>
226 * Use as many <code>entity</code> as you need.
227 * </p>
228 * <p>
229 * This <a href="http://ant.apache.org/manual/CoreTypes/xmlcatalog.html"><code>XMLCatalog</code></a>
230 * feature has been developed in the Ant framework. This feature is also used in the
231 * <a href="http://ant.apache.org/manual/CoreTasks/style.html"><code>Xslt/Style</code></a>: do not hesitate to take a
232 * look in the previous hyperlinks, in order to understand its purpose and how it works. <b>Moreover, there are lots of
233 * examples of usage there.</b>
234 * </p>
235 *
236 * <a name="mail"/>
237 * <h3>Mail feature</h3>
238 * <p>
239 * This task is also the official Ant <a href="http://ant.apache.org/manual/CoreTasks/mail.html">Mail</a> task,
240 * so that you can send eventually the release notes by mail. If the <code>mail</code> parameter is set to <code>yes</code>,
241 * then the generated release notes will be sent as <code>text/html</code>: define the traditional Ant <code>Mail</code>
242 * parameters, except the <code>message</code>, <code>messagefile</code>, <code>messagemimetype</code>, <code>files</code>
243 * and <code>includefilenames</code>.
244 * </p>
245 * <p>
246 * The mail sent will contain the <code>abstract</code> as subject, and the content of the mail will be the
247 * generated release notes.
248 * </p>
249 *
250 * <h2>Example</h2>
251 * <p>
252 * <pre class="code">
253 * <XMLReleaseNotes
254 * xml="ReleaseNotes.xml"
255 * />
256 * </pre>
257 * will render the HTML for the XML release notes file <code>ReleaseNotes.xml</code>.
258 * </p>
259 *
260 * @author Edouard Mercier
261 * @version @XRN_VERSION@
262 * @taskname XMLReleaseNotes
263 * @since V0.3.2, 2004.04.01. Shift in the <code>org.xrn.ant</code> package from V0.18.0.
264 */
265 public class XMLReleaseNotes
266 extends Task
267 {
268
269
270
271
272
273
274
275
276
277
278
279 /***
280 * The XMLReleaseNotes XML file that describes the component release note.
281 */
282 private File _xml_release_notes_file = new File("XMLReleaseNotes.xml");
283
284 /***
285 * Remembers whether the user has provided explicitely an XML release notes file.
286 */
287 private boolean _xml_release_notes_file_defined = false;
288
289 /***
290 * Remembers whether the user has provided more than one XML release notes file.
291 */
292 private boolean _mutiple_xml_release_notes_file_defined = false;
293
294 /***
295 * <a name="xml"/>
296 * Defines the XRN-compliant file that describes the component release notes.
297 * <p>
298 * All operations will be performed on that release notes file.
299 * </p>
300 * <div class="seeBlock">
301 * See the <a href="#fileset"><code>fileset</code></a> inner element.
302 * </div>
303 *
304 * @optional defaults to <code>XMLReleaseNotes.xml</code>, which means that the task will attempt to process this
305 * single release notes file
306 */
307 public void setXML(File xml_release_notes_file)
308 {
309 _xml_release_notes_file_defined = true;
310 _xml_release_notes_file = xml_release_notes_file;
311 }
312
313 private String _xslt_transformer_factory_class_name = null;
314
315 /***
316 * Defines the Fully Qualified Name (FQN) of the XLST <code>TransformerFactory</code> class that will be used for
317 * all transformations.
318 * <p>
319 * This will set the Java system property <code>javax.xml.transform.TransformerFactory</code> with the implementation
320 * of interface <code>javax.xml.transform.TransformerFactory</code>.
321 * </p>
322 *
323 * @optional the default implementation found in the classpath. <span class="warning">With the Sun JRE V1.4, it appears
324 * that this Java system property is not always defined. For instance, define this property with
325 * <code>org.apache.xalan.processor.TransformerFactoryImpl</code>, if you are using the Xalan V2 implementation.</span>
326 */
327 public void setProcessor(String xslt_transformer_factory_class_name)
328 {
329 _xslt_transformer_factory_class_name = xslt_transformer_factory_class_name;
330 }
331
332 /***
333 * @return <code>true</code> if the current XSLT implementation is Xalan.
334 */
335 private boolean isUsingXalanImplementation()
336 {
337 final String xslt_implementation_fqn = getActualTransformerFactory();
338 if (xslt_implementation_fqn.lastIndexOf("xalan") != -1)
339 {
340 return true;
341 }
342 else
343 {
344 return false;
345 }
346 }
347
348 /***
349 * The vector of {@link org.apache.tools.ant.types.FileSet} that defines all the XRN file sets that should be handled.
350 */
351 private Vector _fileset_vector = new Vector();
352
353 /***
354 * <a name="fileset"/>
355 * <span class="important">As many occurrences of this element are allowed.</span>
356 * Enables to define multiple XRN release notes files at the same time. In that case, the task will handle
357 * all the release notes files declared hereby. It is supposed that all provided files are XRN-compliant release
358 * notes file. If one is not, the task will fail.
359 * <div class="warning">
360 * When defined, the <a href="#xml"><code>xml<code></a> attribute is not taken into account.
361 * </div>
362 * <div class="noteBlock">
363 * This XML element is the Ant <a href="http://ant.apache.org/manual/CoreTypes/fileset.html"><code>fileset</code></a> feature.
364 * This same element is being used in the Ant <a href="http://ant.apache.org/manual/CoreTasks/copy.html"><code>copy</code></a> task.
365 * </div>
366 * <div class="seeBlock">
367 * Read this <a href="#renderingFeature">section</a> for additional information.
368 * </div>
369 *
370 * @optional when used, the generated HTML will contain the release notes of all the underlying components
371 */
372 public Object createFileSet()
373 {
374 _mutiple_xml_release_notes_file_defined = true;
375 FileSet file_set = new FileSet();
376 _fileset_vector.add(file_set);
377 return file_set;
378 }
379
380 private Path _xslt_path = null;
381
382 /***
383 * <a name="XSLTPath"/>
384 * <div class="importantBlock">
385 * This feature is only relevant when the XSLT implementation lives outside from the Ant <code>lib</code> directory.
386 * </div>
387 * Indicates to Ant where the XSLT implementation jar file(s) required at runtime are located.
388 *
389 * @optional when the XSLT implementation to use is not in ant <code>lib</code> directory, provide its jar file
390 * path via this element
391 */
392 public Object createXSLTPath()
393 {
394 _xslt_path = new Path(getProject());
395 return _xslt_path;
396 }
397
398 private Path _uri_resolver_path = null;
399
400 /***
401 * <a name="URIResolverPath"/>
402 * <div class="importantBlock">
403 * This feature is only relevant when the XRN task jar file(s) lives outside from the Ant <code>lib</code> directory,
404 * and thus that you declared it via the <code>typedef/taskdef</code> task, with a properly defined classpath.
405 * </div>
406 * Indicates to Ant where the internal resources (<code>.xsl</code>, for the most) that are required at runtime are
407 * located. Those resources are present in the XRN Ant task jar file, so you only need to refer to this file.
408 * <div class="exampleBlock">
409 * Example:
410 * <pre>
411 * <URIResolverPath>
412 * <pathelement location="<the path to>/XMLReleaseNotes.jar"/>
413 * </URIResolverPath>
414 * </pre>
415 * </div>
416 *
417 * @optional when the XRN Ant task jar file is not in Ant <code>lib</code> directory,
418 * put this jar into this classpath
419 */
420 public Object createURIResolverPath()
421 {
422 _uri_resolver_path = new Path(getProject());
423 return _uri_resolver_path;
424 }
425
426 /***
427 * Remembers the array of all XRN files implied in the process.
428 */
429 private File[] _xrn_file_array = null;
430
431 /***
432 * @return the raw array of all XRN files concerned and defined by the user
433 */
434 private File[] getXRNFiles()
435 {
436 if (_xrn_file_array == null)
437 {
438 if (_fileset_vector.size() > 0)
439 {
440 Vector file_vector = new Vector();
441
442 for (int index = 0; index < _fileset_vector.size(); index++)
443 {
444 FileSet file_set = (FileSet) _fileset_vector.elementAt(index);
445 File directory = file_set.getDir(getProject());
446 DirectoryScanner directory_scanner = file_set.getDirectoryScanner(getProject());
447 String[] included_file_array = directory_scanner.getIncludedFiles();
448 for (int file_index = 0; file_index < included_file_array.length; file_index++)
449 {
450 String file_name = included_file_array[file_index];
451 File file = new File(directory, file_name);
452 file_vector.add(file);
453 }
454 }
455 _xrn_file_array = new File[file_vector.size()];
456 file_vector.toArray(_xrn_file_array);
457 }
458 else
459 {
460 _xrn_file_array = new File[1];
461 _xrn_file_array[0] = _xml_release_notes_file;
462 }
463 }
464 return _xrn_file_array;
465 }
466
467 private File _destination_directory = new File(".");
468
469 /***
470 * <a name="destination"/>
471 * Defines the path of the directory where all generated stuff will be generated into.
472 *
473 * @optional <code>.</code> (i.e., the current Ant buildfile <code>basedir</code> directory)
474 */
475 public void setDestination(File destination_directory)
476 {
477 _destination_directory = destination_directory;
478 }
479
480 private String _html_release_notes_file_name = new String("XMLReleaseNotes.html");
481
482 /***
483 * Defines the name (<span class="warning">and not the path!</span>) of the main rendered HTML page.
484 * <div class="warningBlock">
485 * <p>
486 * This is the HTML page to be opened in order to browse the rendered HTML release notes.
487 * </p>
488 * This attribute is ignored if the <a href="#expandedRendering">expanded-rendering</a> feature is disabled.
489 * </div>
490 *
491 * @optional defaults to <code>XMLReleaseNotes.html</code>
492 */
493 public void setHTML(String html_release_notes_file_name)
494 {
495 _html_release_notes_file_name = html_release_notes_file_name;
496 }
497
498 private Boolean _stand_alone_html = new Boolean(true);
499
500 private Boolean _single_page = new Boolean(true);
501
502 /***
503 * <a name="singlePage"/>
504 * <div class="importantBlock">
505 * This attribute only makes sense when the inner <a href="#fileset"><code>fileset</code></a> element is not used.
506 * </div>
507 * Tells whether a single HTML page or multiple HTML pages should be generated for the component release notes.
508 * <p>
509 * This is handy when you want your rendered release notes to be encompassed in a single HTML file.
510 * </p>
511 * <div class="noteBlock">
512 * Read this <a href="#renderingFeature">section</a> for additional information.
513 * </div>
514 *
515 * @optional defaults to <code>yes</code>. When set to <code>yes</code>, only one HTML page is being generated for
516 * the release notes
517 */
518 public void setSinglePage(Boolean single_page)
519 {
520 _single_page = single_page;
521 }
522
523 private boolean useTheMultiplePagesXSLT()
524 {
525 return (_single_page.booleanValue() == false || _mutiple_xml_release_notes_file_defined == true);
526 }
527
528 private Boolean _use_icons = null;
529
530 /***
531 * <div class="warningBlock">
532 * This feature is still at its early stages and is still experimental.
533 * </div>
534 * Indicates whether the generated HTML pages should contains icons or not.
535 *
536 * @optional defaults to <code>no</code>
537 */
538 public void setUseIcons(Boolean use_icons)
539 {
540 _use_icons = use_icons;
541 }
542
543 /***
544 * The CSS file to be used within the generated HTML pages. This CSS must conform to the default CSS style sheet format.
545 */
546 private File _css_file = null;
547
548 /***
549 * <a name="css"/>
550 * Defines the path of the CSS file to be used within the generated HTML pages. This offers the opportunity to
551 * customize the rendered release notes layout. Copy and paste the default CSS, then modify its CSS selectors
552 * in order to customize the HTML rendering.
553 * <p>
554 * This file will be copied at the root of the <a href="#destination"><code>destination</code></a> directory with
555 * the name <code>stylesheet.css</code>.
556 * </p>
557 * <div class="note">
558 * This CSS must conform to the default CSS style sheet format.
559 * </div>
560 *
561 * @optional the default XRN CSS style sheet (present under <code>XMLReleaseNotes/XMLReleaseNotes.css</code>)
562 * embedded in the jar will be used
563 */
564 public void setCSS(File css_file)
565 {
566 _css_file = css_file;
567 }
568
569 private Boolean _inline_css = new Boolean(false);
570
571 /***
572 * Tells whether the generated HTML pages should inline the CSS content or not.
573 * <p>
574 * When enabled, the CSS will be embedded in the generated HTML, so that HTML the release notes is
575 * stand-alone (the CSS style sheet will not be copied in the <a href="#destination"><code>destination</code></a> directory).
576 * This is especially handy when you do not want to make a reference to another file inside the generated HTML.
577 * </p>
578 *
579 * @optional defaults to <code>no</code>. When set to <code>yes</code>, the generated page will embed the CSS content
580 */
581 public void setInlineCSS(Boolean inline_css)
582 {
583 _inline_css = inline_css;
584 }
585
586 private String _abstract_string = "";
587
588 /***
589 * Enables to define an abstract for the generated main HTML page, that will be displayed at the top of the release notes.
590 * <p>
591 * This is an opportunity to provide some overall explanation about the release notes themselves.
592 * </p>
593 * <div class="note">
594 * This element supports <a href="#supportedHTML">HTML tags</a>.
595 * </div>
596 *
597 * @optional empty, by default
598 */
599 public void setAbstract(String abstract_string)
600 {
601 _abstract_string = abstract_string;
602 }
603
604 private Boolean _check_xml_validity = new Boolean(false);
605
606 /***
607 * Indicates whether the XML input release notes should be tested against its XML schema.
608 * <p>
609 * This enables to check that your XRN release notes are compliant with the framework (and all the more that the
610 * file is well formed).
611 * </p>
612 *
613 * @optional defaults to <code>no</code>. When set to <code>yes</code>, the release notes files are being tested
614 * validated
615 */
616 public void setCheckValidity(Boolean check_validity)
617 {
618 _check_xml_validity = check_validity;
619 }
620
621 private Boolean _show_banner = new Boolean(true);
622
623 /***
624 * Tells whether the generated HTML pages should present an advertising footer banner for the XRN framework.
625 * <div class="noteBlock">
626 * If you want to promote the framework, you should consider letting this on.
627 * </div>
628 *
629 * @optional defaults to <code>yes</code>. When set to <code>no</code>, the XRN bottom banner is not displayed
630 */
631 public void setBanner(Boolean show_banner)
632 {
633 _show_banner = show_banner;
634 }
635
636 /***
637 * A class that enables to express HTML code.
638 */
639 public class HTML
640 {
641
642 protected String text = null;
643
644 public HTML(String text)
645 {
646 addText(text);
647 }
648
649 /***
650 * Convenience method in order to expand the inner text against the possible enclosed properties.
651 */
652 private String expand(String content)
653 {
654 return ProjectHelper.replaceProperties(project, content, project.getProperties());
655 }
656
657 public String getText()
658 {
659 return ((text != null) ? expand(text) : "");
660 }
661
662 public void addText(String text)
663 {
664 this.text = text;
665 }
666 }
667
668 private HTML _title = null;
669
670 /***
671 * <span class="important">Only one occurrence of this element is allowed.</span>
672 * <div class="warningBlock">
673 * This element is ignored if the <a href="#expandedRendering">expanded-rendering</a> feature is not enabled.
674 * </div>
675 * This title will be used on the HTML first page (the one that displays all components).
676 * <p>
677 * It enables to display extra personal information about the generated documentation.
678 * </p>
679 * <div class="note">
680 * This element supports <a href="#supportedHTML">HTML tags</a>.
681 * </div>
682 *
683 * @optional if not defined, no special title will be used
684 */
685 public Object createTitle()
686 {
687 _title = new HTML("");
688 return _title;
689 }
690
691 private HTML _header = null;
692
693 /***
694 * <span class="important">Only one occurrence of this element is allowed.</span>
695 * <div class="warningBlock">
696 * This element is ignored if the <a href="#expandedRendering">expanded-rendering</a> feature is not enabled.
697 * </div>
698 * This header will be displayed on multi-components HTML pages.
699 * <div class="note">
700 * This element supports <a href="#supportedHTML">HTML tags</a>.
701 * </div>
702 *
703 * @optional if not defined, no header will be inserted
704 */
705 public Object createHeader()
706 {
707 _header = new HTML("");
708 return _header;
709 }
710
711 private HTML _footer;
712
713 /***
714 * <span class="important">Only one occurrence of this element is allowed.</span>
715 * <div class="warningBlock">
716 * This element is ignored if the <a href="#expandedRendering">expanded-rendering</a> feature is not enabled.
717 * </div>
718 * This footer will be displayed on multi-components HTML pages.
719 * <div class="note">
720 * This element supports <a href="#supportedHTML">HTML tags</a>.
721 * </div>
722 *
723 * @optional if not defined, no footer will be inserted
724 */
725 public Object createFooter()
726 {
727 _footer = new HTML("");
728 return _footer;
729 }
730
731 private Boolean _deploy = new Boolean(false);
732
733 /***
734 * A boolean attribute that indicates whether the task just needs to deploy the XRN main resources:
735 * for instance, the default <a href="#css">CSS style sheet</a> will be copied,
736 * as well as the XRN <code>XMLReleaseNotes.xsd</code> schema file.
737 * <p>
738 * This is a helper that extracts all the necessary resources from the jar file. The deployed resources will be copied
739 * into the <a href="#destination"><code>destination</code></a> directory.
740 * </p>
741 * <div class="note">
742 * When enabled, no other processing than deploying the XRN framework main resources will be performed.
743 * </div>
744 *
745 * @optional defaults to <code>no</code>. When set to <code>yes</code>, the task will only deploy the XRN framework
746 * resources
747 */
748 public void setDeploy(Boolean deploy)
749 {
750 _deploy = deploy;
751 }
752
753 /***
754 * A class that enables to express an XSL transformation.
755 */
756 public class XSLT
757 {
758
759 private File xslFile = null;
760
761 /***
762 * The path of the XSLT file to be used.
763 *
764 * @param xsl_file the <code>.xsl</code> file that will be used for the transformation
765 */
766 public void setXSL(File xsl_file)
767 {
768 if (pluginName != null)
769 {
770 log("The XSL file is already defined via the 'plugin' attribute: " +
771 "the xsl (defined by the 'xsl' attribute) will not be taken into account", Project.MSG_WARN);
772 }
773 xslFile = xsl_file;
774 }
775
776 private String pluginName = null;
777
778 /***
779 * The name of the plugin to be used.
780 *
781 * @param plugin_name the name of the plugin within the jar that will be used
782 */
783 public void setPlugin(String plugin_name)
784 {
785 if (xslFile != null)
786 {
787 log("The XSL file is already defined via the 'xsl' attribute: " +
788 "the plugin name (defined by the 'plugin' attribute) will not be taken into account", Project.MSG_WARN);
789 }
790 else
791 {
792 pluginName = plugin_name;
793 }
794 }
795
796 /***
797 * @return the underling XSLT input stream
798 * @throws org.apache.tools.ant.BuildException if the input cannot be found
799 */
800 public InputStream getXSLTStream()
801 throws BuildException
802 {
803 InputStream input_stream = null;
804 String location = null;
805 if (xslFile != null)
806 {
807 input_stream = getInputStream(xslFile);
808 }
809 else
810 {
811 location = plugin_common_prefix + pluginName + ".xsl";
812 input_stream = getClass().getClassLoader().getResourceAsStream(location);
813 }
814 checkInputStream(input_stream, getXSLTLocation());
815 return input_stream;
816 }
817
818 /***
819 * @return the underling XSLT input stream
820 * @throws org.apache.tools.ant.BuildException if the input cannot be found
821 */
822 public String getXSLTLocation()
823 throws BuildException
824 {
825 if (xslFile != null)
826 {
827 return xslFile.getAbsolutePath();
828 }
829 else
830 {
831 return plugin_common_prefix + pluginName + ".xsl";
832 }
833 }
834
835 private XMLCatalog uriResolver = null;
836
837 /***
838 * Acts as an URI resolver concerning the possible XSL transformation. When some URI resolution is needed during
839 * this transformation, the task will use this catalog (see the Ant
840 * <a href="http://ant.apache.org/manual/CoreTypes/xmlcatalog.html">XMLCatalog</a> for more information).
841 *
842 * @param xml_catalog the catalog that will be used during the XSL transformation, if necessary
843 * @optional the default XSLT implementation URI resolver is used
844 */
845 public void addXMLCatalog(XMLCatalog xml_catalog)
846 {
847 uriResolver = xml_catalog;
848 }
849
850 /***
851 * Copied from the {@link org.apache.tools.ant.taskdefs.XSLTProcess.Param} class: acts as a parameter wrapper
852 */
853 public class Param
854 {
855
856 public void setName(String name)
857 {
858 this.name = name;
859 }
860
861 public void setExpression(String expression)
862 {
863 this.expression = expression;
864 }
865
866 public String getName()
867 throws BuildException
868 {
869 if (name == null)
870 throw new BuildException("Name attribute is missing.");
871 else
872 return name;
873 }
874
875 public String getExpression()
876 throws BuildException
877 {
878 if (expression == null)
879 throw new BuildException("Expression attribute is missing.");
880 else
881 return expression;
882 }
883
884 private String name;
885 private String expression;
886
887 public Param()
888 {
889 name = null;
890 expression = null;
891 }
892 }
893
894 private Vector parameterVector = new Vector();
895
896 /***
897 * Enables to add XSLT additional parameters (see the Ant
898 * <a href="http://ant.apache.org/manual/CoreTypes/xslt.html">XSLT</a> task for more information).
899 *
900 * @return the parameters (and their value) that will be provided to the XSL transformation
901 * @optional no parameters other than the ones defined in the default XSLT layout transformation
902 */
903 public Param createParam()
904 {
905 Param parameters = new Param();
906 parameterVector.add(parameters);
907 return parameters;
908 }
909
910 }
911
912 /***
913 * The vector of all user-specified model XSLT.
914 */
915 private Vector _model_xslt_vector = new Vector();
916
917 /***
918 * <a name="model"/>
919 * <span class="important">As many occurrences of this element are allowed.</span>
920 * Defines a additional XSL transformations to be applied on the XML input. The result of the transformation should
921 * be some XRN-compliant XML file.
922 * The XSL transformations will be applied in the same order as they are declared, from top to bottom.
923 * <div class="noteBlock">
924 * Read <a href="#xsltResource">this explanation</a> in order to understand how to express the XSLT that should be used.
925 * </div>
926 * <p>
927 * This enables to provide multiple XSLT that aggregate information coming from another XML sources. This is part
928 * of the <a href="#enrichment">enrichment</a> step.
929 * </p>
930 * <p>
931 * If not defined, no preliminary XSL transformation is performed on the XML input release notes files;
932 * the <a href="#layout"><code>layout</code></a> parameter is also ignored in this case.
933 * </p>
934 * <div class="noteBlock">
935 * This element supports the <a href="#xmlcatalog"><code>XMLCatalog</code></a> inner XML element, that acts as a
936 * URI resolver. Follow the previous link in order to understand what this is about.
937 * </div>
938 *
939 * @optional
940 */
941 public Object createModel()
942 {
943 XSLT xslt = new XSLT();
944 _model_xslt_vector.add(xslt);
945 return xslt;
946 }
947
948 private XSLT _layout_xslt = null;
949
950 /***
951 * <a name="layout"/>
952 * <span class="important">Only one occurrence of this element is allowed.</span>
953 * Defines the XSL transformation that renders into HTML the XML release notes input.
954 * <p>
955 * The idea is to provide an XSLT that extends the <code>XMLReleaseNotes2HTML.xsl</code> default rendering so as to
956 * take into account the new model information present in the input XML. This XSL will be given the same parameters
957 * as the default ones. This is part of the <a href="#layout">layout</a> step.
958 * </p>
959 * <div class="noteBlock">
960 * Read <a href="#xsltResource">this explanation</a> in order to understand how to express the XSLT that should be used.
961 * </div>
962 * <p>
963 * If not defined, the usual XSL transformation renderer is used.
964 * </p>
965 * <div class="noteBlock">
966 * This element supports the <a href="#xmlcatalog"><code>XMLCatalog</code></a> inner XML element, that acts as a
967 * URI resolver. Follow the previous link in order to understand what this is about.
968 * </div>
969 *
970 * @optional
971 */
972 public Object createLayout()
973 {
974 _layout_xslt = new XSLT();
975 return _layout_xslt;
976 }
977
978 private XSLT _plugin_layout_xslt = null;
979
980 /***
981 * <a name="pluginLayout"/>
982 * <span class="important">Only one occurrence of this element is allowed.</span>
983 * <div class="warningBlock">
984 * This element is ignored if the <a href="#expandedRendering">expanded-rendering</a> feature is not enabled.
985 * </div>
986 * Defines the XSLT layout plugin that should be used when it comes to the rendering into HTML.
987 * <p>
988 * The idea is to provide an XSLT that extends the <code>XRN2HTMLPlugin.xsl</code> default rendering.
989 * </p>
990 * <div class="noteBlock">
991 * Read <a href="#xsltResource">this explanation</a> in order to understand how to express the XSLT that should be used.
992 * </div>
993 * <p>
994 * This XSL will be given the same parameters as the default ones.
995 * </p>
996 * <div class="noteBlock">
997 * This element supports the <a href="#xmlcatalog"><code>XMLCatalog</code></a> inner XML element, that acts as a
998 * URI resolver. Follow the previous link in order to understand what this is about.
999 * </div>
1000 *
1001 * @optional
1002 */
1003 public Object createPluginLayout()
1004 {
1005 _plugin_layout_xslt = new XSLT();
1006 return _plugin_layout_xslt;
1007 }
1008
1009 private EmailTask _mail = null;
1010
1011 /***
1012 * <span class="important">Only one occurrence of this element is allowed.</span>
1013 * Enables to send an e-mail at the end of the XSL layout final transformation.
1014 * <p>
1015 * If not defined, no e-mail will be sent. Otherwise, use this usual <code>mail</code> built-in Ant task
1016 * in order to define the e-mail inputs.
1017 * </p>
1018 * <div class="important">
1019 * <a href="#mail">Read this</a> some more explanation.
1020 * </div>
1021 *
1022 * @optional if not defined, no e-mail will be sent. Otherwise, use this usual <code>mail</code> built-in Ant task
1023 * in order to define the e-mail inputs. See <a href="#mail">the explaining at the top</a>
1024 */
1025 public Object createMail()
1026 {
1027 _mail = new EmailTask();
1028 return _mail;
1029 }
1030
1031 private Boolean _migrate = new Boolean(false);
1032
1033 /***
1034 * <a name="migrate"/>
1035 * Indicates that you want to perform a migration of the XRN input release notes files from their declared XRN framework version
1036 * into the current XRN version.
1037 * <span class="important">In that case, the release notes files must be writable, since they may be overwritten.</span>
1038 * <p>
1039 * For every XRN release note file concerned, the task will attempt to perform the necessary migration from the
1040 * version of the XRN declared via the <code>ReleaseNotes/@version</code> attribute, into the version of the present XRN task.
1041 * </p>
1042 * <div class="warningBlock">
1043 * When enabled, the rest of the standard processing is not performed.
1044 * </div>
1045 *
1046 * @optional defaults to <code>no</code>. When set to <code>yes</code>, if a migration is necessary, it is performed
1047 */
1048 public void setMigrate(Boolean migrate)
1049 {
1050 _migrate = migrate;
1051 }
1052
1053 private Boolean _implicit_migration = new Boolean(false);
1054
1055 /***
1056 * Indicates whether implicit XRN migration should be applied if necessary. This does not stop the process once the
1057 * implicit migration has been applied.
1058 * <p>
1059 * When your XRN release notes files are outdated compared to the XRN version of the Ant XRN task that you are
1060 * currently using, you can ask the task to first migrate (via the integrated <a href="#migrate">migration</a> feature)
1061 * your release notes, and then only, continue the standard process.
1062 * </p>
1063 *
1064 * @optional defaults to <code>no</code>. When set to <code>yes</code> implicit migration is performed
1065 */
1066 public void setImplicitlyMigrate(Boolean implicit_migration)
1067 {
1068 _implicit_migration = implicit_migration;
1069 }
1070
1071 private Boolean _debug = new Boolean(false);
1072
1073 /***
1074 * For internal purpose only. Triggers the debug behavior.
1075 * <div class="warningBlock">
1076 * This feature is not supposed to be used by persons not developing for the XRN framework, unless explicetly asked for.
1077 * </div>
1078 *
1079 * @optional default to <code>no</code>. Set it to <code>yes</code> only provided you know what you are doing!
1080 */
1081 public void setDebug(Boolean debug)
1082 {
1083 _debug = debug;
1084 }
1085
1086 /***
1087 * Checks that the provided parameters are OK.
1088 *
1089 * @throws org.apache.tools.ant.BuildException if some of them will result in a fatal error
1090 */
1091 private void checkParameters()
1092 throws BuildException
1093 {
1094 if (_show_banner.booleanValue() == false)
1095 {
1096 log("------------<(-: Is that sure that you really want to disable the banner? ;->>------------", Project.MSG_WARN);
1097 }
1098 if (_deploy.booleanValue() == false)
1099 {
1100 File[] xrn_file_array = getXRNFiles();
1101 for (int index = 0; index < xrn_file_array.length; index++)
1102 {
1103 File xrn_file = xrn_file_array[index];
1104 if (xrn_file.isFile() == false)
1105 {
1106 throw new BuildException("There is no XML release notes file '" + xrn_file.getAbsolutePath() + "': " +
1107 "cannot generate the release notes layout");
1108 }
1109 }
1110 }
1111 for (int index = 0; index < _model_xslt_vector.size(); index++)
1112 {
1113 XSLT xslt = (XSLT) _model_xslt_vector.elementAt(index);
1114 if (xslt != null)
1115 {
1116 if (xslt.xslFile == null && xslt.pluginName == null)
1117 {
1118 throw new BuildException("The 'xsl' and 'plugin' attributes of the 'model' inner element cannot both be null");
1119 }
1120 }
1121 }
1122 if (_layout_xslt != null)
1123 {
1124 if (_layout_xslt.xslFile == null)
1125 {
1126 throw new BuildException("The 'xsl' attribute of the 'layout' inner element cannot be null");
1127 }
1128 }
1129 if (_mail != null)
1130 {
1131 if (_inline_css.booleanValue() == false)
1132 {
1133 log("Beware, you chose to send an e-mail with the release notes, but you did not chose to inline the CSS " +
1134 "content via the 'inlineCSS' parameter, so that the e-mail HTML content will miss it!", Project.MSG_WARN);
1135 }
1136 }
1137 }
1138
1139 /***
1140 * @return the XRN version this element belongs to
1141 */
1142 public String getXRNVersion()
1143 {
1144 return "@XRN_VERSION@";
1145 }
1146
1147 public void execute()
1148 throws BuildException
1149 {
1150 log("Running XRN V" + getXRNVersion(), Project.MSG_VERBOSE);
1151
1152 checkParameters();
1153
1154 logRuntimeInformation();
1155
1156 if (_deploy.booleanValue() == false)
1157 {
1158 if (_migrate.booleanValue() == true)
1159 {
1160 performMigrationIfNecessary(true);
1161 return;
1162 }
1163 if (_implicit_migration.booleanValue() == true)
1164 {
1165 performMigrationIfNecessary(false);
1166 }
1167 if (_check_xml_validity.booleanValue() == true)
1168 {
1169 checkXMLValidity();
1170 }
1171 generateReleaseNotes();
1172
1173 if (_mail != null)
1174 {
1175 sendMail();
1176 }
1177 }
1178 else
1179 {
1180 deploy();
1181 }
1182 }
1183
1184 /***
1185 * Logs the current runtime environment, so that it is possible to debug more easily the task, especially as far
1186 * as XSLT is concerned.
1187 */
1188 protected void logRuntimeInformation()
1189 {
1190 log("The 'java.class.path' is currently set to '" + System.getProperty("java.class.path", "Error") + "'", Project.MSG_DEBUG);
1191 }
1192
1193 /***
1194 * Scans all XRN files and perform a migration on them if necessary.
1195 *
1196 * @param in_situ indicates whether the XRN files should be migrated in situ, or in a temporary file
1197 * @see #performMigrationIfNecessaryForXRNFile
1198 */
1199 private void performMigrationIfNecessary(boolean in_situ)
1200 throws BuildException
1201 {
1202 File[] xrn_file_array = getXRNFiles();
1203 if (in_situ == true)
1204 {
1205 for (int index = 0; index < xrn_file_array.length; index++)
1206 {
1207 File xrn_file = xrn_file_array[index];
1208 performMigrationIfNecessaryForXRNFile(xrn_file, xrn_file);
1209 }
1210 }
1211 else
1212 {
1213 for (int index = 0; index < xrn_file_array.length; index++)
1214 {
1215 File xrn_file = xrn_file_array[index];
1216 File temporary_file = null;
1217 try
1218 {
1219 temporary_file = File.createTempFile("XRNMigrated", ".xml");
1220 performMigrationIfNecessaryForXRNFile(xrn_file, temporary_file);
1221 log("You can view the migrated XRN file '" + xrn_file.getAbsolutePath() + "' in the file '" +
1222 temporary_file.getAbsolutePath() + "'", Project.MSG_INFO);
1223 _xrn_file_array[index] = temporary_file;
1224 }
1225 catch (IOException io_exception)
1226 {
1227 throw new BuildException("Cannot create a temporary file in order to store the migrated XRN file '" +
1228 xrn_file.getAbsolutePath() + "': cannot continue");
1229 }
1230 }
1231 }
1232 }
1233
1234 /***
1235 * Checks whether a migration is required on the XML release notes file, and then performs it if necessary.
1236 *
1237 * @param xrn_file the XRN file than may be migrated
1238 * @param output_file the file the migrated XRN file will be saved into
1239 * @throws org.apache.tools.ant.BuildException if the migration failed for any reason
1240 */
1241 private void performMigrationIfNecessaryForXRNFile(File xrn_file, File output_file)
1242 throws BuildException
1243 {
1244 log("You asked that a migration of the XML release notes file '" + xrn_file.getAbsolutePath() + "' be " +
1245 "performed if necessary", Project.MSG_INFO);
1246 Document document = getXMLDocumentFromXRNFile(xrn_file);
1247 String version = "0.3.2";
1248 if (document.getDocumentElement().hasAttribute("version") == true)
1249 {
1250 version = document.getDocumentElement().getAttribute("version");
1251 if (version.equals(getXRNVersion()) == true)
1252 {
1253 log("No XML file migration is required", Project.MSG_INFO);
1254 return;
1255 }
1256 }
1257
1258 log("Some migration is required, since the version of the XML release notes file is '" + version + "' " +
1259 "and that the current XRN version is '" + getXRNVersion() + "'", Project.MSG_INFO);
1260 runNecessaryMigrationXSLT(xrn_file, output_file, version);
1261 }
1262
1263 /***
1264 * Gets the DOM document of the provided XRN file.
1265 *
1266 * @param xrn_file the XRN file the DOM is being asked for
1267 * @return the DOM of the XML file
1268 * @throws org.apache.tools.ant.BuildException if the file is not XML-well-formed, or if it cannot be parsed
1269 */
1270 private Document getXMLDocumentFromXRNFile(File xrn_file)
1271 throws BuildException
1272 {
1273 DocumentBuilder document_builder = createADocumentBuilder();
1274 Document document = null;
1275 try
1276 {
1277 document = document_builder.parse(xrn_file);
1278 }
1279 catch (SAXException sax_exception)
1280 {
1281 throw new BuildException("Cannot parse properly the file '" + xrn_file.getAbsolutePath() + "', " +
1282 "which is supposed to be under the XML format");
1283 }
1284 catch (IOException io_exception)
1285 {
1286 throw new BuildException("Error while reading the file '" + xrn_file.getAbsolutePath() + "'");
1287 }
1288 return document;
1289 }
1290
1291 /***
1292 * @return a newly created document builder
1293 * @throws org.apache.tools.ant.BuildException if the runtime cannot create such a builder
1294 */
1295 private DocumentBuilder createADocumentBuilder()
1296 throws BuildException
1297 {
1298 DocumentBuilder document_builder = null;
1299 try
1300 {
1301 DocumentBuilderFactory document_builder_factory = DocumentBuilderFactory.newInstance();
1302 document_builder_factory.setNamespaceAware(true);
1303
1304 document_builder_factory.setValidating(false);
1305 document_builder = document_builder_factory.newDocumentBuilder();
1306 }
1307 catch (ParserConfigurationException parser_configuration_exception)
1308 {
1309 throw new BuildException("Cannot create an XML document builder", parser_configuration_exception);
1310 }
1311 catch (FactoryConfigurationError factory_configuration_error)
1312 {
1313 throw new BuildException("Cannot create an XML document builder", factory_configuration_error);
1314 }
1315 return document_builder;
1316 }
1317
1318 /***
1319 * @param version_string the version string to be analyzed
1320 * @return a version-increasing number that so that it should be greater with a later version compared to a earlier version
1321 */
1322 private long getVersionAsNumber(String version_string)
1323 {
1324 long version = 0;
1325 StringTokenizer tokenizer = new StringTokenizer(version_string, ".");
1326 while (tokenizer.hasMoreElements() == true)
1327 {
1328 version *= 1000;
1329 version += new Integer((String) tokenizer.nextElement()).intValue();
1330 }
1331 return version;
1332 }
1333
1334 /***
1335 * The method that actually performs all necessary migrations on the XML release notes file.
1336 *
1337 * @param xrn_file the XRN file the migration should be applied to
1338 * @param output_file the file the migrated XRN file will be saved into
1339 * @param original_version the original XRN version of the file
1340 */
1341 private void runNecessaryMigrationXSLT(File xrn_file, File output_file, String original_version)
1342 throws BuildException
1343 {
1344 InputStream xml_input_stream = null;
1345 Vector input_stream_vector = new Vector();
1346 try
1347 {
1348 xml_input_stream = new FileInputStream(xrn_file);
1349 input_stream_vector.add(xml_input_stream);
1350 }
1351 catch (FileNotFoundException file_not_found_exception)
1352 {
1353
1354 throw new BuildException("Cannot find the file '" + xrn_file + "'", file_not_found_exception);
1355 }
1356 File temporary_file = null;
1357 Vector temporary_file_vector = new Vector();
1358 long original_version_number = getVersionAsNumber(original_version);
1359 try
1360 {
1361 if (original_version_number < getVersionAsNumber("0.3.3"))
1362 {
1363 temporary_file = File.createTempFile("XRNMigration", ".xml");
1364 temporary_file_vector.add(temporary_file);
1365 runMigration("0.3.3", "XMLReleaseNotesConvertorV0.3.2-0.3.3.xsl", xml_input_stream, new FileOutputStream(temporary_file));
1366 xml_input_stream = new FileInputStream(temporary_file);
1367 input_stream_vector.add(xml_input_stream);
1368 }
1369 if (original_version_number < getVersionAsNumber("0.6.2"))
1370 {
1371 temporary_file = File.createTempFile("XRNMigration", ".xml");
1372 temporary_file_vector.add(temporary_file);
1373 runMigration("0.6.2", "XMLReleaseNotesConvertorV0.6.1-0.6.2.xsl", xml_input_stream, new FileOutputStream(temporary_file));
1374 xml_input_stream = new FileInputStream(temporary_file);
1375 input_stream_vector.add(xml_input_stream);
1376 }
1377 if (original_version_number < getVersionAsNumber("0.6.4"))
1378 {
1379 temporary_file = File.createTempFile("XRNMigration", ".xml");
1380 temporary_file_vector.add(temporary_file);
1381 runMigration("0.6.4", "XMLReleaseNotesConvertorV0.6.2-0.6.4.xsl", xml_input_stream, new FileOutputStream(temporary_file));
1382 xml_input_stream = new FileInputStream(temporary_file);
1383 input_stream_vector.add(xml_input_stream);
1384 }
1385 if (original_version_number < getVersionAsNumber("0.14.0"))
1386 {
1387 temporary_file = File.createTempFile("XRNMigration", ".xml");
1388 temporary_file_vector.add(temporary_file);
1389 runMigration("0.14.0", "XMLReleaseNotesConvertorV0.13.0-0.14.0.xsl", xml_input_stream, new FileOutputStream(temporary_file));
1390 xml_input_stream = new FileInputStream(temporary_file);
1391 input_stream_vector.add(xml_input_stream);
1392 }
1393 if (original_version_number < getVersionAsNumber("0.18.0"))
1394 {
1395 temporary_file = File.createTempFile("XRNMigration", ".xml");
1396 temporary_file_vector.add(temporary_file);
1397 runMigration("0.18.0", "XMLReleaseNotesConvertorV0.17.3-0.18.0.xsl", xml_input_stream, new FileOutputStream(temporary_file));
1398 xml_input_stream = new FileInputStream(temporary_file);
1399 input_stream_vector.add(xml_input_stream);
1400 }
1401
1402 if (temporary_file == null)
1403 {
1404 log("No migration is available and necessary from XRN V" + original_version + " to V" + getXRNVersion(), Project.MSG_INFO);
1405 }
1406 else
1407 {
1408
1409 FileOutputStream file_output_stream = new FileOutputStream(output_file);
1410 PrintWriter print_writer = new PrintWriter(file_output_stream);
1411 xml_input_stream = new FileInputStream(temporary_file);
1412 input_stream_vector.add(xml_input_stream);
1413 InputStreamReader input_stream_reader = new InputStreamReader(xml_input_stream);
1414 BufferedReader buffered_reader = new BufferedReader(input_stream_reader);
1415 String read_line = null;
1416 try
1417 {
1418 while ((read_line = buffered_reader.readLine()) != null)
1419 {
1420 print_writer.println(read_line);
1421 }
1422 }
1423 catch (IOException io_exception)
1424 {
1425
1426 }
1427
1428 finally
1429 {
1430 print_writer.flush();
1431 print_writer.close();
1432 file_output_stream.flush();
1433 file_output_stream.close();
1434 }
1435 }
1436 }
1437 catch (IOException io_exception)
1438 {
1439 throw new BuildException("Internal error while performing the XRN migration", io_exception);
1440 }
1441 finally
1442 {
1443
1444 for (int index = 0; index < temporary_file_vector.size(); index++)
1445 {
1446 File file = (File) temporary_file_vector.elementAt(index);
1447 file.delete();
1448 }
1449
1450 for (int index = 0; index < input_stream_vector.size(); index++)
1451 {
1452 InputStream input_stream = (InputStream) input_stream_vector.elementAt(index);
1453 try
1454 {
1455 input_stream.close();
1456 }
1457 catch (IOException io_exception)
1458 {
1459
1460 }
1461 }
1462 }
1463 }
1464
1465 /***
1466 * Performs an XSL transformation on the provided input stream and outputs the result into the provided output stream.
1467 *
1468 * @param target_version the version the XSL is supposed to XRN-migrate to
1469 * @param migration_xsl_file_name the name of the XSL file that enables this migration (and which should be present
1470 * in the jar file, under <code>XMLReleaseNotes</code>)
1471 * @param xml_input_stream
1472 * @param output_stream
1473 */
1474 private void runMigration(String target_version, String migration_xsl_file_name, InputStream xml_input_stream, OutputStream output_stream)
1475 {
1476 final String migration_common_path = common_prefix + "migration/";
1477 String xsl_resource_name = migration_common_path + migration_xsl_file_name;
1478 log("Migrating the XML release notes file into version '" + target_version + "' via the XSL resource '" +
1479 xsl_resource_name + "'", Project.MSG_INFO);
1480 InputStream xslt_input_stream = getClass().getClassLoader().getResourceAsStream(xsl_resource_name);
1481 if (xslt_input_stream == null)
1482 {
1483 throw new BuildException("The XRN jar file seems to be corrupted since it cannot find the resource '" +
1484 xsl_resource_name + "'");
1485 }
1486
1487 String public_id = "XMLReleaseNotesConvertorCommon.xsl";
1488 String location = migration_common_path + "XMLReleaseNotesConvertorCommon.xsl";
1489 XMLCatalog xml_catalog = createCatalog(public_id, location);
1490 Transformer transformer = createTransformerFromInput(xslt_input_stream, xml_catalog);
1491 StreamResult stream_result = new StreamResult(output_stream);
1492 StreamSource stream_source = new StreamSource(xml_input_stream);
1493 try
1494 {
1495 transformer.transform(stream_source, stream_result);
1496 }
1497 catch (TransformerException transformer_exception)
1498 {
1499 log("Internal error during the XSLT transformation with the XSL resource '" +
1500 xsl_resource_name + "' when migrating to version '" + target_version + "'", Project.MSG_ERR);
1501 throw new BuildException(transformer_exception);
1502 }
1503 }
1504
1505 /***
1506 * Tests the XML-validity of the XRN files.
1507 *
1508 * @see #checkXMLValidityForXRNFile
1509 */
1510 private void checkXMLValidity()
1511 throws BuildException
1512 {
1513 File[] xrn_file_array = getXRNFiles();
1514 for (int index = 0; index < xrn_file_array.length; index++)
1515 {
1516 File xrn_file = xrn_file_array[index];
1517 checkXMLValidityForXRNFile(xrn_file);
1518 }
1519 }
1520
1521 /***
1522 * Checks the XML-validity of the input XML release notes file, agains its XSD.
1523 *
1524 * @throws org.apache.tools.ant.BuildException if the file is not well-formed, nor valid, or if an exception occured during the
1525 * validation
1526 */
1527 private void checkXMLValidityForXRNFile(final File xrn_file)
1528 throws BuildException
1529 {
1530 log("Checking the validity of the XML release notes file '" + xrn_file.getAbsolutePath() + "'", Project.MSG_INFO);
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720 log("The XML release notes file '" + xrn_file.getAbsolutePath() + "' is valid against its schema", Project.MSG_INFO);
1721 }
1722
1723 /***
1724 * The method creates an XML catalog, useful for URI resolution during XSLT.
1725 *
1726 * @return a newly created XML catalog, properly set
1727 * @throws org.apache.tools.ant.BuildException if there were a problem
1728 * @see #createCatalog(java.lang.String, java.lang.String)
1729 */
1730 private XMLCatalog createCatalog()
1731 throws BuildException
1732 {
1733 XMLCatalog xml_catalog = new XMLCatalog();
1734 xml_catalog.setProject(getProject());
1735 if (_uri_resolver_path != null)
1736 {
1737 xml_catalog.setClasspath(_uri_resolver_path);
1738 }
1739 return xml_catalog;
1740 }
1741
1742 /***
1743 * The method creates an XML catalog, useful for URI resolution during XSLT, with already one URI resolution added.
1744 *
1745 * @param public_id the public id of the URI to be resolved
1746 * @param location the actual location of the URI when being resolved
1747 * @return a newly created XML catalog, properly set
1748 * @throws org.apache.tools.ant.BuildException if there were a problem
1749 * @see #addCatalogURI
1750 */
1751 private XMLCatalog createCatalog(String public_id, String location)
1752 throws BuildException
1753 {
1754 XMLCatalog xml_catalog = createCatalog();
1755 addCatalogURI(xml_catalog, public_id, location);
1756 return xml_catalog;
1757 }
1758
1759 /***
1760 * @return if the dectected Ant runtime version is < 1.6.0
1761 */
1762 private boolean isAntInferiorTo160()
1763 {
1764 try
1765 {
1766 Class resouce_location_class = Class.forName("org.apache.tools.ant.loader.AntClassLoader2");
1767 return false;
1768 }
1769 catch (ClassNotFoundException class_not_found_exception)
1770 {
1771
1772 return true;
1773 }
1774 }
1775
1776 /***
1777 * Created because of incompabilities issues between Ant V1.5 and Ant V1.6+.
1778 * <p>
1779 * The method just adds an URI resolution to the provided catalog.
1780 * </p>
1781 *
1782 * @param xml_catalog the catalog a new resolution should be added to
1783 * @param public_id the public id of the URI to be resolved
1784 * @param location the actual location of the URI when being resolved
1785 * @throws org.apache.tools.ant.BuildException if there were a problem
1786 */
1787 private void addCatalogURI(XMLCatalog xml_catalog, String public_id, String location)
1788 {
1789 boolean location_set = false;
1790 Vector class_vector = new Vector();
1791 try
1792 {
1793 Class resouce_location_class = Class.forName("org.apache.tools.ant.types.ResourceLocation");
1794
1795 class_vector.add(resouce_location_class);
1796 }
1797 catch (ClassNotFoundException class_not_found_exception)
1798 {
1799
1800 }
1801 try
1802 {
1803 Class resouce_location_class = Class.forName("org.apache.tools.ant.types.DTDLocation");
1804
1805 class_vector.add(resouce_location_class);
1806 }
1807 catch (ClassNotFoundException class_not_found_exception)
1808 {
1809
1810 }
1811 Exception potential_exception = null;
1812 for (int index = 0; index < class_vector.size(); index++)
1813 {
1814 Class resouce_location_class = (Class) class_vector.elementAt(index);
1815 try
1816 {
1817 Object resource_location = resouce_location_class.newInstance();
1818 resouce_location_class.getMethod("setPublicId", new Class[]{String.class}).invoke(resource_location, new Object[]{public_id});
1819 resouce_location_class.getMethod("setLocation", new Class[]{String.class}).invoke(resource_location, new Object[]{location});
1820 xml_catalog.getClass().getMethod("addEntity", new Class[]{resouce_location_class}).invoke(xml_catalog, new Object[]{resource_location});
1821 }
1822 catch (Exception exception)
1823 {
1824 potential_exception = exception;
1825 log("The seems to be some problem with the 'XMLCatalog'", Project.MSG_WARN);
1826 }
1827 location_set = true;
1828 break;
1829 }
1830 if (class_vector.size() == 0)
1831 {
1832 throw new BuildException("Cannot set properly an XMLCatalog, which enables to resolve URIs");
1833 }
1834 if (location_set == false)
1835 {
1836 throw new BuildException("Cannot set properly an XMLCatalog, which enables to resolve URIs", potential_exception);
1837 }
1838 log("URIResolver: the public id '" + public_id + "' will be resolved as '" + location + "'", Project.MSG_VERBOSE);
1839 }
1840
1841 /***
1842 * The prefix where all public resources are present within the jar.
1843 */
1844 private final static String common_prefix = "XMLReleaseNotes/";
1845
1846 /***
1847 * The common location of all plugin XSLTs.
1848 */
1849 private final static String plugin_common_prefix = common_prefix + "plugin/";
1850
1851 /***
1852 * The part that just deploys the framework resources.
1853 */
1854 private void deploy()
1855 {
1856 log("You chose to deploy the XMLReleaseNotes framework into directory '" +
1857 _destination_directory.getAbsolutePath() + "'", Project.MSG_INFO);
1858 final String[] file_name_array = {common_prefix + _simple_xsl, common_prefix + "XMLReleaseNotesCommon.xsl",
1859 common_prefix + "XMLReleaseNotes.xsd",
1860 common_prefix + "XMLActors.xsd", common_prefix + "XMLReleaseNotes.css"};
1861 for (int index = 0; index < file_name_array.length; index++)
1862 {
1863 String file_name = file_name_array[index];
1864 File original_copied_file = new File(_destination_directory, file_name);
1865 File copied_file = new File(_destination_directory, original_copied_file.getName());
1866 if (JarHelper.copyFileFromJar(file_name, null, copied_file) == false)
1867 {
1868 throw new BuildException("Cannot copy the file present in the jar under '" + file_name +
1869 "' into '" + copied_file.getAbsolutePath() + "'");
1870 }
1871 else
1872 {
1873 log("Copied file from the jar under '" + file_name + "' into file '" + copied_file.getAbsolutePath() + "'", Project.MSG_VERBOSE);
1874 }
1875 }
1876 }
1877
1878 /***
1879 * Sends the final e-mail.
1880 */
1881 private void sendMail()
1882 {
1883 log("An e-mail containing the generated release notes is bound to be sent: hope that you haved made the HTML inline " +
1884 "via the 'inlineCSS' parameter...", Project.MSG_INFO);
1885 _mail.setProject(getProject());
1886 _mail.setMessageFile(getMainOutputFile());
1887 _mail.setMessageMimeType("text/html");
1888 _mail.execute();
1889 }
1890
1891 /***
1892 * The part of the task that actually generates the HTML release notes.
1893 */
1894 private void generateReleaseNotes()
1895 {
1896 log("You chose to generate the HTML release notes", Project.MSG_VERBOSE);
1897
1898 prepareEnvironment();
1899
1900 generateXMLReleaseNotes();
1901 }
1902
1903 /***
1904 * The name of the target CSS copied into the destination directory.
1905 */
1906 final static String copied_css_file_name = "stylesheet.css";
1907
1908 /***
1909 * This method creates the environment so that the execution of the XSL transformation is OK and does not have
1910 * to bother about those details.
1911 * <p>
1912 * The method does the followingt things:
1913 * <ol>
1914 * <li>create the destionation directory if necessary,</li>
1915 * <li>copy the CSS file into the destination directory.</li>
1916 * </ol>
1917 * </p>
1918 */
1919 private void prepareEnvironment()
1920 throws BuildException
1921 {
1922
1923 if (_destination_directory.isDirectory() == false)
1924 {
1925 if (_destination_directory.isFile() == true)
1926 {
1927 throw new BuildException("Cannot use the file '" + _destination_directory.getAbsolutePath() + "' as a destination " +
1928 "directory");
1929 }
1930 if (_destination_directory.mkdirs() == true)
1931 {
1932 log("Destination directory '" + _destination_directory.getAbsolutePath() + "' created", Project.MSG_VERBOSE);
1933 }
1934 }
1935 log("The HTML stuff will be generated into the directory '" + _destination_directory.getAbsolutePath() + "'", Project.MSG_INFO);
1936
1937 if (_css_file == null)
1938 {
1939 if (JarHelper.copyFileFromJar(common_prefix + "XMLReleaseNotes.css", null, getDestinationCSSFile()) == false)
1940 {
1941 log("Cannot create the Cascading Style Sheet (CSS) file '" +
1942 getDestinationCSSFile().getAbsolutePath() + "': the CSS will miss", Project.MSG_WARN);
1943 return;
1944 }
1945 log("The CSS file used will be the default one", Project.MSG_VERBOSE);
1946 }
1947 else
1948 {
1949 final String aftermaths = "the default one will be used instead";
1950
1951 try
1952 {
1953 FileInputStream file_input_stream = new FileInputStream(_css_file);
1954 if (JarHelper.copyInputStreamIntoFile(file_input_stream, getDestinationCSSFile()) == true)
1955 {
1956 log("The CSS file used will be '" + _css_file.getAbsolutePath() + "'", Project.MSG_VERBOSE);
1957 }
1958 else
1959 {
1960 log("The CSS file provided '" + _css_file.getAbsolutePath() + "' could not be copied: " +
1961 aftermaths, Project.MSG_WARN);
1962 }
1963 }
1964 catch (FileNotFoundException file_not_found_exception)
1965 {
1966 log("The CSS file provided '" + _css_file.getAbsolutePath() + "' does not exist: " +
1967 aftermaths, Project.MSG_WARN);
1968 }
1969 }
1970 if (_use_icons != null && _use_icons.booleanValue() == true)
1971 {
1972 final String[] icon_list = {"bug.png", "tech.png", "actor.png"};
1973 for (int index = 0; index < icon_list.length; index++)
1974 {
1975 String file_name = icon_list[index];
1976 if (JarHelper.copyFileFromJar(common_prefix + "image/" + file_name, null, new File(_destination_directory, file_name)) == false)
1977 {
1978 log("Cannot create the icons", Project.MSG_WARN);
1979 }
1980 }
1981 }
1982 }
1983
1984 /***
1985 * A struct-like class that enables to describe an XSLT.
1986 */
1987 private class Transformation
1988 {
1989 InputStream xsltInputStream;
1990 String xsltLocation;
1991 XMLCatalog uriResolver;
1992 XSLT.Param[] parametersArray;
1993 }
1994
1995 private Transformation getMavenXSLTModel(File maven_project_file)
1996 throws BuildException
1997 {
1998 Transformation transaction = new Transformation();
1999 final String location = common_prefix + "MavenModel.xsl";
2000 transaction.xsltInputStream = getClass().getClassLoader().getResourceAsStream(location);
2001 checkInputStream(transaction.xsltInputStream, location);
2002 transaction.parametersArray = new XSLT.Param[1];
2003 transaction.parametersArray[0].setName("MavenPOMFile");
2004 transaction.parametersArray[0].setExpression(maven_project_file.getAbsolutePath());
2005 return transaction;
2006 }
2007
2008 private static int _counter = 0;
2009
2010 /***
2011 * Performs an XSL tranformation, while applying the common parameters.
2012 *
2013 * @param source the XML source that will be transformed
2014 * @param xslt_input_stream the XSLT stream that will be used a style sheet for the transformation: <code>this
2015 * stream is being closed by this call!</b>
2016 * @param stream_result if not <code>null</code>, then the result of the transformation will be written into it
2017 * @param uri_resolver if not <code>null</code>, this URI resolver will be used by the transformer
2018 * @param parameters_array additional parameters that will be set; may be null
2019 * @return the resulting transformation DOM
2020 * @throws org.apache.tools.ant.BuildException if an error happened during the transformation, or in its preparation
2021 */
2022 private DOMResult performTransformation(Source source, InputStream xslt_input_stream, StreamResult stream_result,
2023 XMLCatalog uri_resolver, XSLT.Param[] parameters_array)
2024 throws BuildException
2025 {
2026 try
2027 {
2028
2029 Transformer transformer = null;
2030 try
2031 {
2032
2033 transformer = createTransformerFromInput(xslt_input_stream, uri_resolver);
2034 }
2035 catch (BuildException build_exception)
2036 {
2037 if (xslt_input_stream != null)
2038 {
2039 try
2040 {
2041 xslt_input_stream.close();
2042 }
2043 catch (IOException io_exception)
2044 {
2045
2046 }
2047 }
2048 throw build_exception;
2049 }
2050
2051
2052 setParameters(transformer, parameters_array);
2053
2054 DOMResult dom_result = null;
2055 transformer.setErrorListener(new ErrorListener()
2056 {
2057 public void error(TransformerException transformer_exception)
2058 throws TransformerException
2059 {
2060 log("error during XSLT: '" + transformer_exception.getMessageAndLocation(), Project.MSG_WARN);
2061 }
2062
2063 public void fatalError(TransformerException transformer_exception)
2064 throws TransformerException
2065 {
2066 log("fatalError during XSLT: '" + transformer_exception.getMessageAndLocation(), Project.MSG_ERR);
2067 }
2068
2069 public void warning(TransformerException transformer_exception)
2070 throws TransformerException
2071 {
2072 log("warning during XSLT: '" + transformer_exception.getMessageAndLocation(), Project.MSG_WARN);
2073 }
2074 });
2075
2076 try
2077 {
2078 log("***************************************************************************", Project.MSG_VERBOSE);
2079 log("Now, launching an XSLT transformation...", Project.MSG_VERBOSE);
2080 if (stream_result != null)
2081 {
2082 transformer.transform(source, stream_result);
2083 }
2084
2085 if (isUsingXalanImplementation() == false)
2086 {
2087 dom_result = new DOMResult(createADocumentBuilder().newDocument());
2088 }
2089 else
2090 {
2091 dom_result = new DOMResult();
2092 }
2093 transformer.transform(source, dom_result);
2094 log("... Transformation over", Project.MSG_VERBOSE);
2095 log("***************************************************************************", Project.MSG_VERBOSE);
2096 return dom_result;
2097 }
2098 catch (TransformerException transformer_exception)
2099 {
2100 log("Internal error during the layout transformation: the XSLT may be bugged...", Project.MSG_ERR);
2101 throw new BuildException(transformer_exception);
2102 }
2103 catch (Exception exception)
2104 {
2105 log("Internal unexpected error during the layout transformation: the XSLT may be bugged...", Project.MSG_ERR);
2106 throw new BuildException(exception);
2107 }
2108 }
2109 finally
2110 {
2111 try
2112 {
2113 xslt_input_stream.close();
2114 }
2115 catch (IOException io_exception)
2116 {
2117
2118 }
2119 }
2120 }
2121
2122 /***
2123 * @see #generateXMLReleaseNotesForXRNFile
2124 */
2125 private void generateXMLReleaseNotes()
2126 throws BuildException
2127 {
2128
2129 log("---------------------------------------------------------------------------", Project.MSG_VERBOSE);
2130 log("Handling the individual release notes files", Project.MSG_INFO);
2131 File[] xrn_file_array = getXRNFiles();
2132 for (int index = 0; index < xrn_file_array.length; index++)
2133 {
2134 File xrn_file = xrn_file_array[index];
2135 log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", Project.MSG_VERBOSE);
2136 log("Handling the release notes files '" + xrn_file.getAbsolutePath() + "'", Project.MSG_VERBOSE);
2137 generateXMLReleaseNotesForXRNFile(xrn_file);
2138 }
2139 log("---------------------------------------------------------------------------", Project.MSG_VERBOSE);
2140 log("Handling the multiple release notes files", Project.MSG_INFO);
2141
2142
2143 if (useTheMultiplePagesXSLT() == true)
2144 {
2145 final String xrn_namespace_uri = "http://xmlreleasenotes.free.fr";
2146 Document document = createADocumentBuilder().newDocument();
2147 Element top_element = document.createElementNS(xrn_namespace_uri, "xrn:MultipleReleaseNotes");
2148 document.appendChild(top_element);
2149 Attr xrn_namespace_attribute = document.createAttribute("xmlns:xrn");
2150 xrn_namespace_attribute.setValue(xrn_namespace_uri);
2151 Attr xsi_namespace_attribute = document.createAttribute("xmlns:xsi");
2152 xsi_namespace_attribute.setValue("http://www.w3.org/2001/XMLSchema-instance");
2153 Attr xrn_version_attribute = document.createAttribute("XRNVersion");
2154 xrn_version_attribute.setValue(getXRNVersion());
2155 top_element.setAttributeNode(xrn_namespace_attribute);
2156 top_element.setAttributeNode(xsi_namespace_attribute);
2157 top_element.setAttributeNode(xrn_version_attribute);
2158
2159 for (int index = 0; index < _xrn_top_node_array.size(); index++)
2160 {
2161 Node node = (Node) _xrn_top_node_array.get(index);
2162 Node imported_node = null;
2163 try
2164 {
2165 imported_node = document.importNode(node, true);
2166 }
2167 catch (DOMException dom_exception)
2168 {
2169 log("Cannot create the DOM that contains all the release notes", Project.MSG_ERR);
2170 throw new BuildException(dom_exception);
2171 }
2172 NamedNodeMap attributes = imported_node.getAttributes();
2173 attributes.removeNamedItem("xsi:schemaLocation");
2174
2175 attributes.removeNamedItem("xmlns:xsi");
2176 top_element.appendChild(imported_node);
2177 }
2178
2179 final String resource_location = getMultiplePageResourceName();
2180 InputStream xslt_input_stream = getClass().getClassLoader().getResourceAsStream(resource_location);
2181 checkInputStream(xslt_input_stream, resource_location);
2182
2183 XMLCatalog uri_resolver = createCatalog();
2184 addCatalogURI(uri_resolver, "XMLReleaseNotes2HTML", common_prefix + _simple_xsl);
2185 addCatalogURI(uri_resolver, "XMLReleaseNotes2HTMLPlugin", getXRN2HTMLPluginResourceName());
2186 if (_plugin_layout_xslt != null)
2187 {
2188 addCatalogURI(uri_resolver, "XRN2HTMLPlugin", _plugin_layout_xslt.xslFile.getAbsolutePath());
2189 }
2190 else
2191 {
2192 addCatalogURI(uri_resolver, "XRN2HTMLPlugin", common_prefix + "XRN2HTMLPlugin.xsl");
2193 }
2194
2195
2196 final boolean debug_xalan = false;
2197 File multiple_releasenotes_file = null;
2198 if (_debug.booleanValue() == true)
2199 {
2200 multiple_releasenotes_file = new File(_destination_directory, "" + _counter + ".xml");
2201 _counter++;
2202 }
2203 else if (debug_xalan == true)
2204 {
2205 try
2206 {
2207 multiple_releasenotes_file = File.createTempFile("XRNMultipleReleaseNotess", "xml");
2208 }
2209 catch (IOException io_exception)
2210 {
2211 final String message = "Cannot create a temporary file for writing the multiple release notes file";
2212 log(message, Project.MSG_ERR);
2213 throw new BuildException(message, io_exception);
2214 }
2215 }
2216 if (_debug.booleanValue() == true || debug_xalan == true)
2217 {
2218 writeXMLDocument(document, multiple_releasenotes_file);
2219 }
2220
2221
2222
2223
2224 try
2225 {
2226
2227 performTransformation(new DOMSource(document)
2228 }
2229 finally
2230 {
2231 if (debug_xalan == true && _debug.booleanValue() == false)
2232 {
2233 multiple_releasenotes_file.delete();
2234 }
2235 }
2236 }
2237 }
2238
2239 /***
2240 * Invokes all XRN-Java plug-ins
2241 *
2242 * @param document the XML DOM which is supposed to contain a top 'xrn:MultipleReleaseNotes' element
2243 */
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267 /***
2268 * Writes the DOM document into a given file; the file is overwritten.
2269 *
2270 * @param xml_document the DOM document root element to be output
2271 * @param file the file in which the XML document is being serialized
2272 * @throws org.apache.tools.ant.BuildException if the serialization went wrong
2273 */
2274 private void writeXMLDocument(Document xml_document, File file)
2275 throws BuildException
2276 {
2277 TransformerFactory transformer_factory = createATransformerFactory();
2278 DOMSource dom_source = new DOMSource(xml_document);
2279 Transformer transformer = null;
2280 try
2281 {
2282 transformer = transformer_factory.newTransformer();
2283 }
2284 catch (TransformerConfigurationException transformer_configuration_exception)
2285 {
2286 log("Cannot create a DOM transformer in order to serialize an XML document", Project.MSG_ERR);
2287 throw new BuildException(transformer_configuration_exception);
2288 }
2289 FileOutputStream file_output_stream = null;
2290 try
2291 {
2292 file_output_stream = new FileOutputStream(file);
2293 }
2294 catch (FileNotFoundException file_not_found_exception)
2295 {
2296 log("Cannot create file '" + file.getAbsolutePath() + "'", Project.MSG_ERR);
2297 throw new BuildException(file_not_found_exception);
2298 }
2299 StreamResult stream_result = new StreamResult(file_output_stream);
2300 try
2301 {
2302 transformer.transform(dom_source, stream_result);
2303 }
2304 catch (TransformerException transformer_exception)
2305 {
2306 log("Cannot serialize an XML document to a file", Project.MSG_ERR);
2307 throw new BuildException(transformer_exception);
2308 }
2309 finally
2310 {
2311 try
2312 {
2313 file_output_stream.flush();
2314 file_output_stream.close();
2315 }
2316 catch (IOException io_exception)
2317 {
2318 log("The file where the XML document has been serialized cannot be flushed or closed properly", Project.MSG_WARN);
2319 }
2320 }
2321 }
2322
2323 /***
2324 * The method that actually performs the transformation, and that assumes that the environment is ready.
2325 *
2326 * @param xrn_file the XRN file for which the transformation must be run
2327 * @throws org.apache.tools.ant.BuildException if the transformation failed somewhere
2328 */
2329 private void generateXMLReleaseNotesForXRNFile(File xrn_file)
2330 throws BuildException
2331 {
2332 Source final_xml_source = null;
2333 final_xml_source = performUserModelTransformations(xrn_file);
2334
2335 final_xml_source = performInternalModelTransformations(final_xml_source);
2336
2337 performLayoutTransformation(final_xml_source);
2338 }
2339
2340 /***
2341 * Part of the XSLT that only concerns the user XSLT models.
2342 *
2343 * @param xrn_file the XRN file to be handled
2344 * @return the transformed XRN file, or itself if no XSLT model if available
2345 */
2346 private Source performUserModelTransformations(File xrn_file)
2347 {
2348 Source output_xml_source = null;
2349 ArrayList model_transformation_array = new ArrayList();
2350 for (int index = 0; index < _model_xslt_vector.size(); index++)
2351 {
2352 XSLT xslt = (XSLT) _model_xslt_vector.elementAt(index);
2353 XSLT.Param[] parameters_array = new XSLT.Param[xslt.parameterVector.size()];
2354 xslt.parameterVector.toArray(parameters_array);
2355 Transformation transformation = new Transformation();
2356 transformation.xsltInputStream = xslt.getXSLTStream();
2357 transformation.xsltLocation = xslt.getXSLTLocation();
2358 transformation.uriResolver = createCatalog("XRNModelCommon.xsl", plugin_common_prefix + "XRNModelCommon.xsl");
2359 transformation.parametersArray = parameters_array;
2360 model_transformation_array.add(transformation);
2361 }
2362 Node top_node = null;
2363 if (model_transformation_array.size() > 0)
2364 {
2365 Source xml_source = new StreamSource(xrn_file);
2366 for (int index = 0; index < model_transformation_array.size(); index++)
2367 {
2368 Transformation transformation = (Transformation) model_transformation_array.get(index);
2369 log("Using the XSL model transformation defined in '" + transformation.xsltLocation + "'",
2370 Project.MSG_INFO);
2371 StreamResult stream_result = null;
2372 if (_debug.booleanValue() == true)
2373 {
2374 stream_result = new StreamResult(new File(_destination_directory, "" + _counter + ".xml"));
2375 _counter++;
2376 }
2377 DOMResult dom_result = performTransformation(xml_source, transformation.xsltInputStream, stream_result,
2378 transformation.uriResolver, transformation.parametersArray);
2379 xml_source = new DOMSource(dom_result.getNode());
2380 top_node = dom_result.getNode();
2381 }
2382 output_xml_source = new DOMSource(top_node);
2383 }
2384 else
2385 {
2386 log("No preliminar XSL transformation used", Project.MSG_INFO);
2387 output_xml_source = new StreamSource(xrn_file);
2388 top_node = getXMLDocumentFromXRNFile(xrn_file);
2389 }
2390 Node right_node = null;
2391 NodeList node_list = top_node.getChildNodes();
2392 for (int index = 0; index < node_list.getLength(); index++)
2393 {
2394 Node node = node_list.item(index);
2395 short node_type = node.getNodeType();
2396
2397 if (
2398 {
2399 right_node = node;
2400 break;
2401 }
2402 }
2403 if (right_node == null)
2404 {
2405 throw new BuildException("Cannot find the 'ReleaseNotes' node from the model XSL transformation");
2406 }
2407
2408 rememberXRN(right_node);
2409 return output_xml_source;
2410 }
2411
2412 /***
2413 * Remembers all XRN files the model XSLT have been applied to.
2414 */
2415 private ArrayList _xrn_top_node_array = new ArrayList();
2416
2417 private void rememberXRN(Node top_node)
2418 {
2419 if (useTheMultiplePagesXSLT() == false)
2420 {
2421
2422 return;
2423 }
2424 _xrn_top_node_array.add(top_node);
2425 }
2426
2427 /***
2428 * This part of the code performs all the XRN internal XSLT model transformations when necessary.
2429 *
2430 * @param input_xml_source the XML inout source to be handled
2431 * @return the XML output resulting from the various potential model transformations
2432 */
2433 private Source performInternalModelTransformations(Source input_xml_source)
2434 {
2435 Source output_xml_source = input_xml_source;
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452 return output_xml_source;
2453 }
2454
2455 /***
2456 * Actually performs the last transformation that works on the HTML layout.
2457 *
2458 * @param final_xml_source the input source that will be used for this last transformation
2459 */
2460 private void performLayoutTransformation(Source final_xml_source)
2461 throws BuildException
2462 {
2463 XSLT.Param[] parameters_array = null;
2464 if (_layout_xslt != null)
2465 {
2466 parameters_array = new XSLT.Param[_layout_xslt.parameterVector.size()];
2467 _layout_xslt.parameterVector.toArray(parameters_array);
2468 }
2469
2470 XMLCatalog uri_resolver = null;
2471 if (_layout_xslt != null && _layout_xslt.uriResolver != null)
2472 {
2473 uri_resolver = _layout_xslt.uriResolver;
2474 }
2475 else
2476 {
2477 uri_resolver = createCatalog();
2478 }
2479
2480
2481 InputStream xslt_input_stream = null;
2482 String xsl_file_resouce_name = null;
2483 StreamResult stream_result = null;
2484 if (useTheMultiplePagesXSLT() == true)
2485 {
2486
2487 log("Using the mutiple-HTML-page for the layout transformation", Project.MSG_INFO);
2488 xsl_file_resouce_name = getMultiplePageResourceName();
2489 xslt_input_stream = getClass().getClassLoader().getResourceAsStream(xsl_file_resouce_name);
2490 checkInputStream(xslt_input_stream, xsl_file_resouce_name);
2491 if (_layout_xslt != null)
2492 {
2493 addCatalogURI(uri_resolver, "XMLReleaseNotes2HTML", _layout_xslt.xslFile.getAbsolutePath());
2494 }
2495 else
2496 {
2497 addCatalogURI(uri_resolver, "XMLReleaseNotes2HTML", common_prefix + _simple_xsl);
2498 }
2499 addCatalogURI(uri_resolver, "XMLReleaseNotes2HTMLPlugin", getXRN2HTMLPluginResourceName());
2500 if (_plugin_layout_xslt != null)
2501 {
2502 addCatalogURI(uri_resolver, "XRN2HTMLPlugin", _plugin_layout_xslt.xslFile.getAbsolutePath());
2503 }
2504 else
2505 {
2506 addCatalogURI(uri_resolver, "XRN2HTMLPlugin", common_prefix + "XRN2HTMLPlugin.xsl");
2507 }
2508 }
2509 else
2510 {
2511
2512 log("Using a single-HTML-page for the layout transformation", Project.MSG_INFO);
2513 if (_layout_xslt != null)
2514 {
2515 xsl_file_resouce_name = _layout_xslt.xslFile.getAbsolutePath();
2516 xslt_input_stream = getInputStream(_layout_xslt.xslFile);
2517 checkInputStream(xslt_input_stream, xsl_file_resouce_name);
2518 }
2519 else
2520 {
2521 xsl_file_resouce_name = common_prefix + _simple_xsl;
2522 xslt_input_stream = getClass().getClassLoader().getResourceAsStream(xsl_file_resouce_name);
2523 checkInputStream(xslt_input_stream, xsl_file_resouce_name);
2524 }
2525 if (_layout_xslt != null)
2526 {
2527 addCatalogURI(uri_resolver, _simple_xsl, common_prefix + _simple_xsl);
2528 }
2529 stream_result = new StreamResult(getMainOutputFile());
2530 }
2531
2532 log("Using the XSLT '" + xsl_file_resouce_name + "' for the layout transformation", Project.MSG_VERBOSE);
2533 performTransformation(final_xml_source, xslt_input_stream, stream_result,
2534 uri_resolver, parameters_array);
2535 }
2536
2537 /***
2538 * @return the name of the multiple-page XSLT resource within the jar
2539 */
2540 private String getMultiplePageResourceName()
2541 {
2542 String xsl_file_resouce_name;
2543 xsl_file_resouce_name = common_prefix + "XMLReleaseNotes2HTMLMultiple1.0.xsl";
2544 if (isUsingXalanImplementation() == true)
2545 {
2546 xsl_file_resouce_name = common_prefix + "XMLReleaseNotes2HTMLMultipleXalan.xsl";
2547 }
2548 return xsl_file_resouce_name;
2549 }
2550
2551 /***
2552 * @return the name of the multiple-page plugin XSLT resource within the jar
2553 */
2554 private String getXRN2HTMLPluginResourceName()
2555 {
2556 String xsl_file_resouce_name;
2557 xsl_file_resouce_name = common_prefix + "XMLReleaseNotes2HTMLPlugin1.0.xsl";
2558 if (isUsingXalanImplementation() == true)
2559 {
2560 xsl_file_resouce_name = common_prefix + "XMLReleaseNotes2HTMLPluginXalan.xsl";
2561 }
2562 return xsl_file_resouce_name;
2563 }
2564
2565 /***
2566 * Creates an input stream from the given file.
2567 *
2568 * @param file the file from which an input stream must be created
2569 * @return a newly created input sream
2570 * @throws org.apache.tools.ant.BuildException if the provided file canot be found
2571 */
2572 private InputStream getInputStream(File file)
2573 throws BuildException
2574 {
2575 InputStream xslt_input_stream = null;
2576 try
2577 {
2578 xslt_input_stream = new FileInputStream(file);
2579 }
2580 catch (FileNotFoundException file_not_found_exception)
2581 {
2582 log("Could not find file '" + file.getAbsolutePath() + "'", Project.MSG_ERR);
2583 throw new BuildException("XSL file '" + file.getAbsolutePath() + "' does not exist: cannot perform the XSLT");
2584 }
2585 return xslt_input_stream;
2586 }
2587
2588 /***
2589 * Checks that the provided input stream is not null.
2590 *
2591 * @param input_stream the input stream to be tested
2592 * @param location the location it is supposed to come from
2593 * @throws org.apache.tools.ant.BuildException if the input stream is actually null
2594 */
2595 private void checkInputStream(InputStream input_stream, String location)
2596 throws BuildException
2597 {
2598 if (input_stream == null)
2599 {
2600 throw new BuildException("Cannot find the input stream with location '" + location + "': cannot continue the " +
2601 "processing");
2602 }
2603 try
2604 {
2605 input_stream.available();
2606 }
2607 catch (IOException io_exception)
2608 {
2609 throw new BuildException("Cannot read the input stream with location '" + location + "': cannot continue the " +
2610 "processing");
2611 }
2612 }
2613
2614 /***
2615 * @return the CSS file that will be created and potentially used by the layout final XSL transformation
2616 */
2617 private File getDestinationCSSFile()
2618 {
2619 File copied_css_file = new File(_destination_directory, copied_css_file_name);
2620 return copied_css_file;
2621 }
2622
2623 /***
2624 * @return the main file that is output at the end of the XSL transformations
2625 */
2626 private File getMainOutputFile()
2627 {
2628 return new File(_destination_directory, _html_release_notes_file_name);
2629 }
2630
2631 /***
2632 * Sets the parameters that are expected for all transformations.
2633 *
2634 * @param transformer an already created XSLT transformer
2635 * @param parameter_array if not <code>null</code>, those parameters will be provided to the XSL transformation
2636 */
2637 private void setParameters(Transformer transformer, XSLT.Param[] parameter_array)
2638 {
2639 transformer.setParameter("XSLTImplementation", transformer.getClass().getName());
2640 if (useTheMultiplePagesXSLT() == true)
2641 {
2642 transformer.setParameter("destination", _destination_directory);
2643 }
2644 if (useTheMultiplePagesXSLT() == true)
2645 {
2646 transformer.setParameter("multipleComponents", new Boolean(true));
2647 }
2648 if (_inline_css.booleanValue() == true)
2649 {
2650 transformer.setParameter("CSSContent", getCSSContent());
2651 transformer.setParameter("CSS", "");
2652 }
2653 else
2654 {
2655 transformer.setParameter("CSS", copied_css_file_name);
2656 }
2657 transformer.setParameter("abstract", _abstract_string);
2658 if (_use_icons != null)
2659 {
2660 transformer.setParameter("useIcons", _use_icons);
2661 }
2662
2663 if (parameter_array != null)
2664 {
2665 log("The parameters provided will be used for the coming XSL transformation", Project.MSG_DEBUG);
2666 for (int index = 0; index < parameter_array.length; index++)
2667 {
2668 XSLT.Param parameter = parameter_array[index];
2669 transformer.setParameter(parameter.getName(), parameter.getExpression());
2670 log("Added the parameter with name '" + parameter.getName() + "' and value '" + parameter.getExpression() + "'",
2671 Project.MSG_VERBOSE);
2672 }
2673 }
2674 transformer.setParameter("banner", _show_banner);
2675 if (useTheMultiplePagesXSLT() == true)
2676 {
2677 if (_title != null)
2678 {
2679 transformer.setParameter("titleText", _title.getText());
2680 }
2681 if (_header != null)
2682 {
2683 transformer.setParameter("headerText", _header.getText());
2684 }
2685 if (_footer != null)
2686 {
2687 transformer.setParameter("footerText", _footer.getText());
2688 }
2689 }
2690 }
2691
2692 /***
2693 * @return the input stream of the CSS that will be used
2694 * @throws org.apache.tools.ant.BuildException if the input stream material does not exist
2695 */
2696 private InputStream getCSSStream()
2697 throws BuildException
2698 {
2699 InputStream input_stream = null;
2700 String location = null;
2701 if (_css_file != null)
2702 {
2703 location = _css_file.getAbsolutePath();
2704 input_stream = getInputStream(_css_file);
2705 }
2706 else
2707 {
2708 location = common_prefix + "XMLReleaseNotes.css";
2709 input_stream = getClass().getClassLoader().getResourceAsStream(location);
2710 }
2711 checkInputStream(input_stream, location);
2712 return input_stream;
2713 }
2714
2715 /***
2716 * @return the content of the CSS file
2717 * @throws org.apache.tools.ant.BuildException if the CSS content could not be read
2718 */
2719 private String getCSSContent()
2720 throws BuildException
2721 {
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735 InputStream input_stream = getCSSStream();
2736 InputStreamReader input_stream_reader = new InputStreamReader(input_stream);
2737 BufferedReader buffered_reader = new BufferedReader(input_stream_reader);
2738 String line = null;
2739 String css_content = "";
2740 try
2741 {
2742 while ((line = buffered_reader.readLine()) != null)
2743 {
2744 css_content += line;
2745 }
2746 }
2747 catch (IOException io_exception)
2748 {
2749 throw new BuildException("Cannot read properly the CSS content", io_exception);
2750 }
2751 return css_content;
2752 }
2753
2754 /***
2755 * Creates an XSLT transformer with the provided XSL input.
2756 * <p>
2757 * Whatever the URI resolver provided, the method wil add one in order to be able to get the other XSL stuff inside
2758 * the executing jar.
2759 * </p>
2760 *
2761 * @param xslt_input_stream the input XSL stream that will be used during the transformation
2762 * @param uri_resolver if not <code>null</code>, this URI resolver will be used during the transformation
2763 * @return a newly created transformer that can be used later on
2764 */
2765 private Transformer createTransformerFromInput(InputStream xslt_input_stream, XMLCatalog uri_resolver)
2766 throws BuildException
2767 {
2768 TransformerFactory transformer_factory = createATransformerFactory();
2769
2770 final String public_id = "XMLReleaseNotesCommon.xsl";
2771
2772 if (uri_resolver == null)
2773 {
2774
2775 uri_resolver = createCatalog(public_id, common_prefix + public_id);
2776 }
2777 else
2778 {
2779 addCatalogURI(uri_resolver, public_id, common_prefix + public_id);
2780 }
2781 transformer_factory.setURIResolver(uri_resolver);
2782 log("Using a URI resolver for the next coming XSLT transformation", Project.MSG_VERBOSE);
2783
2784
2785 transformer_factory.setErrorListener(new ErrorListener()
2786 {
2787 public void error(TransformerException transformer_exception)
2788 throws TransformerException
2789 {
2790 log("error during XSLT: '" + transformer_exception.getMessageAndLocation(), Project.MSG_WARN);
2791 }
2792
2793 public void fatalError(TransformerException transformer_exception)
2794 throws TransformerException
2795 {
2796 log("fatalError during XSLT: '" + transformer_exception.getMessageAndLocation(), Project.MSG_ERR);
2797 throw transformer_exception;
2798 }
2799
2800 public void warning(TransformerException transformer_exception)
2801 throws TransformerException
2802 {
2803 log("warning during XSLT: '" + transformer_exception.getMessage(), Project.MSG_WARN);
2804 }
2805 });
2806
2807
2808 Transformer transformer = null;
2809 try
2810 {
2811 log("Creating the transformer with the '" + xslt_input_stream + "' XSL...", Project.MSG_VERBOSE);
2812 StreamSource stream_source = new StreamSource(xslt_input_stream);
2813 transformer = transformer_factory.newTransformer(stream_source);
2814 log("... Transformer created", Project.MSG_VERBOSE);
2815 }
2816 catch (TransformerConfigurationException transformer_configuration_exception)
2817 {
2818 log("Internal error: could not create the XSL transformer: it may be that the XSL source file is bugged...", Project.MSG_ERR);
2819 throw new BuildException(transformer_configuration_exception);
2820 }
2821 return transformer;
2822 }
2823
2824 /***
2825 * Remembers the XSLT created transformer factory, so that only one is being created
2826 */
2827 private TransformerFactory _transformer_factory = null;
2828
2829 /***
2830 * This is the method that should always be invoked in order to create an XSLT transformer factory.
2831 *
2832 * @return a fresh new factory that can be used furtherly in order to create XSLT transformers
2833 * @throws BuildException if the factory could not be properly created
2834 */
2835 private TransformerFactory createATransformerFactory()
2836 throws BuildException
2837 {
2838 if (_transformer_factory != null)
2839 {
2840 return _transformer_factory;
2841 }
2842 TransformerFactory transformer_factory = null;
2843 log("Creating the XSLT transformer factory...", Project.MSG_DEBUG);
2844 if (_xslt_path == null)
2845 {
2846 log("Using an XSLT implementation taken from the classpath", Project.MSG_VERBOSE);
2847 try
2848 {
2849
2850
2851 final String property_name = "javax.xml.transform.TransformerFactory";
2852 if (_xslt_transformer_factory_class_name != null)
2853 {
2854 log("The previous value of the '" + property_name + "' was '" +
2855 System.getProperty(property_name, _xslt_transformer_factory_class_name) + "'", Project.MSG_DEBUG);
2856 System.setProperty(property_name, _xslt_transformer_factory_class_name);
2857 log("Using the XSLT factory implementation '" + _xslt_transformer_factory_class_name + "'", Project.MSG_VERBOSE);
2858 }
2859 else
2860 {
2861 log("We use the '" + property_name + "' default value, which is '" +
2862 System.getProperty(property_name, _xslt_transformer_factory_class_name) + "'", Project.MSG_DEBUG);
2863 }
2864 transformer_factory = TransformerFactory.newInstance();
2865 }
2866 catch (TransformerFactoryConfigurationError transformer_factory_configuration_error)
2867 {
2868 throw new BuildException("Cannot create a XSLT factory", transformer_factory_configuration_error);
2869 }
2870 }
2871 else if (_xslt_transformer_factory_class_name != null && _xslt_path != null)
2872 {
2873
2874 ClassLoader classloader = null;
2875 if (isAntInferiorTo160() == true)
2876 {
2877 final String classloader_fqn = "org.apache.tools.ant.AntClassLoader";
2878 try
2879 {
2880 Class ant_classloader_class = Class.forName(classloader_fqn);
2881 Constructor constructor = ant_classloader_class.getConstructor(new Class[]{Project.class, Path.class, boolean.class});
2882
2883
2884 classloader = (ClassLoader) constructor.newInstance(new Object[]{getProject(), new Path(getProject()), new Boolean(true)});
2885 }
2886 catch (Exception exception)
2887 {
2888 final String message = "Cannot create an instance of the Ant classloader '" + classloader_fqn + "'";
2889 log(message, Project.MSG_ERR);
2890 throw new BuildException(message, exception);
2891 }
2892 }
2893 else
2894 {
2895 final String classloader_fqn = "org.apache.tools.ant.loader.AntClassLoader2";
2896 try
2897 {
2898 Class ant_classloader_class = Class.forName(classloader_fqn);
2899 classloader = (ClassLoader) ant_classloader_class.newInstance();
2900 ant_classloader_class.getMethod("setProject", new Class[]{Project.class}).invoke(classloader, new Object[]{getProject()});
2901 }
2902 catch (Exception exception)
2903 {
2904 final String message = "Cannot create an instance of the Ant classloader '" + classloader_fqn + "'";
2905 log(message, Project.MSG_ERR);
2906 throw new BuildException(message, exception);
2907 }
2908 }
2909 AntClassLoader ant_classloader = (AntClassLoader) classloader;
2910
2911 for (int index = 0; index < _xslt_path.list().length; index++)
2912 {
2913 String path = _xslt_path.list()[index];
2914 ant_classloader.addPathElement(path);
2915 }
2916
2917 Class transformer_factory_class = null;
2918 try
2919 {
2920 transformer_factory_class = Class.forName(_xslt_transformer_factory_class_name, false, ant_classloader);
2921 }
2922 catch (ClassNotFoundException class_not_found_exception)
2923 {
2924 final String message = "Cannot find the class '" + _xslt_transformer_factory_class_name + "', which is required";
2925 log(message, Project.MSG_ERR);
2926 throw new BuildException(message, class_not_found_exception);
2927 }
2928 try
2929 {
2930 Object object = transformer_factory_class.newInstance();
2931 transformer_factory = (TransformerFactory) object;
2932 }
2933 catch (InstantiationException instantiation_exception)
2934 {
2935 final String message = "Cannot create an instance of the class '" + _xslt_transformer_factory_class_name + "'";
2936 log(message, Project.MSG_ERR);
2937 throw new BuildException(message, instantiation_exception);
2938 }
2939 catch (IllegalAccessException illegal_access_exception)
2940 {
2941 final String message = "Cannot create an instance of the class '" + _xslt_transformer_factory_class_name + "'";
2942 log(message, Project.MSG_ERR);
2943 throw new BuildException(message, illegal_access_exception);
2944 }
2945 }
2946 log("... XSLT factory created", Project.MSG_DEBUG);
2947 final String transformer_factory_actual_class_name = transformer_factory.getClass().getName();
2948 log("Using the XSLT TransformerFactory implementation '" + transformer_factory_actual_class_name + "'",
2949 Project.MSG_VERBOSE);
2950 _transformer_factory = transformer_factory;
2951 return transformer_factory;
2952 }
2953
2954 /***
2955 * @return the name of the actual transformation factory FQN
2956 * @throws org.apache.tools.ant.BuildException if no implementation is being present in the classpath
2957 */
2958 private String getActualTransformerFactory
2959 ()
2960 throws BuildException
2961 {
2962 return createATransformerFactory().getClass().getName();
2963 }
2964
2965 /***
2966 * The name of the main XSLT defaut layout transformation style sheet.
2967 */
2968 private final String _simple_xsl = "XMLReleaseNotes2HTML.xsl";
2969
2970 }