KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > xslt > XSLTProcessorImpl


1 /*
2  * Copyright 1999-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 package org.apache.cocoon.components.xslt;
17
18 import java.io.File JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.util.HashMap JavaDoc;
21
22 import javax.xml.transform.Result JavaDoc;
23 import javax.xml.transform.Templates JavaDoc;
24 import javax.xml.transform.Transformer JavaDoc;
25 import javax.xml.transform.TransformerException JavaDoc;
26 import javax.xml.transform.TransformerFactory JavaDoc;
27 import javax.xml.transform.URIResolver JavaDoc;
28 import javax.xml.transform.sax.SAXTransformerFactory JavaDoc;
29 import javax.xml.transform.sax.TemplatesHandler JavaDoc;
30 import javax.xml.transform.sax.TransformerHandler JavaDoc;
31 import javax.xml.transform.stream.StreamSource JavaDoc;
32
33 import org.apache.avalon.framework.activity.Disposable;
34 import org.apache.avalon.framework.component.ComponentException;
35 import org.apache.avalon.framework.component.ComponentManager;
36 import org.apache.avalon.framework.component.Composable;
37 import org.apache.avalon.framework.logger.AbstractLogEnabled;
38 import org.apache.avalon.framework.parameters.ParameterException;
39 import org.apache.avalon.framework.parameters.Parameterizable;
40 import org.apache.avalon.framework.parameters.Parameters;
41 import org.apache.cocoon.ProcessingException;
42 import org.apache.cocoon.util.ClassUtils;
43 import org.apache.cocoon.util.TraxErrorHandler;
44 import org.apache.excalibur.source.Source;
45 import org.apache.excalibur.source.SourceException;
46 import org.apache.excalibur.source.SourceResolver;
47 import org.apache.excalibur.store.Store;
48 import org.xml.sax.ContentHandler JavaDoc;
49 import org.xml.sax.SAXException JavaDoc;
50 import org.xml.sax.XMLFilter JavaDoc;
51
52 /**
53  * This class defines the implementation of the {@link XSLTProcessor}
54  * component.
55  *
56  * To configure it, add the following lines in the
57  * <file>cocoon.xconf</file> file:
58  *
59  * <pre>
60  * &lt;xslt-processor class="org.apache.cocoon.components.xslt.XSLTProcessorImpl"&gt;
61  * &lt;parameter name="use-store" value="true"/&gt;
62  * &lt;parameter name="transformer-factory" value="org.apache.xalan.processor.TransformerFactoryImpl"/&gt;
63  * &lt;/xslt-processor&gt;
64  * </pre>
65  *
66  * The &lt;use-store&gt; configuration forces the transformer to put the
67  * <code>Templates</code> generated from the XSLT stylesheet into the
68  * <code>Store</code>. This property is true by default.
69  * <p>
70  * The &lt;transformer-factory&gt; configuration tells the transformer to use a particular
71  * implementation of <code>javax.xml.transform.TransformerFactory</code>. This allows to force
72  * the use of a given TRAX implementation (e.g. xalan or saxon) if several are available in the
73  * classpath. If this property is not set, the transformer uses the standard TRAX mechanism
74  * (<code>TransformerFactory.newInstance()</code>).
75  *
76  * @deprecated Use the avalon excalibur xslt processor instead.
77  * @author <a HREF="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
78  * @author <a HREF="mailto:stefano@apache.org">Stefano Mazzocchi</a>
79  * @version CVS $Id: XSLTProcessorImpl.java 30932 2004-07-29 17:35:38Z vgritsenko $
80  * @version 1.0
81  * @since July 11, 2001
82  */

