View Javadoc

1   /*
2    * Copyright 2000-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.jetspeed.util;
18  
19  //java stuff
20  import java.io.IOException;
21  import java.io.InputStreamReader;
22  import java.io.OutputStreamWriter;
23  import java.io.PipedInputStream;
24  import java.io.PipedOutputStream;
25  import java.io.Reader;
26  import java.io.StringWriter;
27  import java.io.Writer;
28  import java.util.Iterator;
29  import java.util.Map;
30  
31  //Trax support
32  import javax.xml.transform.TransformerFactory;
33  import javax.xml.transform.Transformer;
34  import javax.xml.transform.Templates;
35  //import javax.xml.transform.stream.StreamSource;
36  import javax.xml.transform.stream.StreamResult;
37  import javax.xml.transform.dom.DOMSource;
38  import javax.xml.transform.sax.SAXSource;
39  import javax.xml.transform.sax.SAXTransformerFactory;
40  import javax.xml.transform.sax.TemplatesHandler;
41  import javax.xml.transform.sax.TransformerHandler;              
42  
43  //xpath objects
44  import org.apache.xpath.objects.XString;
45  
46  //SAX Suport
47  import org.xml.sax.XMLReader;
48  import org.xml.sax.SAXException;
49  import org.xml.sax.helpers.XMLReaderFactory;
50  import org.xml.sax.InputSource;
51  
52  //DOM Support
53  import org.w3c.dom.Document;
54  
55  //Jetspeed stuff
56  import org.apache.jetspeed.cache.disk.JetspeedDiskCache;
57  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
58  import org.apache.jetspeed.services.logging.JetspeedLogger;
59  import org.apache.jetspeed.xml.JetspeedXMLEntityResolver;
60  
61  
62  
63  
64  /***
65   * Provides a very simple mechanism to transform a document using XSLT using 
66   * one XML document and another XSL document.  This implementation uses the TRAX API.
67   * It can be used with any TRAX transformer.   This can be used for very 
68   * simple XML -> XSL processing to reduce the complexity and possibility of a 
69   * runtime failure.
70   *
71   * @author <a href="mailto:burton@apache.org">Kevin A. Burton</a>
72   * @author <a href="mailto:sgala@apache.org">Santiago Gala</a>
73   * @version $Id: SimpleTransform.java,v 1.23 2004/02/23 03:23:42 jford Exp $
74   */
75  public class SimpleTransform
76  {
77      /***
78       * Static initialization of the logger for this class
79       */    
80      private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(SimpleTransform.class.getName());
81      
82      //FIXME: This code should go into the Turbine XSLTservice.
83      //Also, it is a temporary hack, as it should be configurable,
84      // and done later.
85      static
86      {
87          try 
88          {
89              if( System.getProperty( "org.xml.sax.driver" ) == null )
90              {
91                  System.setProperty( "org.xml.sax.driver",
92                                      "org.apache.xerces.parsers.SAXParser" );
93              }
94          }
95          catch (Throwable t)
96          {
97              //be very cautious here. We are in class initialization.
98              t.printStackTrace();
99          }
100     }
101     
102     /***
103      * Given a a DOM and a URL to a stylesheet,
104      * transform the original document.
105      */
106     public static String transform( Document doc,
107                                     String stylesheet_url)
108         throws SAXException
109     {
110         return transform( doc, stylesheet_url, null );
111     }
112 
113     
114     /***
115      * Given a a DOM and a URL to a stylesheet,
116      * transform the original document,
117      * passing parameters to the stylesheet
118      */
119     public static String transform( Document doc,
120                                     String stylesheet_url,
121                                     Map params)
122         throws SAXException
123     {
124 
125         // Instantiate a TransformerFactory.
126         TransformerFactory tFactory = TransformerFactory.newInstance();
127         // Determine whether the TransformerFactory supports the use of SAXSource 
128         // and SAXResult
129         if (!tFactory.getFeature(SAXTransformerFactory.FEATURE) )
130         {
131             logger.error( "SimpleTransform: nobody told you that we need a SAX Transformer?" );
132             throw new SAXException( "Invalid SAX Tranformer" );
133         }
134         try
135         {
136             // Cast the TransformerFactory.
137             SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);
138             // Create a ContentHandler to handle parsing of the stylesheet.
139             TemplatesHandler templatesHandler = saxTFactory.newTemplatesHandler();
140 
141             // Create an XMLReader and set its ContentHandler.
142             XMLReader reader = XMLReaderFactory.createXMLReader();
143             reader.setContentHandler(templatesHandler);
144             // Set it to solve Entities through Jetspeed URL Manager
145             reader.setEntityResolver( new JetspeedXMLEntityResolver() );
146     
147             // Parse the stylesheet.                       
148             final InputSource xstyle = new InputSource( JetspeedDiskCache.getInstance()
149                                                         .getEntry( stylesheet_url ).getReader() );
150             xstyle.setSystemId( stylesheet_url );
151             reader.parse( xstyle );
152 
153             //Get the Templates object from the ContentHandler.
154             Templates templates = templatesHandler.getTemplates();
155             // Create a ContentHandler to handle parsing of the XML source.  
156             TransformerHandler handler 
157                 = saxTFactory.newTransformerHandler(templates);
158         
159             // Reset the XMLReader's ContentHandler.
160             reader.setContentHandler(handler);  
161 
162             // Set the ContentHandler to also function as a LexicalHandler, which
163             // includes "lexical" events (e.g., comments and CDATA).
164             try
165             { 
166                 reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
167             } 
168             catch( org.xml.sax.SAXNotRecognizedException e ) {}
169 
170             final Transformer processor = handler.getTransformer();
171 
172             if( params != null ) {
173                 Iterator keys = params.keySet().iterator();
174                 while( keys.hasNext() )
175                 {
176                     String name  = (String) keys.next();
177                     String value = (String) params.get(name);
178                     processor.setParameter(name, 
179                                            value ); //FIXME: maybe we need to quote again...
180                     // was processor.createXString( value)
181                 }
182             }
183         
184             StringWriter pw = new StringWriter();
185         
186             // Have the XSLTProcessor processor object transform "foo.xml" to
187             // System.out, using the XSLT instructions found in "foo.xsl".
188             processor.transform( new DOMSource( doc ),
189                                  new StreamResult( pw ) );
190                       
191             try
192             {
193 
194                 pw.flush();
195                 pw.close();
196             
197             } 
198             catch (IOException e)
199             {
200                 //should never really happen
201                 logger.error("Exception",  e);
202             }
203             return pw.toString();
204         } 
205         catch (Exception e)
206         {
207             logger.error( "Invalid SAX Transformer: " , e );
208             throw new SAXException( "problem in SAX transform: " + e.toString() );
209         }    
210     }
211     
212     /***
213      * Given a URL to an XML file and a URL to a stylesheet, transform the 
214      * original document.
215      */
216     public static String transform( String url,
217                                     String stylesheet_url )
218         throws SAXException
219     {
220 
221         return transform( url, stylesheet_url, null );
222     
223     }
224     
225     /***
226      * Given a URL to an XML file and a URL to a stylesheet, transform the 
227      * original document.
228      */
229     public static String transform( String url,
230                                     String stylesheet_url,
231                                     Map params )
232         throws SAXException
233     {
234 
235         //bring these URLs local if they happen to be remote
236 
237         InputSource in;
238         InputSource style;
239         try
240         {
241             in = new InputSource( JetspeedDiskCache.getInstance().getEntry( url ).getReader() );
242             style = new InputSource( JetspeedDiskCache.getInstance().getEntry( stylesheet_url ).getReader() );
243         } 
244         catch (IOException e)
245         {
246             logger.error("Exception",  e);
247             //at this point we can just use the original url and stylesheet_url so this shouldn't be a problem
248             in = new InputSource( url ); 
249             style = new InputSource( stylesheet_url );
250         }
251         
252         if ( logger.isInfoEnabled() )
253         {
254             logger.info( "SimpleTransform:  transforming url: " + 
255                   url + 
256                   " with stylesheet: " + 
257                   stylesheet_url );
258         }
259 
260         in.setSystemId( url );
261         style.setSystemId( stylesheet_url );
262 
263         return transform( in,
264                           style,
265                           params );
266     
267     }
268     
269     /***
270      * Used internally to handle doing XSLT transformations directly.
271      */
272     public static String transform( InputSource content, 
273                                     InputSource stylesheet,
274                                     Map params)
275         throws SAXException
276     {
277 
278         // Instantiate a TransformerFactory.
279         TransformerFactory tFactory = TransformerFactory.newInstance();
280         // Determine whether the TransformerFactory supports the use of SAXSource 
281         // and SAXResult
282         if (!tFactory.getFeature(SAXTransformerFactory.FEATURE) )
283         {
284             logger.error( "SimpleTransform: nobody told you that we need a SAX Transformer?" );
285             throw new SAXException( "Invalid SAX Tranformer" );
286         }
287         try
288         {
289             // Cast the TransformerFactory.
290             SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);
291             // Create a ContentHandler to handle parsing of the stylesheet.
292             TemplatesHandler templatesHandler = saxTFactory.newTemplatesHandler();
293 
294             // Create an XMLReader and set its ContentHandler.
295             XMLReader reader = XMLReaderFactory.createXMLReader();
296             reader.setContentHandler(templatesHandler);
297     
298             // Parse the stylesheet.                       
299             reader.parse( stylesheet );
300 
301             //Get the Templates object from the ContentHandler.
302             Templates templates = templatesHandler.getTemplates();
303             // Create a ContentHandler to handle parsing of the XML source.  
304             TransformerHandler handler 
305                 = saxTFactory.newTransformerHandler(templates);
306         
307             // Reset the XMLReader's ContentHandler.
308             reader.setContentHandler(handler);  
309 
310             // Set the ContentHandler to also function as a LexicalHandler, which
311             // includes "lexical" events (e.g., comments and CDATA). 
312             try
313             {
314                 reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
315             } 
316             catch( org.xml.sax.SAXNotRecognizedException e ) {}
317             
318             final Transformer processor = handler.getTransformer();
319 
320         
321             if( params != null )
322             {
323                 Iterator keys = params.keySet().iterator();
324                 while( keys.hasNext() )
325                 {
326                     String name  = (String) keys.next();
327                     String value = (String) params.get(name);
328                     processor.setParameter(name, 
329                                            new XString( value ) 
330                          /*FIXME: was processor.createXString( value) */ );
331                 }
332             }
333 
334             StringWriter pw = new StringWriter();
335         
336             // Have the XSLTProcessor processor object transform "foo.xml" to
337             // System.out, using the XSLT instructions found in "foo.xsl".
338             processor.transform( new SAXSource( content ),
339                                  new StreamResult( pw ) );
340                       
341             try
342             {
343                 pw.flush();
344                 pw.close();
345             } 
346             catch (IOException e)
347             {
348                 //should never really happen
349                 logger.error("Exception",  e);
350             }
351             return pw.toString();
352         }
353         catch (Exception e)
354         {
355             logger.error( "Invalid SAX Transformer: " , e);
356             throw new SAXException( "problem in SAX transform: " + e.toString() );
357         }    
358     }
359 
360     /***
361      * Perform a event based parsing of the given content_url, 
362      * process it with the XSLT stylesheet stylesheet_url, using the params
363      * parameters, and return a Reader that will do the transformation dynamically.
364      *
365      * @param content_url The url of the xml document
366      * @param stylesheet_url The url of the stylesheet
367      * @param params A Map containing stylesheet parameters
368      * @return a Reader on the transformed document
369      *
370      */
371     public static Reader SAXTransform( String content_url, 
372                                        String stylesheet_url,
373                                        Map params) throws IOException
374     {
375 
376         // Instantiate a TransformerFactory.
377         TransformerFactory tFactory = TransformerFactory.newInstance();
378         // Determine whether the TransformerFactory supports the use of SAXSource 
379         // and SAXResult
380         if (!tFactory.getFeature(SAXTransformerFactory.FEATURE) )
381         {
382             logger.error( "SimpleTransform: nobody told you that we need a SAX Transformer?" );
383             throw new IOException( "Invalid SAX Tranformer" );
384         }
385         try
386         {
387             // Cast the TransformerFactory.
388             SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);
389             // Create a ContentHandler to handle parsing of the stylesheet.
390             TemplatesHandler templatesHandler = saxTFactory.newTemplatesHandler();
391 
392             // Create an XMLReader and set its ContentHandler.
393             XMLReader reader = XMLReaderFactory.createXMLReader();
394             reader.setContentHandler(templatesHandler);
395             // Set it to solve Entities through Jetspeed URL Manager
396             reader.setEntityResolver( new JetspeedXMLEntityResolver() );
397 
398             // Parse the stylesheet.                       
399             InputSource style = new InputSource( JetspeedDiskCache.getInstance()
400                                                  .getEntry( stylesheet_url ).getReader() );
401             style.setSystemId( stylesheet_url );
402             final InputSource xstyle = style;
403 
404             reader.parse( xstyle );
405 
406             //Get the Templates object from the ContentHandler.
407             Templates templates = templatesHandler.getTemplates();
408             // Create a ContentHandler to handle parsing of the XML source.  
409             TransformerHandler handler 
410                 = saxTFactory.newTransformerHandler(templates);
411         
412             // Reset the XMLReader's ContentHandler.
413             reader.setContentHandler(handler);  
414 
415             // Set the ContentHandler to also function as a LexicalHandler, which
416             // includes "lexical" events (e.g., comments and CDATA). 
417             try
418             {
419                 reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
420             }
421             catch( org.xml.sax.SAXNotRecognizedException e ) {}
422 
423             final Transformer processor = handler.getTransformer();
424 
425             //Set the parameters (if any)
426             if( params != null )
427             {
428                 Iterator keys = params.keySet().iterator();
429                 while( keys.hasNext() )
430                 {
431                     String name  = (String) keys.next();
432                     String value = (String) params.get(name);
433                     //FIXME: maybe we need to quote again...
434                     // was processor.createXString( value)
435                     processor.setParameter(name, 
436                                            new XString( value ) );
437                 }
438             }
439 
440             PipedInputStream pis = new PipedInputStream();
441             PipedOutputStream pos = new PipedOutputStream( pis );
442             try
443             {
444 
445                 final Writer pw = new OutputStreamWriter( pos, "utf-8" );
446                 InputSource is = new InputSource( JetspeedDiskCache.getInstance()
447                                                   .getEntry( content_url ).getReader() );
448                 is.setSystemId( content_url );
449 
450                 final SAXSource xinput = new SAXSource( is );
451                 //Perform the transformation on a new thread, using
452                 // PipedStreams 
453                 Thread t = new Thread( new Runnable()
454                     {
455                         public void run()
456                         {
457                             // Have the processor object transform 
458                             // "foo.xml" to
459                             // System.out, using the XSLT instructions 
460                             //found in "foo.xsl".
461                             logger.debug("Starting SAX thread...");
462                             try 
463                             {
464                                 processor.transform( xinput,
465                                                      new StreamResult( pw ) );
466                                 pw.close();
467                                 logger.debug("...ending SAX thread.");
468                             } 
469                             catch( Exception se)
470                             {
471                                 logger.debug("Error in SAXTransform" + se.toString(), se );
472                             }
473                         }
474                     } );
475                 t.start();
476             } 
477             catch (java.io.UnsupportedEncodingException uee)
478             {
479                 logger.error("Need utf-8 encoding to SAXTransform", uee);
480             }
481             return new InputStreamReader ( pis, "utf-8" );
482         } 
483         catch (Exception e)
484         {
485             logger.error( "Invalid SAX Transformer:" , e);
486             throw new IOException( "problem in SAX transform: " + e.toString() );
487         }    
488     }
489 
490 }