View Javadoc

1   /*
2    * $Id: XSLTransformer.java 2345 2010-07-31 14:30:28Z andrewinkler $
3    * ============================================================================
4    * Project awtools-xml
5    * Copyright (c) 2000-2010 by Andre Winkler. All rights reserved.
6    * ============================================================================
7    *          GNU LESSER GENERAL PUBLIC LICENSE
8    *  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
9    *
10   *  This library is free software; you can redistribute it and/or
11   *  modify it under the terms of the GNU Lesser General Public
12   *  License as published by the Free Software Foundation; either
13   *  version 2.1 of the License, or (at your option) any later version.
14   *
15   *  This library is distributed in the hope that it will be useful,
16   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   *  Lesser General Public License for more details.
19   *
20   *  You should have received a copy of the GNU Lesser General Public
21   *  License along with this library; if not, write to the Free Software
22   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23   *
24   */
25  
26  package de.awtools.xml;
27  
28  import java.io.File;
29  import java.io.FileInputStream;
30  import java.io.FileNotFoundException;
31  import java.io.FileOutputStream;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.net.MalformedURLException;
35  import java.util.HashMap;
36  import java.util.Map;
37  
38  import javax.xml.transform.OutputKeys;
39  import javax.xml.transform.Transformer;
40  import javax.xml.transform.URIResolver;
41  
42  import org.apache.commons.io.FileUtils;
43  import org.apache.commons.io.IOUtils;
44  import org.apache.commons.lang.StringUtils;
45  import org.apache.commons.lang.Validate;
46  import org.dom4j.Document;
47  import org.dom4j.DocumentException;
48  import org.dom4j.io.SAXReader;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  /**
53   * Transformiert ein XML Dokument mittels eines XSL Stylesheet in ein
54   * Zieldokument. Ausgangsdokument, Stylesheet und Ausgabe liegen im
55   * Filesystem.<br/> Das zugrunde liegende Stylesheet wird solange
56   * wiederverwendet, bis es neu gesetzt wird.
57   * 
58   * @version $LastChangedRevision: 2345 $ $LastChangedDate: 2010-07-31 16:30:28 +0200 (Sa, 31 Jul 2010) $
59   * @author by Andre Winkler, $LastChangedBy: andrewinkler $
60   */
61  public class XSLTransformer {
62  
63      /** Der private Logger der Klasse. */
64      private final Logger log = LoggerFactory.getLogger(XSLTransformer.class);
65  
66      // -- parameter -----------------------------------------------------------
67  
68      /** Parameter für den XSL Transformer. */
69      private Map<String, Object> params = new HashMap<String, Object>();
70  
71      /**
72       * Setzt einen neuen Parameter ein.
73       * 
74       * @param key Der Schlüssel.
75       * @param value Der Wert.
76       */
77      public final void addParameter(final String key, final Object value) {
78          params.put(key, value);
79      }
80  
81      /**
82       * Setzt die Parameterliste zurück.
83       */
84      public final void resetParams() {
85          params.clear();
86      }
87  
88      /**
89       * Liefert ein nicht modifizierbare Map der Parameter-Map.
90       * 
91       * @return Ein nicht modifizierbare Map aller Parameter.
92       */
93      private Map<String, Object> getParams() {
94          return params;
95      }
96  
97      /**
98       * Initialisiert den Transformer mit den Parametern.
99       * 
100      * @param _transe Der zu initialisierende Transformer.
101      */
102     private void addParams(final Transformer _transe) {
103         for (String key : getParams().keySet()) {
104             _transe.setParameter(key, getParams().get(key));
105         }
106     }
107 
108     // -- style ---------------------------------------------------------------
109 
110     /** Die Stylesheet-Datei als String. */
111     private String style;
112 
113     /** Das Stylesheet als InputStream. */
114     private InputStream styleStream;
115 
116     /** Das Stylesheet als File. */
117     private File styleFile;
118 
119     /** Das Stylesheet verändert? */
120     private boolean styleModified = false;
121 
122     /**
123      * Liefert das Stylesheet als InputStream.
124      * 
125      * @return Ein InputStream.
126      * @throws IOException Stylesheet konnte nicht gelesen werden.
127      */
128     public final InputStream getStyle() throws IOException {
129         InputStream is = null;
130 
131         if (style != null) {
132             if (log.isDebugEnabled()) {
133                 StringBuilder buf =
134                         new StringBuilder("Load xslt by string: '").append(
135                             getStyle()).append("'.");
136                 log.debug(buf.toString());
137             }
138             is = new FileInputStream(style);
139         } else if (styleFile != null) {
140             is = new FileInputStream(styleFile);
141         } else if (styleStream != null) {
142             is = styleStream;
143         }
144 
145         return is;
146     }
147 
148     /**
149      * Setzt das Stylesheet.
150      * 
151      * @param value Das Stylesheet.
152      */
153     public final void setStyle(final String value) {
154         style = value;
155         styleModified = true;
156 
157         styleFile = null;
158         styleStream = null;
159     }
160 
161     /**
162      * Setzt das Stylesheet.
163      * 
164      * @param value Das Stylesheet als InputStream.
165      */
166     public final void setStyle(final InputStream value) {
167         styleStream = value;
168         styleModified = true;
169 
170         style = null;
171         styleFile = null;
172     }
173 
174     /**
175      * Setzt das Stylesheet.
176      * 
177      * @param value Das Stylesheet als File.
178      */
179     public final void setStyle(final File value) {
180         styleFile = value;
181         styleModified = true;
182 
183         style = null;
184         styleStream = null;
185     }
186 
187     // -- source --------------------------------------------------------------
188 
189     /** Die Eingabedatei. */
190     private File source;
191 
192     /**
193      * Liefert die Source.
194      * 
195      * @return Die Source.
196      */
197     public final File getSource() {
198         return source;
199     }
200 
201     /**
202      * Setzt die Source.
203      * 
204      * @param value Die Source.
205      */
206     public final void setSource(final String value) {
207         source = new File(value);
208     }
209 
210     /**
211      * Setzt das Source-File.
212      *
213      * @param value Die Source.
214      */
215     public final void setSource(final File value) {
216         source = value;
217     }
218 
219     // -- target --------------------------------------------------------------
220 
221     /** Die Ausgabedatei. */
222     private File target;
223 
224     /**
225      * Liefert die Ausgabedatei.
226      * 
227      * @return Die Ausgabedatei.
228      */
229     public final File getTarget() {
230         return target;
231     }
232 
233     /**
234      * Setzt die Ausgabedatei.
235      * 
236      * @param value Die Ausgabedatei.
237      */
238     public final void setTarget(final String value) {
239         target = new File(value);
240     }
241 
242     /**
243      * Setzt die Ausgabedatei.
244      * 
245      * @param value Die Ausgabedatei.
246      */
247     public final void setTarget(final File value) {
248         target = value;
249     }
250 
251     // -- uriResolver ---------------------------------------------------------
252 
253     /** Ein URIResolver. */
254     private URIResolver uriResolver;
255 
256     /**
257      * Liefert einen URIResolver.
258      * 
259      * @return Ein URIResolver.
260      */
261     public final URIResolver getUriResolver() {
262         return uriResolver;
263     }
264 
265     /**
266      * Setzt den URIResolver.
267      * 
268      * @param value Ein URIResolver.
269      */
270     public final void setUriResolver(final URIResolver value) {
271         uriResolver = value;
272     }
273 
274     // -- encoding ------------------------------------------------------------
275 
276     /** Encoding. */
277     private String encoding;
278 
279     /**
280      * Liefert das Encoding.
281      * 
282      * @return Das Encoding.
283      */
284     public final String getEncoding() {
285         return encoding;
286     }
287 
288     /**
289      * Setzt das Encoding.
290      * 
291      * @param value Das Encoding.
292      */
293     public final void setEncoding(final String value) {
294         encoding = value;
295     }
296 
297     // -- method --------------------------------------------------------------
298 
299     /** Ausgabeformat: html, xml oder text. */
300     private String method;
301 
302     /**
303      * Liefert das Ausgabeformat: html, xml oder text.
304      * 
305      * @return Das Ausgabeformat.
306      */
307     public final String getMethod() {
308         return method;
309     }
310 
311     /**
312      * Setzt das Ausgabeformat: html, xml oder text.
313      * 
314      * @param value Das Ausgabeformat.
315      */
316     public final void setMethod(final String value) {
317         method = value;
318     }
319 
320     // -- omitXmlDeclaration --------------------------------------------------
321 
322     /** yes or no. XML Deklaration für generierte Datei? */
323     private String omitXmlDeclaration;
324 
325     /**
326      * Liefert OmitXmlDeclaration.
327      * 
328      * @return omitXmlDeclaration.
329      */
330     public final String getOmitXmlDeclaration() {
331         return omitXmlDeclaration;
332     }
333 
334     /**
335      * Setzt das OmitXmlDeclaration
336      * 
337      * @param value omitXmlDeclaration.
338      */
339     public final void setOmitXmlDeclaration(final String value) {
340         omitXmlDeclaration = value;
341     }
342 
343     // ------------------------------------------------------------------------
344 
345     /** Das Stylesheet in compilierter Form. */
346     private Transformer transe;
347 
348     /**
349      * Liefert den Transformer.
350      * 
351      * @return Ein <code>Transformer</code>
352      */
353     private final Transformer getTransformer() {
354         if ((transe == null) || (styleModified)) {
355             InputStream xslt = null;
356             try {
357                 xslt = getStyle();
358                 transe =
359                         TransformerUtils.getTransformer(xslt, getUriResolver());
360 
361                 transe.setOutputProperty(OutputKeys.ENCODING, getEncoding());
362                 transe.setOutputProperty(OutputKeys.METHOD, getMethod());
363                 transe.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
364                     getOmitXmlDeclaration());
365 
366                 styleModified = false;
367             } catch (IOException ex) {
368                 log.debug("IOException", ex);
369                 throw new RuntimeException(ex);
370             } finally {
371                 IOUtils.closeQuietly(xslt);
372             }
373         }
374         return transe;
375     }
376 
377     /**
378      * Startet die Transformation eines XML Files.
379      */
380     public final void run() {
381         Validate.notNull(getSource());
382         Validate.notNull(getTarget());
383         Validate.isTrue(!StringUtils.isBlank(getEncoding()));
384         Validate.isTrue(!StringUtils.isBlank(getMethod()));
385         Validate.isTrue(!StringUtils.isBlank(getOmitXmlDeclaration()));
386 
387         SAXReader reader = new SAXReader();
388         try {
389             cleanTargetFile(getTarget());
390 
391             if (!getTarget().canWrite()) {
392                 final String MSG = "Can not write to target file!";
393                 log.debug(MSG);
394                 throw new IllegalArgumentException(MSG);
395             }
396 
397             if (!getSource().canRead()) {
398                 final String MSG = "Can not read source file!";
399                 log.debug(MSG);
400                 throw new IllegalArgumentException(MSG);
401             }
402 
403             style(reader.read(getSource()), getTarget());
404         } catch (MalformedURLException ex) {
405             log.debug("MalformedURLException with file '" + getSource() + "'.",
406                 ex);
407             throw new RuntimeException("MalformedURLException with file '"
408                 + getSource() + "'.", ex);
409         } catch (DocumentException ex) {
410             log.debug("DocumentException with file '" + getSource() + "'.", ex);
411             throw new RuntimeException(ex);
412         } catch (IOException ex) {
413             log.debug("Can not delete or create target file '" + getTarget()
414                 + "'!", ex);
415             throw new RuntimeException(ex);
416         }
417     }
418 
419     /**
420      * Existiert das Target-File bereits, so wird dieses gelöscht und dann
421      * neu angelegt.
422      *
423      * @param targetFile Target-Datei.
424      * @throws IOException Die Datei konnte nicht gelöscht oder angelegt werden.
425      */
426     private void cleanTargetFile(final File targetFile) throws IOException {
427 
428         if (targetFile.exists()) {
429             FileUtils.forceDelete(targetFile);
430         }
431         if (!targetFile.createNewFile()) {
432             log.debug("Can not create target file '" + getTarget() + "'!");
433         }
434     }
435 
436     /**
437      * Startet die Transformation eines dom4j Dokuments.
438      * 
439      * @param doc Ein dom4j Dokument.
440      * @param targetFile Die Ausgabedatei.
441      */
442     private void style(final Document doc, final File targetFile) {
443         FileOutputStream fous = null;
444         try {
445             fous = new FileOutputStream(targetFile);
446             Transformer _transe = getTransformer();
447             addParams(_transe);
448             TransformerUtils.transform(fous, _transe, doc);
449         } catch (FileNotFoundException ex) {
450             log.debug("FileNotFoundException:", ex);
451             throw new RuntimeException(ex);
452         } finally {
453             IOUtils.closeQuietly(fous);
454         }
455     }
456 
457 }