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 }