1 /* TransformerImpl.java --
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.xml.transform;
40 import java.io.BufferedOutputStream;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43 import java.io.OutputStream;
44 import java.io.UnsupportedEncodingException;
45 import java.io.Writer;
46 import java.net.MalformedURLException;
47 import java.net.UnknownServiceException;
49 import java.net.URLConnection;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.LinkedHashMap;
54 import java.util.Properties;
55 import javax.xml.transform.ErrorListener;
56 import javax.xml.transform.OutputKeys;
57 import javax.xml.transform.Result;
58 import javax.xml.transform.Source;
59 import javax.xml.transform.Transformer;
60 import javax.xml.transform.TransformerConfigurationException;
61 import javax.xml.transform.TransformerException;
62 import javax.xml.transform.URIResolver;
63 import javax.xml.transform.dom.DOMSource;
64 import javax.xml.transform.dom.DOMResult;
65 import javax.xml.transform.sax.SAXResult;
66 import javax.xml.transform.stream.StreamResult;
67 import org.w3c.dom.Document;
68 import org.w3c.dom.DocumentType;
69 import org.w3c.dom.DOMImplementation;
70 import org.w3c.dom.NamedNodeMap;
71 import org.w3c.dom.Node;
72 import org.w3c.dom.Text;
73 import org.xml.sax.ContentHandler;
74 import org.xml.sax.SAXException;
75 import org.xml.sax.ext.LexicalHandler;
76 import gnu.xml.dom.DomDoctype;
77 import gnu.xml.dom.DomDocument;
78 import gnu.xml.dom.ls.WriterOutputStream;
79 import gnu.xml.xpath.Expr;
80 import gnu.xml.xpath.Root;
83 * The transformation process for a given stylesheet.
85 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
91 final TransformerFactoryImpl factory;
92 final Stylesheet stylesheet;
93 URIResolver uriResolver;
94 ErrorListener errorListener;
95 Properties outputProperties;
97 TransformerImpl(TransformerFactoryImpl factory,
98 Stylesheet stylesheet,
99 Properties outputProperties)
100 throws TransformerConfigurationException
102 this.factory = factory;
103 uriResolver = factory.userResolver;
104 errorListener = factory.userListener;
105 this.stylesheet = stylesheet;
106 this.outputProperties = outputProperties;
107 if (stylesheet != null)
109 // Set up parameter context for this transformer
110 stylesheet.bindings.push(false);
114 public void transform(Source xmlSource, Result outputTarget)
115 throws TransformerException
117 // Get the source tree
119 synchronized (factory.resolver)
121 factory.resolver.setUserResolver(uriResolver);
122 factory.resolver.setUserListener(errorListener);
123 source = factory.resolver.resolveDOM(xmlSource, null, null);
125 Node context = source.getNode();
126 Document doc = (context instanceof Document) ? (Document) context :
127 context.getOwnerDocument();
128 if (doc instanceof DomDocument)
130 // Suppress mutation events
131 ((DomDocument) doc).setBuilding(true);
132 // TODO find a better/more generic way of doing this than
135 // Get the result tree
136 Node parent = null, nextSibling = null;
137 if (outputTarget instanceof DOMResult)
139 DOMResult dr = (DOMResult) outputTarget;
140 parent = dr.getNode();
141 nextSibling = dr.getNextSibling();
143 Document rdoc = (parent instanceof Document) ? (Document) parent :
144 parent.getOwnerDocument();
145 if (rdoc instanceof DomDocument)
147 // Suppress mutation events and allow multiple root elements
148 DomDocument drdoc = (DomDocument) rdoc;
149 drdoc.setBuilding(true);
150 drdoc.setCheckWellformedness(false);
151 // TODO find a better/more generic way of doing this than
155 boolean created = false;
157 if (stylesheet != null)
161 // Create a new document to hold the result
162 DomDocument resultDoc = new DomDocument();
163 resultDoc.setBuilding(true);
164 resultDoc.setCheckWellformedness(false);
168 // Make a copy of the source node, and strip it
169 context = context.cloneNode(true);
171 // XSLT transformation
174 // Set output properties in the underlying stylesheet
175 ((TransformerOutputProperties) outputProperties).apply();
176 stylesheet.initTopLevelVariables(context);
177 TemplateNode t = stylesheet.getTemplate(null, context, false);
180 stylesheet.current = context;
181 t.apply(stylesheet, null, context, 1, 1, parent, nextSibling);
184 catch (TransformerException e)
186 // Done transforming, reset document
187 if (doc instanceof DomDocument)
189 ((DomDocument) doc).setBuilding(false);
196 // Identity transform
197 Node clone = context.cloneNode(true);
198 if (context.getNodeType() != Node.DOCUMENT_NODE)
203 // Create a new document to hold the result
204 DomDocument rd = new DomDocument();
205 rd.setBuilding(true);
206 rd.setCheckWellformedness(false);
207 parent = resultDoc = rd;
212 resultDoc = (parent instanceof Document) ?
214 parent.getOwnerDocument();
216 Document sourceDoc = context.getOwnerDocument();
217 if (sourceDoc != resultDoc)
219 clone = resultDoc.adoptNode(clone);
221 if (nextSibling != null)
223 parent.insertBefore(clone, nextSibling);
227 parent.appendChild(clone);
232 // Cannot append document to another tree
237 String method = outputProperties.getProperty(OutputKeys.METHOD);
238 int outputMethod = "html".equals(method) ? Stylesheet.OUTPUT_HTML :
239 "text".equals(method) ? Stylesheet.OUTPUT_TEXT :
240 Stylesheet.OUTPUT_XML;
241 String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
242 String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
243 String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
244 String version = outputProperties.getProperty(OutputKeys.VERSION);
245 boolean omitXmlDeclaration =
246 "yes".equals(outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION));
248 "yes".equals(outputProperties.getProperty(OutputKeys.STANDALONE));
249 String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
250 // TODO cdata-section-elements
254 // Discover document element
255 DomDocument resultDoc = (DomDocument) parent;
256 Node root = resultDoc.getDocumentElement();
257 // Add doctype if specified
258 if ((publicId != null || systemId != null) &&
261 // We must know the name of the root element to
262 // create the document type
263 resultDoc.appendChild(new DomDoctype(resultDoc,
268 resultDoc.setBuilding(false);
269 resultDoc.setCheckWellformedness(true);
271 else if (publicId != null || systemId != null)
273 switch (parent.getNodeType())
275 case Node.DOCUMENT_NODE:
276 case Node.DOCUMENT_FRAGMENT_NODE:
277 Document resultDoc = (parent instanceof Document) ?
279 parent.getOwnerDocument();
280 DOMImplementation impl = resultDoc.getImplementation();
281 DocumentType doctype =
282 impl.createDocumentType(resultDoc.getNodeName(),
285 // Try to insert doctype before first element
286 Node ctx = parent.getFirstChild();
287 for (; ctx != null &&
288 ctx.getNodeType() != Node.ELEMENT_NODE;
289 ctx = ctx.getNextSibling())
294 parent.insertBefore(doctype, ctx);
298 parent.appendChild(doctype);
304 parent.setUserData("version", version, stylesheet);
306 if (omitXmlDeclaration)
308 parent.setUserData("omit-xml-declaration", "yes", stylesheet);
312 parent.setUserData("standalone", "yes", stylesheet);
314 if (mediaType != null)
316 parent.setUserData("media-type", mediaType, stylesheet);
318 // Render result to the target device
319 if (outputTarget instanceof DOMResult)
323 DOMResult dr = (DOMResult) outputTarget;
325 dr.setNextSibling(null);
328 else if (outputTarget instanceof StreamResult)
330 StreamResult sr = (StreamResult) outputTarget;
331 IOException ex = null;
334 writeStreamResult(parent, sr, outputMethod, encoding);
336 catch (UnsupportedEncodingException e)
340 writeStreamResult(parent, sr, outputMethod, "UTF-8");
342 catch (IOException e2)
347 catch (IOException e)
353 if (errorListener != null)
355 errorListener.error(new TransformerException(ex));
359 ex.printStackTrace(System.err);
363 else if (outputTarget instanceof SAXResult)
365 SAXResult sr = (SAXResult) outputTarget;
368 ContentHandler ch = sr.getHandler();
369 LexicalHandler lh = sr.getLexicalHandler();
370 if (lh == null && ch instanceof LexicalHandler)
372 lh = (LexicalHandler) ch;
374 SAXSerializer serializer = new SAXSerializer();
375 serializer.serialize(parent, ch, lh);
377 catch (SAXException e)
379 if (errorListener != null)
381 errorListener.error(new TransformerException(e));
385 e.printStackTrace(System.err);
392 * Strip whitespace from the source tree.
394 void strip(Node node)
395 throws TransformerConfigurationException
397 short nt = node.getNodeType();
398 if (nt == Node.ENTITY_REFERENCE_NODE)
400 // Replace entity reference with its content
401 Node parent = node.getParentNode();
402 Node child = node.getFirstChild();
407 while (child != null)
409 Node next = child.getNextSibling();
410 node.removeChild(child);
411 parent.insertBefore(child, node);
414 parent.removeChild(node);
416 if (nt == Node.TEXT_NODE) // CDATA sections ?
418 if (!stylesheet.isPreserved((Text) node))
420 node.getParentNode().removeChild(node);
425 for (Node child = node.getFirstChild(); child != null;
426 child = child.getNextSibling())
434 * Obtain a suitable output stream for writing the result to,
435 * and use the StreamSerializer to write the result tree to the stream.
437 void writeStreamResult(Node node, StreamResult sr, int outputMethod,
441 OutputStream out = null;
444 out = sr.getOutputStream();
447 Writer writer = sr.getWriter();
450 out = new WriterOutputStream(writer);
455 String systemId = sr.getSystemId();
458 URL url = new URL(systemId);
459 URLConnection connection = url.openConnection();
460 connection.setDoOutput(true);
461 out = connection.getOutputStream();
463 catch (MalformedURLException e)
465 out = new FileOutputStream(systemId);
467 catch (UnknownServiceException e)
469 URL url = new URL(systemId);
470 out = new FileOutputStream(url.getPath());
473 out = new BufferedOutputStream(out);
474 StreamSerializer serializer =
475 new StreamSerializer(outputMethod, encoding, null);
476 if (stylesheet != null)
478 Collection celem = stylesheet.outputCdataSectionElements;
479 serializer.setCdataSectionElements(celem);
481 serializer.serialize(node, out);
493 catch (IOException e)
499 void copyChildren(Document dstDoc, Node src, Node dst)
501 Node srcChild = src.getFirstChild();
502 while (srcChild != null)
504 Node dstChild = dstDoc.adoptNode(srcChild);
505 dst.appendChild(dstChild);
506 srcChild = srcChild.getNextSibling();
510 public void setParameter(String name, Object value)
512 if (stylesheet != null)
514 stylesheet.bindings.set(name, value, false);
518 public Object getParameter(String name)
520 if (stylesheet != null)
522 return stylesheet.bindings.get(name, null, 1, 1);
527 public void clearParameters()
529 if (stylesheet != null)
531 stylesheet.bindings.pop(false);
532 stylesheet.bindings.push(false);
536 public void setURIResolver(URIResolver resolver)
538 uriResolver = resolver;
541 public URIResolver getURIResolver()
546 public void setOutputProperties(Properties oformat)
547 throws IllegalArgumentException
551 outputProperties.clear();
555 outputProperties.putAll(oformat);
559 public Properties getOutputProperties()
561 return (Properties) outputProperties.clone();
564 public void setOutputProperty(String name, String value)
565 throws IllegalArgumentException
567 outputProperties.put(name, value);
570 public String getOutputProperty(String name)
571 throws IllegalArgumentException
573 return outputProperties.getProperty(name);
576 public void setErrorListener(ErrorListener listener)
578 errorListener = listener;
581 public ErrorListener getErrorListener()
583 return errorListener;