1 /* TransformerFactoryImpl.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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 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.IOException;
41 import java.net.URL;
42 import java.util.Iterator;
43 import java.util.LinkedHashMap;
44 import java.util.LinkedList;
45 import java.util.Map;
46 import java.util.Properties;
47 import javax.xml.transform.ErrorListener;
48 import javax.xml.transform.Source;
49 import javax.xml.transform.Templates;
50 import javax.xml.transform.Transformer;
51 import javax.xml.transform.TransformerConfigurationException;
52 import javax.xml.transform.TransformerException;
53 import javax.xml.transform.TransformerFactory;
54 import javax.xml.transform.URIResolver;
55 import javax.xml.transform.dom.DOMResult;
56 import javax.xml.transform.dom.DOMSource;
57 import javax.xml.transform.sax.SAXResult;
58 import javax.xml.transform.sax.SAXSource;
59 import javax.xml.transform.stream.StreamResult;
60 import javax.xml.transform.stream.StreamSource;
61 import javax.xml.xpath.XPathFactory;
62 import org.w3c.dom.Document;
63 import org.w3c.dom.Node;
64 import gnu.xml.dom.DomDocument;
65 
66 /**
67  * GNU transformer factory implementation.
68  *
69  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
70  */
71 public class TransformerFactoryImpl
72   extends TransformerFactory
73 {
74 
75   final XPathFactory xpathFactory;
76 	final XSLURIResolver resolver;
77   ErrorListener userListener;
78   URIResolver userResolver;
79 
TransformerFactoryImpl()80   public TransformerFactoryImpl()
81   {
82     xpathFactory = new gnu.xml.xpath.XPathFactoryImpl();
83     resolver = new XSLURIResolver();
84   }
85 
newTransformer(Source source)86   public Transformer newTransformer(Source source)
87     throws TransformerConfigurationException
88   {
89     Stylesheet stylesheet = newStylesheet(source, 0, null);
90     Properties outputProperties =
91       new TransformerOutputProperties(stylesheet);
92     TransformerImpl transformer =
93       new TransformerImpl(this, stylesheet, outputProperties);
94     stylesheet.transformer = transformer;
95     return transformer;
96   }
97 
newTransformer()98   public Transformer newTransformer()
99     throws TransformerConfigurationException
100   {
101     return new TransformerImpl(this, null, new Properties());
102   }
103 
newTemplates(Source source)104   public Templates newTemplates(Source source)
105     throws TransformerConfigurationException
106   {
107     Stylesheet stylesheet = newStylesheet(source, 0, null);
108     return new TemplatesImpl(this, stylesheet);
109   }
110 
newStylesheet(Source source, int precedence, Stylesheet parent)111   Stylesheet newStylesheet(Source source, int precedence, Stylesheet parent)
112     throws TransformerConfigurationException
113   {
114     Document doc = null;
115     String systemId = null;
116     if (source != null)
117       {
118         try
119           {
120             DOMSource ds;
121             synchronized (resolver)
122               {
123                 resolver.setUserResolver(userResolver);
124                 resolver.setUserListener(userListener);
125                 ds = resolver.resolveDOM(source, null, null);
126               }
127             Node node = ds.getNode();
128             if (node == null)
129               {
130                 throw new TransformerConfigurationException("no source document");
131               }
132             doc = (node instanceof Document) ? (Document) node :
133               node.getOwnerDocument();
134             systemId = ds.getSystemId();
135           }
136         catch (TransformerException e)
137           {
138             throw new TransformerConfigurationException(e);
139           }
140       }
141     return new Stylesheet(this, parent, doc, systemId, precedence);
142   }
143 
getAssociatedStylesheet(Source source, String media, String title, String charset)144   public Source getAssociatedStylesheet(Source source,
145                                         String media,
146                                         String title,
147                                         String charset)
148     throws TransformerConfigurationException
149   {
150     try
151       {
152         DOMSource ds;
153         synchronized (resolver)
154           {
155             resolver.setUserResolver(userResolver);
156             resolver.setUserListener(userListener);
157             ds = resolver.resolveDOM(source, null, null);
158           }
159         Node node = ds.getNode();
160         if (node == null)
161           {
162             throw new TransformerConfigurationException("no source document");
163           }
164         Document doc = (node instanceof Document) ? (Document) node :
165           node.getOwnerDocument();
166         LinkedList matches = new LinkedList();
167         for (node = doc.getFirstChild();
168              node != null;
169              node = node.getNextSibling())
170           {
171             if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
172                 "xml-stylesheet".equals(node.getNodeName()))
173               {
174                 Map params = parseParameters(node.getNodeValue());
175                 if (media != null && !media.equals(params.get("media")))
176                   {
177                     continue;
178                   }
179                 if (title != null && !title.equals(params.get("title")))
180                   {
181                     continue;
182                   }
183                 if (charset != null && !charset.equals(params.get("charset")))
184                   {
185                     continue;
186                   }
187                 String href = (String) params.get("href");
188                 URL url = resolver.resolveURL(null, node.getBaseURI(), href);
189                 matches.add(url);
190               }
191           }
192         switch (matches.size())
193           {
194           case 0:
195             return null;
196           case 1:
197             return new StreamSource(((URL) matches.getFirst()).toString());
198           default:
199             // Create a source representing a stylesheet with a list of
200             // imports
201             DomDocument ssDoc = new DomDocument();
202             ssDoc.setBuilding(true);
203             // Create document element
204             Node root =
205               ssDoc.createElementNS(Stylesheet.XSL_NS, "stylesheet");
206             Node version =
207               ssDoc.createAttributeNS(null, "version");
208             version.setNodeValue("1.0");
209             root.getAttributes().setNamedItemNS(version);
210             ssDoc.appendChild(root);
211             // Create xsl:import for each URL
212             for (Iterator i = matches.iterator(); i.hasNext(); )
213               {
214                 URL url = (URL) i.next();
215                 Node imp =
216                   ssDoc.createElementNS(Stylesheet.XSL_NS, "import");
217                 Node href =
218                   ssDoc.createAttributeNS(null, "href");
219                 href.setNodeValue(url.toString());
220                 imp.getAttributes().setNamedItemNS(href);
221                 root.appendChild(imp);
222               }
223             ssDoc.setBuilding(false);
224             return new DOMSource(ssDoc);
225           }
226       }
227     catch (IOException e)
228       {
229         throw new TransformerConfigurationException(e);
230       }
231     catch (TransformerException e)
232       {
233         throw new TransformerConfigurationException(e);
234       }
235   }
236 
parseParameters(String data)237   Map parseParameters(String data)
238   {
239     Map ret = new LinkedHashMap();
240     int len = data.length();
241     String key = null;
242     int start = 0;
243     char quoteChar = '\u0000';
244     for (int i = 0; i < len; i++)
245       {
246         char c = data.charAt(i);
247         if (quoteChar == '\u0000' && c == ' ')
248           {
249             if (key == null && start < i)
250               {
251                 key = data.substring(start, i);
252               }
253             else
254               {
255                 String val = unquote(data.substring(start, i).trim());
256                 ret.put(key, val);
257                 key = null;
258               }
259             start = i + 1;
260           }
261         else if (c == '"')
262           {
263             quoteChar = (quoteChar == c) ? '\u0000' : c;
264           }
265         else if (c == '\'')
266           {
267             quoteChar = (quoteChar == c) ? '\u0000' : c;
268           }
269       }
270     if (start < len && key != null)
271       {
272         String val = unquote(data.substring(start, len).trim());
273         ret.put(key, val);
274       }
275     return ret;
276   }
277 
unquote(String text)278   String unquote(String text)
279   {
280     int end = text.length() - 1;
281     if (text.charAt(0) == '\'' && text.charAt(end) == '\'')
282       {
283         return text.substring(1, end);
284       }
285     if (text.charAt(0) == '"' && text.charAt(end) == '"')
286       {
287         return text.substring(1, end);
288       }
289     return text;
290   }
291 
setURIResolver(URIResolver resolver)292   public void setURIResolver(URIResolver resolver)
293   {
294     userResolver = resolver;
295   }
296 
getURIResolver()297   public URIResolver getURIResolver()
298   {
299     return userResolver;
300   }
301 
setFeature(String name, boolean value)302   public void setFeature(String name, boolean value)
303     throws TransformerConfigurationException
304   {
305     throw new TransformerConfigurationException("not supported");
306   }
307 
getFeature(String name)308   public boolean getFeature(String name)
309   {
310     if (SAXSource.FEATURE.equals(name) ||
311         SAXResult.FEATURE.equals(name) ||
312         StreamSource.FEATURE.equals(name) ||
313         StreamResult.FEATURE.equals(name) ||
314         DOMSource.FEATURE.equals(name) ||
315         DOMResult.FEATURE.equals(name))
316       {
317         return true;
318       }
319     return false;
320   }
321 
setAttribute(String name, Object value)322   public void setAttribute(String name, Object value)
323     throws IllegalArgumentException
324   {
325     throw new IllegalArgumentException("not supported");
326   }
327 
getAttribute(String name)328   public Object getAttribute(String name)
329     throws IllegalArgumentException
330   {
331     throw new IllegalArgumentException("not supported");
332   }
333 
setErrorListener(ErrorListener listener)334   public void setErrorListener(ErrorListener listener)
335     throws IllegalArgumentException
336   {
337     userListener = listener;
338   }
339 
getErrorListener()340   public ErrorListener getErrorListener()
341   {
342     return userListener;
343   }
344 
345 }
346