View Javadoc

1   /*
2    * The Apache Software License, Version 1.1
3    *
4    * Copyright (c) 1999, 2000 The Apache Software Foundation.  All rights
5    * reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions
9    * are met:
10   *
11   * 1. Redistributions of source code must retain the above copyright
12   *    notice, this list of conditions and the following disclaimer.
13   *
14   * 2. Redistributions in binary form must reproduce the above copyright
15   *    notice, this list of conditions and the following disclaimer in
16   *    the documentation and/or other materials provided with the
17   *    distribution.
18   *
19   * 3. The end-user documentation included with the redistribution, if
20   *    any, must include the following acknowlegement:
21   *       "This product includes software developed by the
22   *        Apache Software Foundation (http://www.apache.org/)."
23   *    Alternately, this acknowlegement may appear in the software itself,
24   *    if and wherever such third-party acknowlegements normally appear.
25   *
26   * 4. The names "The Jakarta Project", "Ant", and "Apache Software
27   *    Foundation" must not be used to endorse or promote products derived
28   *    from this software without prior written permission. For written
29   *    permission, please contact apache@apache.org.
30   *
31   * 5. Products derived from this software may not be called "Apache"
32   *    nor may "Apache" appear in their names without prior written
33   *    permission of the Apache Group.
34   *
35   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46   * SUCH DAMAGE.
47   * ====================================================================
48   *
49   * This software consists of voluntary contributions made by many
50   * individuals on behalf of the Apache Software Foundation.  For more
51   * information on the Apache Software Foundation, please see
52   * <http://www.apache.org/>.
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  * &lt;taskdef
105  *   classname="org.xrn.ant.XMLReleaseNotes"
106  *   name="XMLReleaseNotes"
107  * /&gt;
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  * &lt;xmlcatalog&gt;
219  *     &lt;entity
220  *       publicId="SomePublicID"
221  *       location="its_actual_URI"
222  *     /&gt;
223  *    ...
224  * &lt;/xmlcatalog&gt;
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  * &lt;XMLReleaseNotes
254  *   xml="ReleaseNotes.xml"
255  * /&gt;
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   /*private void log(String message, int level)
270   {
271 
272   }
273 
274   private Project getProject()
275   {
276     return null;
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    * &lt;URIResolverPath&gt;
412    *   &lt;pathelement location="&lt;the path to&gt;/XMLReleaseNotes.jar"/&gt;
413    * &lt;/URIResolverPath&gt;
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         // Iterates over the Ant buildfiles
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     // We check that the inputs are OK
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       // If a mail sending has been ask for, do it
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     // In that case, some migration is required
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       // We disable the validation, since it reports numerous unindentified problems
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       // This should not happen because of the preliminar checks
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       // It may happen that the declared XRN version is not the current one, but that no migration is necessary
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         // At last, we output the result of all those transformation into the original XML release notes file
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           // Normal: the outstream may be at the end
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       // We do not forget to delete all the temporary files generated
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       // And to close all the input stream
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           // Does not matter
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     // Creates an XSLT transformer that is properly set with all parameters defined
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     /*XMLValidateTask xml_validate_task = new XMLValidateTask();
1533     xml_validate_task.setProject(getProject());
1534     xml_validate_task.setFile(_xml_release_notes_file);
1535     xml_validate_task.setLenient(false);
1536     xml_validate_task.setWarn(true);
1537     xml_validate_task.setFailOnError(true);
1538     xml_validate_task.execute();*/
1539     /*InputStream input_stream = null;
1540     File temporary_xsd_file = null;
1541     try
1542     {
1543       final String location = common_prefix + "XMLReleaseNotes.xsd";
1544       input_stream = getClass().getClassLoader().getResourceAsStream(location);
1545       if (input_stream == null)
1546       {
1547         log("Cannot find the XRN release notes schema file '" + location + "' in the jar: cannot validate", Project.MSG_WARN);
1548         return;
1549       }
1550       temporary_xsd_file = File.createTempFile("xrn", "xsd");
1551       if (JarHelper.copyInputStreamIntoFile(input_stream, temporary_xsd_file) == false)
1552       {
1553         log("Cannot copy the XRN release notes schema file '" + location + "' from the jar into the " +
1554                 "temporary file '" + temporary_xsd_file.getAbsolutePath() + "': cannot validate", Project.MSG_WARN);
1555         return;
1556       }
1557       DOMParser dom_parser = new DOMParser();
1558       try
1559       {
1560         dom_parser.setFeature("http://xml.org/sax/features/validation", true);
1561         dom_parser.setFeature("http://apache.org/xml/features/validation/schema", true);
1562         dom_parser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
1563         //dom_parser.setFeature("http://apache.org/xml/features/standard-uri-conformant",false);
1564         //dom_parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", "V://XMLReleaseNotes//source//XML//XMLReleaseNotes.xsd");
1565         dom_parser.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation", temporary_xsd_file.getAbsolutePath());
1566       }
1567       catch (SAXNotRecognizedException sax_not_recognized_exception)
1568       {
1569         throw new BuildException("Does not recognize an XSD validation feature or property", sax_not_recognized_exception);
1570       }
1571       catch (SAXNotSupportedException sax_not_supported_exception)
1572       {
1573         throw new BuildException("Cannot support an XSD validation feature or property", sax_not_supported_exception);
1574       }
1575       dom_parser.setErrorHandler(new ErrorHandler()
1576       {
1577         public void error(SAXParseException sax_parser_exception)
1578                 throws SAXException
1579         {
1580           throwException(sax_parser_exception);
1581         }
1582 
1583         public void fatalError(SAXParseException sax_parser_exception)
1584                 throws SAXException
1585         {
1586           throwException(sax_parser_exception);
1587         }
1588 
1589         public void warning(SAXParseException sax_parser_exception)
1590                 throws SAXException
1591         {
1592         }
1593 
1594         private void throwException(SAXParseException sax_parser_exception)
1595                 throws BuildException
1596         {
1597           if (sax_parser_exception.getMessage().startsWith("cvc-datatype-valid.1.2.1") == true)
1598           {
1599             return;
1600           }
1601           else if (sax_parser_exception.getMessage().startsWith("cvc-attribute.3") == true)
1602           {
1603             return;
1604           }
1605           else if (sax_parser_exception.getMessage().startsWith("cvc-complex-type.2.4.c") == true)
1606           {
1607             return;
1608           }
1609           throw new BuildException("Invalid XML release notes file '" + xrn_file.getAbsolutePath() + "' at line " +
1610                   sax_parser_exception.getLineNumber() + ", column " + sax_parser_exception.getColumnNumber() +
1611                   ": " + sax_parser_exception.getMessage(), sax_parser_exception);
1612         }
1613       });
1614       dom_parser.parse(xrn_file.getAbsolutePath());
1615     }
1616     catch (SAXException sax_exception)
1617     {
1618       throw new BuildException("Invalid XML release notes file", sax_exception);
1619     }
1620     catch (IOException io_exception)
1621     {
1622       throw new BuildException("Cannot read the XML release notes file", io_exception);
1623     }
1624     finally
1625     {
1626       if (input_stream != null)
1627       {
1628         try
1629         {
1630           input_stream.close();
1631         }
1632         catch (IOException io_exception)
1633         {
1634           // Does not matter
1635         }
1636       }
1637       temporary_xsd_file.delete();
1638     }*/
1639 
1640     /*try
1641         {
1642           XMLReader xml_reader = XMLReaderFactory.createXMLReader();
1643           xml_reader.setFeature("http://xml.org/sax/features/validation", true);
1644           xml_reader.parse(_xml_release_notes_file.getAbsolutePath());
1645         }
1646         catch (SAXException e)
1647         {
1648           e.printStackTrace();  //To change body of catch statement use Options | File Templates.
1649         }
1650         catch (IOException e)
1651         {
1652           e.printStackTrace();  //To change body of catch statement use Options | File Templates.
1653         }
1654 
1655         SAXParserFactory parser_factory = null;
1656         try
1657         {
1658           parser_factory = SAXParserFactory.newInstance();
1659           parser_factory.setValidating(true);
1660           parser_factory.setNamespaceAware(true);
1661         }
1662         catch (FactoryConfigurationError factory_configuration_error)
1663         {
1664           throw new BuildException("Cannot create a new SAX parser factory", factory_configuration_error);
1665         }
1666         SAXParser parser = null;
1667         try
1668         {
1669           parser = parser_factory.newSAXParser();
1670         }
1671         catch (ParserConfigurationException parser_configuration_exception)
1672         {
1673           throw new BuildException("Cannot create a new SAX parser: it is not properly intialized", parser_configuration_exception);
1674         }
1675         catch (SAXException sax_exception)
1676         {
1677           throw new BuildException("Cannot create a new SAX parser", sax_exception);
1678         }
1679         try
1680         {
1681           DefaultHandler default_handler = new DefaultHandler()
1682           {
1683 
1684             public void error(SAXParseException e)
1685               throws SAXException
1686             {
1687               throwException(e);
1688             }
1689 
1690             public void fatalError(SAXParseException e)
1691               throws SAXException
1692             {
1693               throwException(e);
1694             }
1695 
1696             public void warning(SAXParseException e)
1697               throws SAXException
1698             {
1699               throwException(e);
1700             }
1701 
1702             private void throwException(SAXParseException sax_parser_exception)
1703             {
1704               throw new BuildException("Invalid XML release notes file '" + _xml_release_notes_file.getAbsolutePath() + "' at line " +
1705                 sax_parser_exception.getLineNumber() + ", column " + sax_parser_exception.getColumnNumber() +
1706                 ": " + sax_parser_exception.getMessage(), sax_parser_exception);
1707 
1708             }
1709           };
1710           parser.parse(_xml_release_notes_file, default_handler);
1711         }
1712         catch (SAXException sax_exception)
1713         {
1714           throw new BuildException("Invalid XML release notes file", sax_exception);
1715         }
1716         catch (IOException io_exception)
1717         {
1718           throw new BuildException("Cannot read the XML release notes file", io_exception);
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       // We should not be running under Ant V1.5=-
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       // We should be running under Ant V1.6=+
1795       class_vector.add(resouce_location_class);
1796     }
1797     catch (ClassNotFoundException class_not_found_exception)
1798     {
1799       // We are not running under Ant V1.6=+
1800     }
1801     try
1802     {
1803       Class resouce_location_class = Class.forName("org.apache.tools.ant.types.DTDLocation");
1804       // We should be running under Ant V1.5=-
1805       class_vector.add(resouce_location_class);
1806     }
1807     catch (ClassNotFoundException class_not_found_exception)
1808     {
1809       // We should not be running under Ant V1.5=-
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     // Prepares the disk environment (creation of directories, copy of files if necessary)
1898     prepareEnvironment();
1899     // If it's OK, we can go for the transformation
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     // Destination directory
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     // The CSS file is only copied if necessary
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       // In that case, we use the CSS provided by the end-user
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       // Creates an XSLT transformer that is properly set with all parameters defined
2029       Transformer transformer = null;
2030       try
2031       {
2032         // Creates the transformer
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             // Does not matter
2046           }
2047         }
2048         throw build_exception;
2049       }
2050 
2051       // Sets the common XSLT parameters, plus the provided ones
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       // Now, go for the transformation!
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         // In any case, we provide the DOM
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         // Does not matter
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     // If a multiple-component release notes is being asked for, we finally perform the transformation that
2142     // generates the HTML glue
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         //attributes.removeNamedItem("xmlns:xrn");
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       // The XML file is flushed to a temporary file because of a bug in Xalan implementation
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       // If needed, the XRN-Java plug-ins are being called
2222       //callJavaPlugins(document, multiple_releasenotes_file);
2223 
2224       try
2225       {
2226         // The default main style sheet should not be written, this is why we do not write the output of the transformation
2227         performTransformation(new DOMSource(document)/*new StreamSource(multiple_releasenotes_file)*/, xslt_input_stream, null, uri_resolver, null);
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   /*private void callJavaPlugins(Document document, File file)
2245     throws BuildException
2246   {
2247     Plugin plugin = new Plugin();
2248     try
2249     {
2250       if (document != null)
2251       {
2252         plugin.run(document);
2253       }
2254       else
2255       {
2256         plugin.run(file);
2257       }
2258     }
2259     catch (Exception exception)
2260     {
2261       final String message = "The execution of the XRN-Java plug-in was a failure";
2262       log(message, Project.MSG_ERR);
2263       throw new BuildException(message, exception);
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     // We perform the internal XSL model transformations
2335     final_xml_source = performInternalModelTransformations(final_xml_source);
2336     // The last thing to do is to launch the HTML layout rendering
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       // This is not possible to rely on the node local name because Saxon is bugged and always return null...
2397       if (/*node.getLocalName() != null && node.getLocalName().equals("ReleaseNotes") == true &&*/ node_type == Node.ELEMENT_NODE)
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     // If necessary, we remember the XML source
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       // We do not need to remember the node in that case
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     /*if (useCumulative() == true)
2437     {
2438       // We perform an intermediate XSLT transformation if the cumulative option has been chosen
2439       final String cumulative_model_location = common_prefix + "CumulativeModel.xsl";
2440       log("The cumulative XSLT '" + cumulative_model_location + "' is being applied", Project.MSG_INFO);
2441       InputStream cumulative_input_stream = getClass().getClassLoader().getResourceAsStream(cumulative_model_location);
2442       if (cumulative_input_stream == null)
2443       {
2444         log("Error: the cumulative XSLT is missing from the jar: this feature will not be taken into account", Project.MSG_ERR);
2445       }
2446       else
2447       {
2448         DOMResult dom_result = performTransformation(input_xml_source, cumulative_input_stream, null, null, new XSLT.Param[0]);
2449         output_xml_source = new DOMSource(dom_result.getNode());
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     // We prepare the URI resolver
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     // Determines the XSLT input
2481     InputStream xslt_input_stream = null;
2482     String xsl_file_resouce_name = null;
2483     StreamResult stream_result = null;
2484     if (useTheMultiplePagesXSLT() == true)
2485     {
2486       // The multiple-HTML-page template XSLT is used
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       // A single-HTML-page template XSLT is used
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     // At last, we provide the provided parameter_array
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     // We use the Ant 'LoadFile' task in order to get the content of the CSS file, and remove the comments
2723     /*LoadFile load_file = new LoadFile();
2724     load_file.setProject(getProject());
2725     load_file.setSrcFile(getDestinationCSSFile());
2726     FilterChain filter_chain = new FilterChain();
2727     // Remove the comments
2728     filter_chain.addStripJavaComments(new StripJavaComments());
2729     load_file.addFilterChain(filter_chain);
2730     final String dummy_property = "DUMMY_PROPERTY" + new Random().nextLong();
2731     load_file.setProperty(dummy_property);
2732     load_file.execute();
2733     final String css_content = getProject().getProperty(dummy_property);
2734     return css_content;*/
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     // We provide an URI resolver to the factory (it does not work with the transformer itself) in all cases
2770     final String public_id = "XMLReleaseNotesCommon.xsl";
2771     // We add the common XSL to the resolver
2772     if (uri_resolver == null)
2773     {
2774       // We create an URI resolver
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     // Defines an error listener so as to get more information when the XSLT is not well-formed
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     // Creates the transformer
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         // We set the XSLT Java system property right now, because we make use from now
2850         // Sets the XSLT implementation: the default one will be used if no special one is being asked for
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       // Dependending on the Ant version, we use the specific class loader
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           // It is very important to use the parent classloader first, otherwise you may get 'java.lang.LinkageError'
2883           // under Ant V1.5!
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       //ant_classloader.setIsolated(true);
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 }