83 public class XSLTProcessorImpl
84   extends AbstractLogEnabled
85   implements XSLTProcessor,
86              Composable,
87              Disposable,
88              Parameterizable,
89              URIResolver JavaDoc {
90
91     protected ComponentManager manager;
92
93     /** The store service instance */
94     protected Store store;
95
96     /** The trax TransformerFactory lookup table*/
97     protected HashMap JavaDoc factories;
98
99     /** The trax TransformerFactory this component uses */
100     protected SAXTransformerFactory JavaDoc factory;
101
102     /** Is the store turned on? (default is on) */
103     protected boolean useStore = true;
104
105     /** Is incremental processing turned on? (default for Xalan: no) */
106     protected boolean incrementalProcessing = false;
107
108     /** The source resolver used by this processor **/
109     protected SourceResolver resolver;
110
111     /** The error handler for the transformer */
112     protected TraxErrorHandler errorHandler;
113
114     /**
115      * Compose. Try to get the store
116      */

117     public void compose(ComponentManager manager)
118     throws ComponentException {
119         this.manager = manager;
120         if (this.getLogger().isDebugEnabled())
121             this.getLogger().debug("XSLTProcessorImpl component initialized.");
122         this.store = (Store) manager.lookup(Store.TRANSIENT_STORE);
123         this.errorHandler = new TraxErrorHandler( this.getLogger() );
124         this.resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
125     }
126
127     /**
128      * Dispose
129      */

130     public void dispose() {
131         if (this.manager != null) {
132             this.manager.release(this.store);
133             this.store = null;
134             this.manager.release(this.resolver);
135             this.resolver = null;
136         }
137         this.errorHandler = null;
138         this.manager = null;
139     }
140
141     /**
142      * Configure the component
143      */

144     public void parameterize(Parameters params)
145     throws ParameterException {
146         this.useStore = params.getParameterAsBoolean("use-store", true);
147         this.incrementalProcessing = params.getParameterAsBoolean("incremental-processing", false);
148         this.factory = getTransformerFactory(params.getParameter("transformer-factory", DEFAULT_FACTORY));
149     }
150
151     /**
152      * Set the source resolver used by this component
153      * @deprecated The processor can now simply lookup the source resolver.
154      */

155     public void setSourceResolver(org.apache.cocoon.environment.SourceResolver resolver) {
156         if (this.getLogger().isDebugEnabled()) {
157             this.getLogger().debug("XSLTProcessor: the setSourceResolver() method is deprecated.");
158         }
159     }
160
161     /**
162      * Set the transformer factory used by this component
163      */

164     public void setTransformerFactory(String JavaDoc classname) {
165         this.factory = getTransformerFactory(classname);
166     }
167
168     public TransformerHandler JavaDoc getTransformerHandler(org.apache.cocoon.environment.Source stylesheet)
169     throws ProcessingException {
170         return getTransformerHandler(stylesheet, null);
171     }
172
173     public TransformerHandler JavaDoc getTransformerHandler(org.apache.cocoon.environment.Source stylesheet,
174                                                     XMLFilter JavaDoc filter)
175     throws ProcessingException {
176         try {
177             final String JavaDoc id = stylesheet.getSystemId();
178             Templates JavaDoc templates = getTemplates(stylesheet, id);
179             if (templates == null) {
180                 if (this.getLogger().isDebugEnabled()) {
181                     getLogger().debug("Creating new Templates for " + id);
182                 }
183
184                 // Create a Templates ContentHandler to handle parsing of the
185
// stylesheet.
186
TemplatesHandler JavaDoc templatesHandler = this.factory.newTemplatesHandler();
187
188                 // Set the system ID for the template handler since some
189
// TrAX implementations (XSLTC) rely on this in order to obtain
190
// a meaningful identifier for the Templates instances.
191
templatesHandler.setSystemId(id);
192
193                 if (filter != null) {
194                     filter.setContentHandler(templatesHandler);
195                 }
196
197                 if (this.getLogger().isDebugEnabled()) {
198                     getLogger().debug("Source = " + stylesheet
199                     + ", templatesHandler = " + templatesHandler);
200                 }
201
202                 // Process the stylesheet.
203
stylesheet.toSAX(filter != null ?
204                             (ContentHandler JavaDoc)filter : (ContentHandler JavaDoc)templatesHandler);
205
206                 // Get the Templates object (generated during the parsing of
207
// the stylesheet) from the TemplatesHandler.
208
templates = templatesHandler.getTemplates();
209                 putTemplates (templates, stylesheet, id);
210             } else {
211                 if (this.getLogger().isDebugEnabled()) {
212                     getLogger().debug("Reusing Templates for " + id);
213                 }
214             }
215
216             TransformerHandler JavaDoc handler = this.factory.newTransformerHandler(templates);
217             handler.getTransformer().setErrorListener(this.errorHandler);
218             handler.getTransformer().setURIResolver(this);
219             return handler;
220         } catch (ProcessingException e) {
221             throw e;
222         } catch (SAXException JavaDoc e) {
223             if (e.getException() == null) {
224                 throw new ProcessingException("Exception in creating Transform Handler", e);
225             } else {
226                 if (getLogger().isDebugEnabled())
227                     getLogger().debug("Got SAXException. Rethrowing cause exception.", e);
228                 throw new ProcessingException("Exception in creating Transform Handler", e.getException());
229             }
230         } catch (Exception JavaDoc e) {
231             throw new ProcessingException("Exception in creating Transform Handler", e);
232         }
233     }
234
235     public void transform(org.apache.cocoon.environment.Source source,
236                           org.apache.cocoon.environment.Source stylesheet,
237                           Parameters params,
238                           Result JavaDoc result)
239     throws ProcessingException {
240         try {
241             if (this.getLogger().isDebugEnabled()) {
242                 getLogger().debug("XSLTProcessorImpl: transform source = " + source
243                     + ", stylesheet = " + stylesheet
244                     + ", parameters = " + params
245                     + ", result = " + result);
246             }
247             TransformerHandler JavaDoc handler = getTransformerHandler(stylesheet);
248
249             Transformer JavaDoc transformer = handler.getTransformer();
250             if (params != null) {
251                 transformer.clearParameters();
252                 String JavaDoc[] names = params.getNames();
253                 for (int i = names.length -1 ; i >= 0; i--) {
254                     transformer.setParameter(names[i], params.getParameter(names[i]));
255                 }
256             }
257
258             if (getLogger().isDebugEnabled())
259                 this.getLogger().debug("XSLTProcessorImpl: starting transform");
260             // Is it possible to use Source's toSAX method?
261
handler.setResult(result);
262             source.toSAX(handler);
263
264             if (getLogger().isDebugEnabled())
265                 this.getLogger().debug("XSLTProcessorImpl: transform done");
266         } catch (Exception JavaDoc e) {
267             throw new ProcessingException("Error in running Transformation", e);
268         }
269     }
270
271     /**
272      * Get the TransformerFactory associated with the given classname. If
273      * the class can't be found or the given class doesn't implement
274      * the required interface, the default factory is returned.
275      */

276     private SAXTransformerFactory JavaDoc getTransformerFactory(String JavaDoc factoryName) {
277         SAXTransformerFactory JavaDoc _factory;
278
279         if ((factoryName == null) || (factoryName == XSLTProcessor.DEFAULT_FACTORY)) {
280             _factory = (SAXTransformerFactory JavaDoc) TransformerFactory.newInstance();
281         } else {
282             try {
283                 _factory = (SAXTransformerFactory JavaDoc) ClassUtils.loadClass(factoryName).newInstance();
284             } catch (ClassNotFoundException JavaDoc cnfe) {
285                 if (getLogger().isErrorEnabled())
286                     getLogger().error("Cannot find the requested TrAX factory '" + factoryName
287                                       + "'. Using default TrAX Transformer Factory instead.");
288                 if (this.factory != null) return this.factory;
289                 _factory = (SAXTransformerFactory JavaDoc) TransformerFactory.newInstance();
290             } catch (ClassCastException JavaDoc cce) {
291                 if (getLogger().isErrorEnabled())
292                     getLogger().error("The indicated class '" + factoryName
293                                       + "' is not a TrAX Transformer Factory. Using default TrAX Transformer Factory instead.");
294                 if (this.factory != null) return this.factory;
295                 _factory = (SAXTransformerFactory JavaDoc) TransformerFactory.newInstance();
296             } catch (Exception JavaDoc e) {
297                 if (getLogger().isErrorEnabled())
298                     getLogger().error("Error found loading the requested TrAX Transformer Factory '"
299                                       + factoryName + "'. Using default TrAX Transformer Factory instead.");
300                 if (this.factory != null) return this.factory;
301                 _factory = (SAXTransformerFactory JavaDoc) TransformerFactory.newInstance();
302             }
303         }
304
305         _factory.setErrorListener(this.errorHandler);
306         _factory.setURIResolver(this);
307
308         // implementation-specific parameter passing should be
309
// made more extensible.
310
if (_factory.getClass().getName().equals("org.apache.xalan.processor.TransformerFactoryImpl")) {
311             _factory.setAttribute("http://xml.apache.org/xalan/features/incremental",
312                     new Boolean JavaDoc (incrementalProcessing));
313         }
314
315         return _factory;
316     }
317
318     private Templates JavaDoc getTemplates(org.apache.cocoon.environment.Source stylesheet,
319                                    String JavaDoc id)
320     throws IOException JavaDoc, ProcessingException {
321         if (!useStore) {
322             return null;
323         }
324
325         // we must augment the template ID with the factory classname since one
326
// transformer implementation cannot handle the instances of a
327
// template created by another one.
328
id += factory.getClass().getName();
329
330         Templates JavaDoc templates = null;
331         // only stylesheets with a last modification date are stored
332
if (stylesheet.getLastModified() != 0) {
333             // Stored is an array of the template and the caching time
334
if (store.containsKey(id)) {
335                 Object JavaDoc[] templateAndTime = (Object JavaDoc[])store.get(id);
336
337                 if(templateAndTime != null && templateAndTime[1] != null) {
338                     long storedTime = ((Long JavaDoc)templateAndTime[1]).longValue();
339
340                     if (storedTime < stylesheet.getLastModified()) {
341                         store.remove(id);
342                     } else {
343                         templates = (Templates JavaDoc)templateAndTime[0];
344                     }
345                 }
346             }
347         } else if (store.containsKey(id)) {
348             // remove an old template if it exists
349
store.remove(id);
350         }
351         return templates;
352     }
353
354     private void putTemplates (Templates JavaDoc templates, org.apache.cocoon.environment.Source stylesheet,
355                                String JavaDoc id)
356     throws IOException JavaDoc, ProcessingException {
357         if (!useStore) {
358             return;
359         }
360
361         // we must augment the template ID with the factory classname since one
362
// transformer implementation cannot handle the instances of a
363
// template created by another one.
364
id += factory.getClass().getName();
365
366         // only stylesheets with a last modification date are stored
367
if (stylesheet.getLastModified() != 0) {
368
369             // Stored is an array of the template and the current time
370
Object JavaDoc[] templateAndTime = new Object JavaDoc[2];
371             templateAndTime[0] = templates;
372             templateAndTime[1] = new Long JavaDoc(stylesheet.getLastModified());
373             store.store(id, templateAndTime);
374         }
375     }
376
377     /**
378      * Called by the processor when it encounters
379      * an xsl:include, xsl:import, or document() function.
380      *
381      * @param href An href attribute, which may be relative or absolute.
382      * @param base The base URI in effect when the href attribute
383      * was encountered.
384      *
385      * @return A Source object, or null if the href cannot be resolved,
386      * and the processor should try to resolve the URI itself.
387      *
388      * @throws TransformerException if an error occurs when trying to
389      * resolve the URI.
390      */

391     public javax.xml.transform.Source JavaDoc resolve(String JavaDoc href, String JavaDoc base)
392     throws TransformerException JavaDoc {
393         if (this.getLogger().isDebugEnabled()) {
394             this.getLogger().debug("resolve(href = " + href +
395                                    ", base = " + base + "); resolver = " + resolver);
396         }
397
398         Source JavaDoc xslSource = null;
399         try {
400             if (href.indexOf(":") > 1) {
401                 xslSource = resolver.resolveURI(href);
402             } else {
403                 // patch for a null pointer passed as base
404
if (base == null)
405                     throw new IllegalArgumentException JavaDoc("Null pointer passed as base");
406
407                 // is the base a file or a real url
408
if (!base.startsWith("file:")) {
409                     int lastPathElementPos = base.lastIndexOf('/');
410                     if (lastPathElementPos == -1) {
411                         // this should never occur as the base should
412
// always be protocol:/....
413
return null; // we can't resolve this
414
} else {
415                         xslSource = resolver.resolveURI(new StringBuffer JavaDoc(base.substring(0, lastPathElementPos))
416                         .append("/").append(href).toString());
417                     }
418                 } else {
419                     File JavaDoc parent = new File JavaDoc(base.substring(5));
420                     File JavaDoc parent2 = new File JavaDoc(parent.getParentFile(), href);
421                     xslSource = resolver.resolveURI(parent2.toURL().toExternalForm());
422                 }
423             }
424
425             if (this.getLogger().isDebugEnabled()) {
426                 getLogger().debug("xslSource = " + xslSource
427                 + ", system id = " + xslSource.getURI());
428             }
429
430             return new StreamSource JavaDoc(xslSource.getInputStream(), xslSource.getURI());
431
432         } catch (java.net.MalformedURLException JavaDoc mue) {
433             return null;
434         } catch (SourceException pe) {
435             throw new TransformerException JavaDoc(pe);
436         } catch (IOException JavaDoc ioe) {
437             return null;
438         } finally {
439             this.resolver.release( xslSource );
440         }
441     }
442 }
443
Popular Tags