1 /* TransformerFactoryImpl.java --
2    Copyright (C) 2004,2006 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.FileInputStream;
41 import java.io.FileOutputStream;
42 import java.io.InputStream;
43 import java.io.IOException;
44 import java.io.OutputStream;
45 import java.net.URL;
46 import java.util.Iterator;
47 import java.util.LinkedHashMap;
48 import java.util.LinkedList;
49 import java.util.Map;
50 import java.util.Properties;
51 import javax.xml.transform.ErrorListener;
52 import javax.xml.transform.Source;
53 import javax.xml.transform.Templates;
54 import javax.xml.transform.Transformer;
55 import javax.xml.transform.TransformerConfigurationException;
56 import javax.xml.transform.TransformerException;
57 import javax.xml.transform.TransformerFactory;
58 import javax.xml.transform.URIResolver;
59 import javax.xml.transform.dom.DOMResult;
60 import javax.xml.transform.dom.DOMSource;
61 import javax.xml.transform.sax.SAXResult;
62 import javax.xml.transform.sax.SAXSource;
63 import javax.xml.transform.sax.SAXTransformerFactory;
64 import javax.xml.transform.sax.TemplatesHandler;
65 import javax.xml.transform.sax.TransformerHandler;
66 import javax.xml.transform.stream.StreamResult;
67 import javax.xml.transform.stream.StreamSource;
68 import javax.xml.xpath.XPathFactory;
69 import org.w3c.dom.Document;
70 import org.w3c.dom.Node;
71 import org.xml.sax.XMLFilter;
72 import gnu.xml.dom.DomDocument;
73 
74 /**
75  * GNU transformer factory implementation.
76  *
77  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
78  */
79 public class TransformerFactoryImpl
80   extends SAXTransformerFactory
81 {
82 
83   final XPathFactory xpathFactory;
84   final XSLURIResolver resolver;
85   ErrorListener userListener;
86   URIResolver userResolver;
87 
TransformerFactoryImpl()88   public TransformerFactoryImpl()
89   {
90     xpathFactory = new gnu.xml.xpath.XPathFactoryImpl();
91     resolver = new XSLURIResolver();
92   }
93 
newTransformer(Source source)94   public Transformer newTransformer(Source source)
95     throws TransformerConfigurationException
96   {
97     Stylesheet stylesheet = newStylesheet(source, 0, null);
98     Properties outputProperties =
99       new TransformerOutputProperties(stylesheet);
100     TransformerImpl transformer =
101       new TransformerImpl(this, stylesheet, outputProperties);
102     stylesheet.transformer = transformer;
103     return transformer;
104   }
105 
newTransformer()106   public Transformer newTransformer()
107     throws TransformerConfigurationException
108   {
109     return new TransformerImpl(this, null, new Properties());
110   }
111 
newTemplates(Source source)112   public Templates newTemplates(Source source)
113     throws TransformerConfigurationException
114   {
115     Stylesheet stylesheet = newStylesheet(source, 0, null);
116     return new TemplatesImpl(this, stylesheet);
117   }
118 
newStylesheet(Source source, int precedence, Stylesheet parent)119   Stylesheet newStylesheet(Source source, int precedence, Stylesheet parent)
120     throws TransformerConfigurationException
121   {
122     Document doc = null;
123     String systemId = null;
124     if (source != null)
125       {
126         try
127           {
128             DOMSource ds;
129             synchronized (resolver)
130               {
131                 resolver.setUserResolver(userResolver);
132                 resolver.setUserListener(userListener);
133                 ds = resolver.resolveDOM(source, null, null);
134               }
135             Node node = ds.getNode();
136             if (node == null)
137               {
138                 throw new TransformerConfigurationException("no source document");
139               }
140             doc = (node instanceof Document) ? (Document) node :
141               node.getOwnerDocument();
142             systemId = ds.getSystemId();
143           }
144         catch (TransformerException e)
145           {
146             throw new TransformerConfigurationException(e);
147           }
148       }
149     return new Stylesheet(this, parent, doc, systemId, precedence);
150   }
151 
getAssociatedStylesheet(Source source, String media, String title, String charset)152   public Source getAssociatedStylesheet(Source source,
153                                         String media,
154                                         String title,
155                                         String charset)
156     throws TransformerConfigurationException
157   {
158     try
159       {
160         DOMSource ds;
161         synchronized (resolver)
162           {
163             resolver.setUserResolver(userResolver);
164             resolver.setUserListener(userListener);
165             ds = resolver.resolveDOM(source, null, null);
166           }
167         Node node = ds.getNode();
168         if (node == null)
169           {
170             throw new TransformerConfigurationException("no source document");
171           }
172         Document doc = (node instanceof Document) ? (Document) node :
173           node.getOwnerDocument();
174         LinkedList matches = new LinkedList();
175         for (node = doc.getFirstChild();
176              node != null;
177              node = node.getNextSibling())
178           {
179             if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
180                 "xml-stylesheet".equals(node.getNodeName()))
181               {
182                 Map params = parseParameters(node.getNodeValue());
183                 if (media != null && !media.equals(params.get("media")))
184                   {
185                     continue;
186                   }
187                 if (title != null && !title.equals(params.get("title")))
188                   {
189                     continue;
190                   }
191                 if (charset != null && !charset.equals(params.get("charset")))
192                   {
193                     continue;
194                   }
195                 String href = (String) params.get("href");
196                 URL url = resolver.resolveURL(null, node.getBaseURI(), href);
197                 matches.add(url);
198               }
199           }
200         switch (matches.size())
201           {
202           case 0:
203             return null;
204           case 1:
205             return new StreamSource(((URL) matches.getFirst()).toString());
206           default:
207             // Create a source representing a stylesheet with a list of
208             // imports
209             DomDocument ssDoc = new DomDocument();
210             ssDoc.setBuilding(true);
211             // Create document element
212             Node root =
213               ssDoc.createElementNS(Stylesheet.XSL_NS, "stylesheet");
214             Node version =
215               ssDoc.createAttributeNS(null, "version");
216             version.setNodeValue("1.0");
217             root.getAttributes().setNamedItemNS(version);
218             ssDoc.appendChild(root);
219             // Create xsl:import for each URL
220             for (Iterator i = matches.iterator(); i.hasNext(); )
221               {
222                 URL url = (URL) i.next();
223                 Node imp =
224                   ssDoc.createElementNS(Stylesheet.XSL_NS, "import");
225                 Node href =
226                   ssDoc.createAttributeNS(null, "href");
227                 href.setNodeValue(url.toString());
228                 imp.getAttributes().setNamedItemNS(href);
229                 root.appendChild(imp);
230               }
231             ssDoc.setBuilding(false);
232             return new DOMSource(ssDoc);
233           }
234       }
235     catch (IOException e)
236       {
237         throw new TransformerConfigurationException(e);
238       }
239     catch (TransformerException e)
240       {
241         throw new TransformerConfigurationException(e);
242       }
243   }
244 
parseParameters(String data)245   Map parseParameters(String data)
246   {
247     Map ret = new LinkedHashMap();
248     int len = data.length();
249     String key = null;
250     int start = 0;
251     char quoteChar = '\u0000';
252     for (int i = 0; i < len; i++)
253       {
254         char c = data.charAt(i);
255         if (quoteChar == '\u0000' && c == ' ')
256           {
257             if (key == null && start < i)
258               {
259                 key = data.substring(start, i);
260               }
261             else
262               {
263                 String val = unquote(data.substring(start, i).trim());
264                 ret.put(key, val);
265                 key = null;
266               }
267             start = i + 1;
268           }
269         else if (c == '"')
270           {
271             quoteChar = (quoteChar == c) ? '\u0000' : c;
272           }
273         else if (c == '\'')
274           {
275             quoteChar = (quoteChar == c) ? '\u0000' : c;
276           }
277       }
278     if (start < len && key != null)
279       {
280         String val = unquote(data.substring(start, len).trim());
281         ret.put(key, val);
282       }
283     return ret;
284   }
285 
unquote(String text)286   String unquote(String text)
287   {
288     int end = text.length() - 1;
289     if (text.charAt(0) == '\'' && text.charAt(end) == '\'')
290       {
291         return text.substring(1, end);
292       }
293     if (text.charAt(0) == '"' && text.charAt(end) == '"')
294       {
295         return text.substring(1, end);
296       }
297     return text;
298   }
299 
setURIResolver(URIResolver resolver)300   public void setURIResolver(URIResolver resolver)
301   {
302     userResolver = resolver;
303   }
304 
getURIResolver()305   public URIResolver getURIResolver()
306   {
307     return userResolver;
308   }
309 
setFeature(String name, boolean value)310   public void setFeature(String name, boolean value)
311     throws TransformerConfigurationException
312   {
313     throw new TransformerConfigurationException("not supported");
314   }
315 
getFeature(String name)316   public boolean getFeature(String name)
317   {
318     if (SAXSource.FEATURE.equals(name) ||
319         SAXResult.FEATURE.equals(name) ||
320         StreamSource.FEATURE.equals(name) ||
321         StreamResult.FEATURE.equals(name) ||
322         DOMSource.FEATURE.equals(name) ||
323         DOMResult.FEATURE.equals(name) ||
324         SAXTransformerFactory.FEATURE.equals(name))
325       {
326         return true;
327       }
328     return false;
329   }
330 
setAttribute(String name, Object value)331   public void setAttribute(String name, Object value)
332     throws IllegalArgumentException
333   {
334     throw new IllegalArgumentException("not supported");
335   }
336 
getAttribute(String name)337   public Object getAttribute(String name)
338     throws IllegalArgumentException
339   {
340     throw new IllegalArgumentException("not supported");
341   }
342 
setErrorListener(ErrorListener listener)343   public void setErrorListener(ErrorListener listener)
344     throws IllegalArgumentException
345   {
346     userListener = listener;
347   }
348 
getErrorListener()349   public ErrorListener getErrorListener()
350   {
351     return userListener;
352   }
353 
354   // -- SAXTransformerFactory --
355 
newTemplatesHandler()356   public TemplatesHandler newTemplatesHandler()
357     throws TransformerConfigurationException
358   {
359     return new SAXTemplatesHandler(this);
360   }
361 
newTransformerHandler()362   public TransformerHandler newTransformerHandler()
363     throws TransformerConfigurationException
364   {
365     Transformer transformer = newTransformer();
366     return new SAXTransformerHandler(this, transformer);
367   }
368 
newTransformerHandler(Source source)369   public TransformerHandler newTransformerHandler(Source source)
370     throws TransformerConfigurationException
371   {
372     Transformer transformer = newTransformer(source);
373     return new SAXTransformerHandler(this, transformer);
374   }
375 
newTransformerHandler(Templates templates)376   public TransformerHandler newTransformerHandler(Templates templates)
377     throws TransformerConfigurationException
378   {
379     Transformer transformer = templates.newTransformer();
380     return new SAXTransformerHandler(this, transformer);
381   }
382 
newXMLFilter(Source source)383   public XMLFilter newXMLFilter(Source source)
384     throws TransformerConfigurationException
385   {
386     throw new UnsupportedOperationException();
387   }
388 
newXMLFilter(Templates templates)389   public XMLFilter newXMLFilter(Templates templates)
390     throws TransformerConfigurationException
391   {
392     throw new UnsupportedOperationException();
393   }
394 
395   // -- SAXTransformerFactory end --
396 
397   /**
398    * Syntax: TransformerFactoryImpl [<stylesheet> [<input> [<output>]]]
399    */
main(String[] args)400   public static void main(String[] args)
401     throws Exception
402   {
403     InputStream stylesheet = null, in = null;
404     OutputStream out = null;
405     try
406       {
407         if (args.length > 0)
408           {
409             stylesheet = new FileInputStream(args[0]);
410             if (args.length > 1)
411               {
412                 in = new FileInputStream(args[1]);
413                 if (args.length > 2)
414                   out = new FileOutputStream(args[2]);
415               }
416           }
417         if (in == null)
418           in = System.in;
419         if (out == null)
420           out = System.out;
421         TransformerFactory f = new TransformerFactoryImpl();
422         Transformer t = (stylesheet != null) ?
423           f.newTransformer(new StreamSource(stylesheet)) :
424           f.newTransformer();
425         t.transform(new StreamSource(in), new StreamResult(out));
426       }
427     finally
428       {
429         if (stylesheet != null)
430           stylesheet.close();
431         if (in != null && in instanceof FileInputStream)
432           in.close();
433         if (out != null && out instanceof FileOutputStream)
434           out.close();
435       }
436   }
437 
438 }
439