OSDN Git Service

* external/w3c_dom/Makefile.am: New file.
[pf3gnuchains/gcc-fork.git] / libjava / gnu / xml / transform / TransformerImpl.java
1 /* TransformerImpl.java -- 
2    Copyright (C) 2004 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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)
9 any later version.
10
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.
15
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
19 02111-1307 USA.
20
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
24 combination.
25
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. */
37
38 package gnu.xml.transform;
39
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;
48 import java.net.URL;
49 import java.net.URLConnection;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.LinkedHashMap;
53 import java.util.Map;
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;
81
82 /**
83  * The transformation process for a given stylesheet.
84  *
85  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
86  */
87 class TransformerImpl
88   extends Transformer
89 {
90
91   final TransformerFactoryImpl factory;
92   final Stylesheet stylesheet;
93   URIResolver uriResolver;
94   ErrorListener errorListener;
95   Properties outputProperties;
96
97   TransformerImpl(TransformerFactoryImpl factory,
98                   Stylesheet stylesheet,
99                   Properties outputProperties)
100     throws TransformerConfigurationException
101   {
102     this.factory = factory;
103     uriResolver = factory.userResolver;
104     errorListener = factory.userListener;
105     this.stylesheet = stylesheet;
106     this.outputProperties = outputProperties;
107     if (stylesheet != null)
108       {
109         // Set up parameter context for this transformer
110         stylesheet.bindings.push(false);
111       }
112   }
113
114   public void transform(Source xmlSource, Result outputTarget)
115     throws TransformerException
116   {
117     // Get the source tree
118     DOMSource source;
119     synchronized (factory.resolver)
120       {
121         factory.resolver.setUserResolver(uriResolver);
122         factory.resolver.setUserListener(errorListener);
123         source = factory.resolver.resolveDOM(xmlSource, null, null);
124       }
125     Node context = source.getNode();
126     Document doc = (context instanceof Document) ? (Document) context :
127       context.getOwnerDocument();
128     if (doc instanceof DomDocument)
129       {
130         // Suppress mutation events
131         ((DomDocument) doc).setBuilding(true);
132         // TODO find a better/more generic way of doing this than
133         // casting
134       }
135     // Get the result tree
136     Node parent = null, nextSibling = null;
137     if (outputTarget instanceof DOMResult)
138       {
139         DOMResult dr = (DOMResult) outputTarget;
140         parent = dr.getNode();
141         nextSibling = dr.getNextSibling();
142
143         Document rdoc = (parent instanceof Document) ? (Document) parent :
144           parent.getOwnerDocument();
145         if (rdoc instanceof DomDocument)
146           {
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
152             // casting
153           }
154       }
155     boolean created = false;
156     // Transformation
157     if (stylesheet != null)
158       {
159         if (parent == null)
160           {
161             // Create a new document to hold the result
162             DomDocument resultDoc = new DomDocument();
163             resultDoc.setBuilding(true);
164             resultDoc.setCheckWellformedness(false);
165             parent = resultDoc;
166             created = true;
167           }
168         // Make a copy of the source node, and strip it
169         context = context.cloneNode(true);
170         strip(context);
171         // XSLT transformation
172         try
173           {
174             // Set output properties in the underlying stylesheet
175             ((TransformerOutputProperties) outputProperties).apply();
176             stylesheet.initTopLevelVariables(context);
177             TemplateNode t = stylesheet.getTemplate(null, context, false);
178             if (t != null)
179               {
180                 stylesheet.current = context;
181                 t.apply(stylesheet, null, context, 1, 1, parent, nextSibling);
182               }
183           }
184         catch (TransformerException e)
185           {
186             // Done transforming, reset document
187             if (doc instanceof DomDocument)
188               {
189                 ((DomDocument) doc).setBuilding(false);
190               }
191             throw e;
192           }
193       }
194     else
195       {
196         // Identity transform
197         Node clone = context.cloneNode(true);
198         if (context.getNodeType() != Node.DOCUMENT_NODE)
199           {
200             Document resultDoc;
201             if (parent == null)
202               {
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;
208                 created = true;
209               }
210             else
211               {
212                 resultDoc = (parent instanceof Document) ?
213                   (Document) parent :
214                   parent.getOwnerDocument();
215               }
216             Document sourceDoc = context.getOwnerDocument();
217             if (sourceDoc != resultDoc)
218               {
219                 clone = resultDoc.adoptNode(clone);
220               }
221             if (nextSibling != null)
222               {
223                 parent.insertBefore(clone, nextSibling);
224               }
225             else
226               {
227                 parent.appendChild(clone);
228               }
229           }
230         else
231           {
232             // Cannot append document to another tree
233             parent = clone;
234             created = true;
235           }
236       }
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));
247     boolean standalone = 
248       "yes".equals(outputProperties.getProperty(OutputKeys.STANDALONE));
249     String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
250     // TODO cdata-section-elements
251     // TODO indent
252     if (created)
253       {
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) &&
259             root != null)
260           {
261             // We must know the name of the root element to
262             // create the document type
263             resultDoc.appendChild(new DomDoctype(resultDoc,
264                                                  root.getNodeName(),
265                                                  publicId,
266                                                  systemId));
267           }
268         resultDoc.setBuilding(false);
269         resultDoc.setCheckWellformedness(true);
270       }
271     else if (publicId != null || systemId != null)
272       {
273         switch (parent.getNodeType())
274           {
275           case Node.DOCUMENT_NODE:
276           case Node.DOCUMENT_FRAGMENT_NODE:
277             Document resultDoc = (parent instanceof Document) ?
278               (Document) parent :
279               parent.getOwnerDocument();
280             DOMImplementation impl = resultDoc.getImplementation();
281             DocumentType doctype =
282               impl.createDocumentType(resultDoc.getNodeName(),
283                                       publicId,
284                                       systemId);
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())
290               {
291               }
292             if (ctx != null)
293               {
294                 parent.insertBefore(doctype, ctx);
295               }
296             else
297               {
298                 parent.appendChild(doctype);
299               }
300           }
301       }
302     if (version != null)
303       {
304         parent.setUserData("version", version, stylesheet);
305       }
306     if (omitXmlDeclaration)
307       {
308         parent.setUserData("omit-xml-declaration", "yes", stylesheet);
309       }
310     if (standalone)
311       {
312         parent.setUserData("standalone", "yes", stylesheet);
313       }
314     if (mediaType != null)
315       {
316         parent.setUserData("media-type", mediaType, stylesheet);
317       }
318     // Render result to the target device
319     if (outputTarget instanceof DOMResult)
320       {
321         if (created)
322           {
323             DOMResult dr = (DOMResult) outputTarget;
324             dr.setNode(parent);
325             dr.setNextSibling(null);
326           }
327       }
328     else if (outputTarget instanceof StreamResult)
329       {
330         StreamResult sr = (StreamResult) outputTarget;
331         IOException ex = null;
332         try
333           {
334             writeStreamResult(parent, sr, outputMethod, encoding);
335           }
336         catch (UnsupportedEncodingException e)
337           {
338             try
339               {
340                 writeStreamResult(parent, sr, outputMethod, "UTF-8");
341               }
342             catch (IOException e2)
343               {
344                 ex = e2;
345               }
346           }
347         catch (IOException e)
348           {
349             ex = e;
350           }
351         if (ex != null)
352           {
353             if (errorListener != null)
354               {
355                 errorListener.error(new TransformerException(ex));
356               }
357             else
358               {
359                 ex.printStackTrace(System.err);
360               }
361           }
362       }
363     else if (outputTarget instanceof SAXResult)
364       {
365         SAXResult sr = (SAXResult) outputTarget;
366         try
367           {
368             ContentHandler ch = sr.getHandler();
369             LexicalHandler lh = sr.getLexicalHandler();
370             if (lh == null && ch instanceof LexicalHandler)
371               {
372                 lh = (LexicalHandler) ch;
373               }
374             SAXSerializer serializer = new SAXSerializer();
375             serializer.serialize(parent, ch, lh);
376           }
377         catch (SAXException e)
378           {
379             if (errorListener != null)
380               {
381                 errorListener.error(new TransformerException(e));
382               }
383             else
384               {
385                 e.printStackTrace(System.err);
386               }
387           }
388       }
389   }
390
391   /**
392    * Strip whitespace from the source tree.
393    */
394   void strip(Node node)
395     throws TransformerConfigurationException
396   {
397     short nt = node.getNodeType();
398     if (nt == Node.ENTITY_REFERENCE_NODE)
399       {
400         // Replace entity reference with its content
401         Node parent = node.getParentNode();
402         Node child = node.getFirstChild();
403         if (child != null)
404           {
405             strip(child);
406           }
407         while (child != null)
408           {
409             Node next = child.getNextSibling();
410             node.removeChild(child);
411             parent.insertBefore(child, node);
412             child = next;
413           }
414         parent.removeChild(node);
415       }
416     if (nt == Node.TEXT_NODE) // CDATA sections ?
417       {
418         if (!stylesheet.isPreserved((Text) node))
419           {
420             node.getParentNode().removeChild(node);
421           }
422       }
423     else
424       {
425         for (Node child = node.getFirstChild(); child != null;
426              child = child.getNextSibling())
427           {
428             strip(child);
429           }
430       }
431   }
432
433   /**
434    * Obtain a suitable output stream for writing the result to,
435    * and use the StreamSerializer to write the result tree to the stream.
436    */
437   void writeStreamResult(Node node, StreamResult sr, int outputMethod,
438                          String encoding)
439     throws IOException
440   {
441     OutputStream out = null;
442     try
443       {
444         out = sr.getOutputStream();
445         if (out == null)
446           {
447             Writer writer = sr.getWriter();
448             if (writer != null)
449               {
450                 out = new WriterOutputStream(writer);
451               }
452           }
453         if (out == null)
454           {
455             String systemId = sr.getSystemId();
456             try
457               {
458                 URL url = new URL(systemId);
459                 URLConnection connection = url.openConnection();
460                 connection.setDoOutput(true);
461                 out = connection.getOutputStream();
462               }
463             catch (MalformedURLException e)
464               {
465                 out = new FileOutputStream(systemId);
466               }
467             catch (UnknownServiceException e)
468               {
469                 URL url = new URL(systemId);
470                 out = new FileOutputStream(url.getPath());
471               }
472           }
473         out = new BufferedOutputStream(out);
474         StreamSerializer serializer =
475           new StreamSerializer(outputMethod, encoding, null);
476         if (stylesheet != null)
477           {
478             Collection celem = stylesheet.outputCdataSectionElements;
479             serializer.setCdataSectionElements(celem);
480           }
481         serializer.serialize(node, out);
482         out.flush();
483       }
484     finally
485       {
486         try
487           {
488             if (out != null)
489               {
490                 out.close();
491               }
492           }
493         catch (IOException e)
494           {
495           }
496       }
497   }
498
499   void copyChildren(Document dstDoc, Node src, Node dst)
500   {
501     Node srcChild = src.getFirstChild();
502     while (srcChild != null)
503       {
504         Node dstChild = dstDoc.adoptNode(srcChild);
505         dst.appendChild(dstChild);
506         srcChild = srcChild.getNextSibling();
507       }
508   }
509
510   public void setParameter(String name, Object value)
511   {
512     if (stylesheet != null)
513       {
514         stylesheet.bindings.set(name, value, false);
515       }
516   }
517
518   public Object getParameter(String name)
519   {
520     if (stylesheet != null)
521       {
522         return stylesheet.bindings.get(name, null, 1, 1);
523       }
524     return null;
525   }
526
527   public void clearParameters()
528   {
529     if (stylesheet != null)
530       {
531         stylesheet.bindings.pop(false);
532         stylesheet.bindings.push(false);
533       }
534   }
535
536   public void setURIResolver(URIResolver resolver)
537   {
538     uriResolver = resolver;
539   }
540
541   public URIResolver getURIResolver()
542   {
543     return uriResolver;
544   }
545
546   public void setOutputProperties(Properties oformat)
547     throws IllegalArgumentException
548   {
549     if (oformat == null)
550       {
551         outputProperties.clear();
552       }
553     else
554       {
555         outputProperties.putAll(oformat);
556       }
557   }
558
559   public Properties getOutputProperties()
560   {
561     return (Properties) outputProperties.clone();
562   }
563
564   public void setOutputProperty(String name, String value)
565     throws IllegalArgumentException
566   {
567     outputProperties.put(name, value);
568   }
569
570   public String getOutputProperty(String name)
571     throws IllegalArgumentException
572   {
573     return outputProperties.getProperty(name);
574   }
575
576   public void setErrorListener(ErrorListener listener)
577   {
578     errorListener = listener;
579   }
580
581   public ErrorListener getErrorListener()
582   {
583     return errorListener;
584   }
585
586 }