1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: TransformerFactoryImpl.java 468640 2006-10-28 06:53:53Z minchau $
20  */
21 package org.apache.xalan.processor;
22 
23 import java.io.BufferedInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.Enumeration;
27 import java.util.Properties;
28 
29 import javax.xml.XMLConstants;
30 import javax.xml.transform.ErrorListener;
31 import javax.xml.transform.Source;
32 import javax.xml.transform.Templates;
33 import javax.xml.transform.Transformer;
34 import javax.xml.transform.TransformerConfigurationException;
35 import javax.xml.transform.TransformerException;
36 import javax.xml.transform.URIResolver;
37 import javax.xml.transform.dom.DOMResult;
38 import javax.xml.transform.dom.DOMSource;
39 import javax.xml.transform.sax.SAXResult;
40 import javax.xml.transform.sax.SAXSource;
41 import javax.xml.transform.sax.SAXTransformerFactory;
42 import javax.xml.transform.sax.TemplatesHandler;
43 import javax.xml.transform.sax.TransformerHandler;
44 import javax.xml.transform.stream.StreamResult;
45 import javax.xml.transform.stream.StreamSource;
46 
47 import org.apache.xalan.res.XSLMessages;
48 import org.apache.xalan.res.XSLTErrorResources;
49 import org.apache.xalan.transformer.TrAXFilter;
50 import org.apache.xalan.transformer.TransformerIdentityImpl;
51 import org.apache.xalan.transformer.TransformerImpl;
52 import org.apache.xalan.transformer.XalanProperties;
53 
54 import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
55 import org.apache.xml.utils.DefaultErrorHandler;
56 import org.apache.xml.utils.SystemIDResolver;
57 import org.apache.xml.utils.TreeWalker;
58 import org.apache.xml.utils.StylesheetPIHandler;
59 import org.apache.xml.utils.StopParseException;
60 
61 import org.w3c.dom.Node;
62 
63 import org.xml.sax.InputSource;
64 import org.xml.sax.XMLFilter;
65 import org.xml.sax.XMLReader;
66 import org.xml.sax.helpers.XMLReaderFactory;
67 
68 /**
69  * The TransformerFactoryImpl, which implements the TRaX TransformerFactory
70  * interface, processes XSLT stylesheets into a Templates object
71  * (a StylesheetRoot).
72  */
73 public class TransformerFactoryImpl extends SAXTransformerFactory
74 {
75   /**
76    * The path/filename of the property file: XSLTInfo.properties
77    * Maintenance note: see also
78    * <code>org.apache.xpath.functions.FuncSystemProperty.XSLT_PROPERTIES</code>
79    */
80   public static final String XSLT_PROPERTIES =
81     "org/apache/xalan/res/XSLTInfo.properties";
82 
83   /**
84    * <p>State of secure processing feature.</p>
85    */
86   private boolean m_isSecureProcessing = false;
87 
88   /**
89    * Constructor TransformerFactoryImpl
90    *
91    */
TransformerFactoryImpl()92   public TransformerFactoryImpl()
93   {
94   }
95 
96   /** Static string to be used for incremental feature */
97   public static final String FEATURE_INCREMENTAL =
98                              "http://xml.apache.org/xalan/features/incremental";
99 
100   /** Static string to be used for optimize feature */
101   public static final String FEATURE_OPTIMIZE =
102                              "http://xml.apache.org/xalan/features/optimize";
103 
104   /** Static string to be used for source_location feature */
105   public static final String FEATURE_SOURCE_LOCATION =
106                              XalanProperties.SOURCE_LOCATION;
107 
processFromNode(Node node)108   public javax.xml.transform.Templates processFromNode(Node node)
109           throws TransformerConfigurationException
110   {
111 
112     try
113     {
114       TemplatesHandler builder = newTemplatesHandler();
115       TreeWalker walker = new TreeWalker(builder,
116                                          new org.apache.xml.utils.DOM2Helper(),
117                                          builder.getSystemId());
118 
119       walker.traverse(node);
120 
121       return builder.getTemplates();
122     }
123     catch (org.xml.sax.SAXException se)
124     {
125       if (m_errorListener != null)
126       {
127         try
128         {
129           m_errorListener.fatalError(new TransformerException(se));
130         }
131         catch (TransformerConfigurationException ex)
132         {
133           throw ex;
134         }
135         catch (TransformerException ex)
136         {
137           throw new TransformerConfigurationException(ex);
138         }
139 
140         return null;
141       }
142       else
143       {
144 
145         // Should remove this later... but right now diagnostics from
146         // TransformerConfigurationException are not good.
147         // se.printStackTrace();
148         throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), se);
149         //"processFromNode failed", se);
150       }
151     }
152     catch (TransformerConfigurationException tce)
153     {
154       // Assume it's already been reported to the error listener.
155       throw tce;
156     }
157    /* catch (TransformerException tce)
158     {
159       // Assume it's already been reported to the error listener.
160       throw new TransformerConfigurationException(tce.getMessage(), tce);
161     }*/
162     catch (Exception e)
163     {
164       if (m_errorListener != null)
165       {
166         try
167         {
168           m_errorListener.fatalError(new TransformerException(e));
169         }
170         catch (TransformerConfigurationException ex)
171         {
172           throw ex;
173         }
174         catch (TransformerException ex)
175         {
176           throw new TransformerConfigurationException(ex);
177         }
178 
179         return null;
180       }
181       else
182       {
183         // Should remove this later... but right now diagnostics from
184         // TransformerConfigurationException are not good.
185         // se.printStackTrace();
186         throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), e); //"processFromNode failed",
187                                                     //e);
188       }
189     }
190   }
191 
192   /**
193    * The systemID that was specified in
194    * processFromNode(Node node, String systemID).
195    */
196   private String m_DOMsystemID = null;
197 
198   /**
199    * The systemID that was specified in
200    * processFromNode(Node node, String systemID).
201    *
202    * @return The systemID, or null.
203    */
getDOMsystemID()204   String getDOMsystemID()
205   {
206     return m_DOMsystemID;
207   }
208 
209   /**
210    * Process the stylesheet from a DOM tree, if the
211    * processor supports the "http://xml.org/trax/features/dom/input"
212    * feature.
213    *
214    * @param node A DOM tree which must contain
215    * valid transform instructions that this processor understands.
216    * @param systemID The systemID from where xsl:includes and xsl:imports
217    * should be resolved from.
218    *
219    * @return A Templates object capable of being used for transformation purposes.
220    *
221    * @throws TransformerConfigurationException
222    */
processFromNode(Node node, String systemID)223   javax.xml.transform.Templates processFromNode(Node node, String systemID)
224           throws TransformerConfigurationException
225   {
226 
227     m_DOMsystemID = systemID;
228 
229     return processFromNode(node);
230   }
231 
232   /**
233    * Get InputSource specification(s) that are associated with the
234    * given document specified in the source param,
235    * via the xml-stylesheet processing instruction
236    * (see http://www.w3.org/TR/xml-stylesheet/), and that matches
237    * the given criteria.  Note that it is possible to return several stylesheets
238    * that match the criteria, in which case they are applied as if they were
239    * a list of imports or cascades.
240    *
241    * <p>Note that DOM2 has it's own mechanism for discovering stylesheets.
242    * Therefore, there isn't a DOM version of this method.</p>
243    *
244    *
245    * @param source The XML source that is to be searched.
246    * @param media The media attribute to be matched.  May be null, in which
247    *              case the prefered templates will be used (i.e. alternate = no).
248    * @param title The value of the title attribute to match.  May be null.
249    * @param charset The value of the charset attribute to match.  May be null.
250    *
251    * @return A Source object capable of being used to create a Templates object.
252    *
253    * @throws TransformerConfigurationException
254    */
getAssociatedStylesheet( Source source, String media, String title, String charset)255   public Source getAssociatedStylesheet(
256           Source source, String media, String title, String charset)
257             throws TransformerConfigurationException
258   {
259 
260     String baseID;
261     InputSource isource = null;
262     Node node = null;
263     XMLReader reader = null;
264 
265     if (source instanceof DOMSource)
266     {
267       DOMSource dsource = (DOMSource) source;
268 
269       node = dsource.getNode();
270       baseID = dsource.getSystemId();
271     }
272     else
273     {
274       isource = SAXSource.sourceToInputSource(source);
275       baseID = isource.getSystemId();
276     }
277 
278     // What I try to do here is parse until the first startElement
279     // is found, then throw a special exception in order to terminate
280     // the parse.
281     StylesheetPIHandler handler = new StylesheetPIHandler(baseID, media,
282                                     title, charset);
283 
284     // Use URIResolver. Patch from Dmitri Ilyin
285     if (m_uriResolver != null)
286     {
287       handler.setURIResolver(m_uriResolver);
288     }
289 
290     try
291     {
292       if (null != node)
293       {
294         TreeWalker walker = new TreeWalker(handler, new org.apache.xml.utils.DOM2Helper(), baseID);
295 
296         walker.traverse(node);
297       }
298       else
299       {
300 
301         // Use JAXP1.1 ( if possible )
302         try
303         {
304           javax.xml.parsers.SAXParserFactory factory =
305             javax.xml.parsers.SAXParserFactory.newInstance();
306 
307           factory.setNamespaceAware(true);
308 
309           if (m_isSecureProcessing)
310           {
311             try
312             {
313               factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
314             }
315             catch (org.xml.sax.SAXException e) {}
316           }
317 
318           javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser();
319 
320           reader = jaxpParser.getXMLReader();
321         }
322         catch (javax.xml.parsers.ParserConfigurationException ex)
323         {
324           throw new org.xml.sax.SAXException(ex);
325         }
326         catch (javax.xml.parsers.FactoryConfigurationError ex1)
327         {
328           throw new org.xml.sax.SAXException(ex1.toString());
329         }
330         catch (NoSuchMethodError ex2){}
331         catch (AbstractMethodError ame){}
332 
333         if (null == reader)
334         {
335           reader = XMLReaderFactory.createXMLReader();
336         }
337 
338         if(m_isSecureProcessing)
339         {
340             reader.setFeature("http://xml.org/sax/features/external-general-entities",false);
341         }
342         // Need to set options!
343         reader.setContentHandler(handler);
344         reader.parse(isource);
345       }
346     }
347     catch (StopParseException spe)
348     {
349 
350       // OK, good.
351     }
352     catch (org.xml.sax.SAXException se)
353     {
354       throw new TransformerConfigurationException(
355         "getAssociatedStylesheets failed", se);
356     }
357     catch (IOException ioe)
358     {
359       throw new TransformerConfigurationException(
360         "getAssociatedStylesheets failed", ioe);
361     }
362 
363     return handler.getAssociatedStylesheet();
364   }
365 
366   /**
367    * Create a new Transformer object that performs a copy
368    * of the source to the result.
369    *
370    * @return A Transformer object that may be used to perform a transformation
371    * in a single thread, never null.
372    *
373    * @throws TransformerConfigurationException May throw this during
374    *            the parse when it is constructing the
375    *            Templates object and fails.
376    */
newTemplatesHandler()377   public TemplatesHandler newTemplatesHandler()
378           throws TransformerConfigurationException
379   {
380     return new StylesheetHandler(this);
381   }
382 
383   /**
384    * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s
385    * or <code>Template</code>s created by this factory.</p>
386    *
387    * <p>
388    * Feature names are fully qualified {@link java.net.URI}s.
389    * Implementations may define their own features.
390    * An {@link TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the
391    * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature.
392    * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state.
393    * </p>
394    *
395    * <p>See {@link javax.xml.transform.TransformerFactory} for full documentation of specific features.</p>
396    *
397    * @param name Feature name.
398    * @param value Is feature state <code>true</code> or <code>false</code>.
399    *
400    * @throws TransformerConfigurationException if this <code>TransformerFactory</code>
401    *   or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature.
402    * @throws NullPointerException If the <code>name</code> parameter is null.
403    */
setFeature(String name, boolean value)404   public void setFeature(String name, boolean value)
405 	  throws TransformerConfigurationException {
406 
407   	// feature name cannot be null
408   	if (name == null) {
409   	    throw new NullPointerException(
410                   XSLMessages.createMessage(
411                       XSLTErrorResources.ER_SET_FEATURE_NULL_NAME, null));
412   	}
413 
414   	// secure processing?
415   	if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
416   	    m_isSecureProcessing = value;
417   	}
418   	// This implementation does not support the setting of a feature other than
419   	// the secure processing feature.
420   	else
421     {
422       throw new TransformerConfigurationException(
423           XSLMessages.createMessage(
424             XSLTErrorResources.ER_UNSUPPORTED_FEATURE,
425             new Object[] {name}));
426     }
427   }
428 
429   /**
430    * Look up the value of a feature.
431    * <p>The feature name is any fully-qualified URI.  It is
432    * possible for an TransformerFactory to recognize a feature name but
433    * to be unable to return its value; this is especially true
434    * in the case of an adapter for a SAX1 Parser, which has
435    * no way of knowing whether the underlying parser is
436    * validating, for example.</p>
437    *
438    * @param name The feature name, which is a fully-qualified URI.
439    * @return The current state of the feature (true or false).
440    */
getFeature(String name)441   public boolean getFeature(String name) {
442 
443     // feature name cannot be null
444     if (name == null)
445     {
446     	throw new NullPointerException(
447             XSLMessages.createMessage(
448             XSLTErrorResources.ER_GET_FEATURE_NULL_NAME, null));
449     }
450 
451     // Try first with identity comparison, which
452     // will be faster.
453     if ((DOMResult.FEATURE == name) || (DOMSource.FEATURE == name)
454             || (SAXResult.FEATURE == name) || (SAXSource.FEATURE == name)
455             || (StreamResult.FEATURE == name)
456             || (StreamSource.FEATURE == name)
457             || (SAXTransformerFactory.FEATURE == name)
458             || (SAXTransformerFactory.FEATURE_XMLFILTER == name))
459       return true;
460     else if ((DOMResult.FEATURE.equals(name))
461              || (DOMSource.FEATURE.equals(name))
462              || (SAXResult.FEATURE.equals(name))
463              || (SAXSource.FEATURE.equals(name))
464              || (StreamResult.FEATURE.equals(name))
465              || (StreamSource.FEATURE.equals(name))
466              || (SAXTransformerFactory.FEATURE.equals(name))
467              || (SAXTransformerFactory.FEATURE_XMLFILTER.equals(name)))
468       return true;
469     // secure processing?
470     else if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING))
471       return m_isSecureProcessing;
472     else
473       // unknown feature
474       return false;
475   }
476 
477   /**
478    * Flag set by FEATURE_OPTIMIZE.
479    * This feature specifies whether to Optimize stylesheet processing. By
480    * default it is set to true.
481    */
482   private boolean m_optimize = true;
483 
484   /** Flag set by FEATURE_SOURCE_LOCATION.
485    * This feature specifies whether the transformation phase should
486    * keep track of line and column numbers for the input source
487    * document. Note that this works only when that
488    * information is available from the source -- in other words, if you
489    * pass in a DOM, there's little we can do for you.
490    *
491    * The default is false. Setting it true may significantly
492    * increase storage cost per node.
493    */
494   private boolean m_source_location = false;
495 
496   /**
497    * Flag set by FEATURE_INCREMENTAL.
498    * This feature specifies whether to produce output incrementally, rather than
499    * waiting to finish parsing the input before generating any output. By
500    * default this attribute is set to false.
501    */
502   private boolean m_incremental = false;
503 
504   /**
505    * Allows the user to set specific attributes on the underlying
506    * implementation.
507    *
508    * @param name The name of the attribute.
509    * @param value The value of the attribute; Boolean or String="true"|"false"
510    *
511    * @throws IllegalArgumentException thrown if the underlying
512    * implementation doesn't recognize the attribute.
513    */
setAttribute(String name, Object value)514   public void setAttribute(String name, Object value)
515           throws IllegalArgumentException
516   {
517     if (name.equals(FEATURE_INCREMENTAL))
518     {
519       if(value instanceof Boolean)
520       {
521         // Accept a Boolean object..
522         m_incremental = ((Boolean)value).booleanValue();
523       }
524       else if(value instanceof String)
525       {
526         // .. or a String object
527         m_incremental = (new Boolean((String)value)).booleanValue();
528       }
529       else
530       {
531         // Give a more meaningful error message
532         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
533       }
534 	}
535     else if (name.equals(FEATURE_OPTIMIZE))
536     {
537       if(value instanceof Boolean)
538       {
539         // Accept a Boolean object..
540         m_optimize = ((Boolean)value).booleanValue();
541       }
542       else if(value instanceof String)
543       {
544         // .. or a String object
545         m_optimize = (new Boolean((String)value)).booleanValue();
546       }
547       else
548       {
549         // Give a more meaningful error message
550         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
551       }
552     }
553 
554     // Custom Xalan feature: annotate DTM with SAX source locator fields.
555     // This gets used during SAX2DTM instantiation.
556     //
557     // %REVIEW% Should the name of this field really be in XalanProperties?
558     // %REVIEW% I hate that it's a global static, but didn't want to change APIs yet.
559     else if(name.equals(FEATURE_SOURCE_LOCATION))
560     {
561       if(value instanceof Boolean)
562       {
563         // Accept a Boolean object..
564         m_source_location = ((Boolean)value).booleanValue();
565       }
566       else if(value instanceof String)
567       {
568         // .. or a String object
569         m_source_location = (new Boolean((String)value)).booleanValue();
570       }
571       else
572       {
573         // Give a more meaningful error message
574         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
575       }
576     }
577 
578     else
579     {
580       throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_SUPPORTED, new Object[]{name})); //name + "not supported");
581     }
582   }
583 
584   /**
585    * Allows the user to retrieve specific attributes on the underlying
586    * implementation.
587    *
588    * @param name The name of the attribute.
589    * @return value The value of the attribute.
590    *
591    * @throws IllegalArgumentException thrown if the underlying
592    * implementation doesn't recognize the attribute.
593    */
getAttribute(String name)594   public Object getAttribute(String name) throws IllegalArgumentException
595   {
596     if (name.equals(FEATURE_INCREMENTAL))
597     {
598       return new Boolean(m_incremental);
599     }
600     else if (name.equals(FEATURE_OPTIMIZE))
601     {
602       return new Boolean(m_optimize);
603     }
604     else if (name.equals(FEATURE_SOURCE_LOCATION))
605     {
606       return new Boolean(m_source_location);
607     }
608     else
609       throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ATTRIB_VALUE_NOT_RECOGNIZED, new Object[]{name})); //name + " attribute not recognized");
610   }
611 
612   /**
613    * Create an XMLFilter that uses the given source as the
614    * transformation instructions.
615    *
616    * @param src The source of the transformation instructions.
617    *
618    * @return An XMLFilter object, or null if this feature is not supported.
619    *
620    * @throws TransformerConfigurationException
621    */
newXMLFilter(Source src)622   public XMLFilter newXMLFilter(Source src)
623           throws TransformerConfigurationException
624   {
625 
626     Templates templates = newTemplates(src);
627     if( templates==null ) return null;
628 
629     return newXMLFilter(templates);
630   }
631 
632   /**
633    * Create an XMLFilter that uses the given source as the
634    * transformation instructions.
635    *
636    * @param templates non-null reference to Templates object.
637    *
638    * @return An XMLFilter object, or null if this feature is not supported.
639    *
640    * @throws TransformerConfigurationException
641    */
newXMLFilter(Templates templates)642   public XMLFilter newXMLFilter(Templates templates)
643           throws TransformerConfigurationException
644   {
645     try
646     {
647       return new TrAXFilter(templates);
648     }
649     catch( TransformerConfigurationException ex )
650     {
651       if( m_errorListener != null)
652       {
653         try
654         {
655           m_errorListener.fatalError( ex );
656           return null;
657         }
658         catch( TransformerConfigurationException ex1 )
659         {
660           throw ex1;
661         }
662         catch( TransformerException ex1 )
663         {
664           throw new TransformerConfigurationException(ex1);
665         }
666       }
667       throw ex;
668     }
669   }
670 
671   /**
672    * Get a TransformerHandler object that can process SAX
673    * ContentHandler events into a Result, based on the transformation
674    * instructions specified by the argument.
675    *
676    * @param src The source of the transformation instructions.
677    *
678    * @return TransformerHandler ready to transform SAX events.
679    *
680    * @throws TransformerConfigurationException
681    */
newTransformerHandler(Source src)682   public TransformerHandler newTransformerHandler(Source src)
683           throws TransformerConfigurationException
684   {
685 
686     Templates templates = newTemplates(src);
687     if( templates==null ) return null;
688 
689     return newTransformerHandler(templates);
690   }
691 
692   /**
693    * Get a TransformerHandler object that can process SAX
694    * ContentHandler events into a Result, based on the Templates argument.
695    *
696    * @param templates The source of the transformation instructions.
697    *
698    * @return TransformerHandler ready to transform SAX events.
699    * @throws TransformerConfigurationException
700    */
newTransformerHandler(Templates templates)701   public TransformerHandler newTransformerHandler(Templates templates)
702           throws TransformerConfigurationException
703   {
704     try {
705       TransformerImpl transformer =
706         (TransformerImpl) templates.newTransformer();
707       transformer.setURIResolver(m_uriResolver);
708       TransformerHandler th =
709         (TransformerHandler) transformer.getInputContentHandler(true);
710 
711       return th;
712     }
713     catch( TransformerConfigurationException ex )
714     {
715       if( m_errorListener != null )
716       {
717         try
718         {
719           m_errorListener.fatalError( ex );
720           return null;
721         }
722         catch (TransformerConfigurationException ex1 )
723         {
724           throw ex1;
725         }
726         catch (TransformerException ex1 )
727         {
728           throw new TransformerConfigurationException(ex1);
729         }
730       }
731 
732       throw ex;
733     }
734 
735   }
736 
737 //  /** The identity transform string, for support of newTransformerHandler()
738 //   *  and newTransformer().  */
739 //  private static final String identityTransform =
740 //    "<xsl:stylesheet " + "xmlns:xsl='http://www.w3.org/1999/XSL/Transform' "
741 //    + "version='1.0'>" + "<xsl:template match='/|node()'>"
742 //    + "<xsl:copy-of select='.'/>" + "</xsl:template>" + "</xsl:stylesheet>";
743 //
744 //  /** The identity transform Templates, built from identityTransform,
745 //   *  for support of newTransformerHandler() and newTransformer().  */
746 //  private static Templates m_identityTemplate = null;
747 
748   /**
749    * Get a TransformerHandler object that can process SAX
750    * ContentHandler events into a Result.
751    *
752    * @return TransformerHandler ready to transform SAX events.
753    *
754    * @throws TransformerConfigurationException
755    */
newTransformerHandler()756   public TransformerHandler newTransformerHandler()
757           throws TransformerConfigurationException
758   {
759     return new TransformerIdentityImpl(m_isSecureProcessing);
760   }
761 
762   /**
763    * Process the source into a Transformer object.  Care must
764    * be given to know that this object can not be used concurrently
765    * in multiple threads.
766    *
767    * @param source An object that holds a URL, input stream, etc.
768    *
769    * @return A Transformer object capable of
770    * being used for transformation purposes in a single thread.
771    *
772    * @throws TransformerConfigurationException May throw this during the parse when it
773    *            is constructing the Templates object and fails.
774    */
newTransformer(Source source)775   public Transformer newTransformer(Source source)
776           throws TransformerConfigurationException
777   {
778     try
779     {
780       Templates tmpl=newTemplates( source );
781       /* this can happen if an ErrorListener is present and it doesn't
782          throw any exception in fatalError.
783          The spec says: "a Transformer must use this interface
784          instead of throwing an exception" - the newTemplates() does
785          that, and returns null.
786       */
787       if( tmpl==null ) return null;
788       Transformer transformer = tmpl.newTransformer();
789       transformer.setURIResolver(m_uriResolver);
790       return transformer;
791     }
792     catch( TransformerConfigurationException ex )
793     {
794       if( m_errorListener != null )
795       {
796         try
797         {
798           m_errorListener.fatalError( ex );
799           return null; // TODO: but the API promises to never return null...
800         }
801         catch( TransformerConfigurationException ex1 )
802         {
803           throw ex1;
804         }
805         catch( TransformerException ex1 )
806         {
807           throw new TransformerConfigurationException( ex1 );
808         }
809       }
810       throw ex;
811     }
812   }
813 
814   /**
815    * Create a new Transformer object that performs a copy
816    * of the source to the result.
817    *
818    * @return A Transformer object capable of
819    * being used for transformation purposes in a single thread.
820    *
821    * @throws TransformerConfigurationException May throw this during
822    *            the parse when it is constructing the
823    *            Templates object and it fails.
824    */
newTransformer()825   public Transformer newTransformer() throws TransformerConfigurationException
826   {
827       return new TransformerIdentityImpl(m_isSecureProcessing);
828   }
829 
830   /**
831    * Process the source into a Templates object, which is likely
832    * a compiled representation of the source. This Templates object
833    * may then be used concurrently across multiple threads.  Creating
834    * a Templates object allows the TransformerFactory to do detailed
835    * performance optimization of transformation instructions, without
836    * penalizing runtime transformation.
837    *
838    * @param source An object that holds a URL, input stream, etc.
839    * @return A Templates object capable of being used for transformation purposes.
840    *
841    * @throws TransformerConfigurationException May throw this during the parse when it
842    *            is constructing the Templates object and fails.
843    */
newTemplates(Source source)844   public Templates newTemplates(Source source)
845           throws TransformerConfigurationException
846   {
847 
848     String baseID = source.getSystemId();
849 
850     if (null != baseID) {
851        baseID = SystemIDResolver.getAbsoluteURI(baseID);
852     }
853 
854 
855     if (source instanceof DOMSource)
856     {
857       DOMSource dsource = (DOMSource) source;
858       Node node = dsource.getNode();
859 
860       if (null != node)
861         return processFromNode(node, baseID);
862       else
863       {
864         String messageStr = XSLMessages.createMessage(
865           XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null);
866 
867         throw new IllegalArgumentException(messageStr);
868       }
869     }
870 
871     TemplatesHandler builder = newTemplatesHandler();
872     builder.setSystemId(baseID);
873 
874     try
875     {
876       InputSource isource = SAXSource.sourceToInputSource(source);
877       isource.setSystemId(baseID);
878       XMLReader reader = null;
879 
880       if (source instanceof SAXSource)
881         reader = ((SAXSource) source).getXMLReader();
882 
883       if (null == reader)
884       {
885 
886         // Use JAXP1.1 ( if possible )
887         try
888         {
889           javax.xml.parsers.SAXParserFactory factory =
890             javax.xml.parsers.SAXParserFactory.newInstance();
891 
892           factory.setNamespaceAware(true);
893 
894           if (m_isSecureProcessing)
895           {
896             try
897             {
898               factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
899             }
900             catch (org.xml.sax.SAXException se) {}
901           }
902 
903           javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser();
904 
905           reader = jaxpParser.getXMLReader();
906         }
907         catch (javax.xml.parsers.ParserConfigurationException ex)
908         {
909           throw new org.xml.sax.SAXException(ex);
910         }
911         catch (javax.xml.parsers.FactoryConfigurationError ex1)
912         {
913           throw new org.xml.sax.SAXException(ex1.toString());
914         }
915         catch (NoSuchMethodError ex2){}
916         catch (AbstractMethodError ame){}
917       }
918 
919       if (null == reader)
920         reader = XMLReaderFactory.createXMLReader();
921 
922       // If you set the namespaces to true, we'll end up getting double
923       // xmlns attributes.  Needs to be fixed.  -sb
924       // reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
925       reader.setContentHandler(builder);
926       reader.parse(isource);
927     }
928     catch (org.xml.sax.SAXException se)
929     {
930       if (m_errorListener != null)
931       {
932         try
933         {
934           m_errorListener.fatalError(new TransformerException(se));
935         }
936         catch (TransformerConfigurationException ex1)
937         {
938           throw ex1;
939         }
940         catch (TransformerException ex1)
941         {
942           throw new TransformerConfigurationException(ex1);
943         }
944       }
945       else
946       {
947         throw new TransformerConfigurationException(se.getMessage(), se);
948       }
949     }
950     catch (Exception e)
951     {
952       if (m_errorListener != null)
953       {
954         try
955         {
956           m_errorListener.fatalError(new TransformerException(e));
957           return null;
958         }
959         catch (TransformerConfigurationException ex1)
960         {
961           throw ex1;
962         }
963         catch (TransformerException ex1)
964         {
965           throw new TransformerConfigurationException(ex1);
966         }
967       }
968       else
969       {
970         throw new TransformerConfigurationException(e.getMessage(), e);
971       }
972     }
973 
974     return builder.getTemplates();
975   }
976 
977   /**
978    * The object that implements the URIResolver interface,
979    * or null.
980    */
981   URIResolver m_uriResolver;
982 
983   /**
984    * Set an object that will be used to resolve URIs used in
985    * xsl:import, etc.  This will be used as the default for the
986    * transformation.
987    * @param resolver An object that implements the URIResolver interface,
988    * or null.
989    */
setURIResolver(URIResolver resolver)990   public void setURIResolver(URIResolver resolver)
991   {
992     m_uriResolver = resolver;
993   }
994 
995   /**
996    * Get the object that will be used to resolve URIs used in
997    * xsl:import, etc.  This will be used as the default for the
998    * transformation.
999    *
1000    * @return The URIResolver that was set with setURIResolver.
1001    */
getURIResolver()1002   public URIResolver getURIResolver()
1003   {
1004     return m_uriResolver;
1005   }
1006 
1007   /** The error listener.   */
1008   private ErrorListener m_errorListener = new org.apache.xml.utils.DefaultErrorHandler(false);
1009 
1010   /**
1011    * Get the error listener in effect for the TransformerFactory.
1012    *
1013    * @return A non-null reference to an error listener.
1014    */
getErrorListener()1015   public ErrorListener getErrorListener()
1016   {
1017     return m_errorListener;
1018   }
1019 
1020   /**
1021    * Set an error listener for the TransformerFactory.
1022    *
1023    * @param listener Must be a non-null reference to an ErrorListener.
1024    *
1025    * @throws IllegalArgumentException if the listener argument is null.
1026    */
setErrorListener(ErrorListener listener)1027   public void setErrorListener(ErrorListener listener)
1028           throws IllegalArgumentException
1029   {
1030 
1031     if (null == listener)
1032       throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ERRORLISTENER, null));
1033       // "ErrorListener");
1034 
1035     m_errorListener = listener;
1036   }
1037 
1038   /**
1039    * Return the state of the secure processing feature.
1040    *
1041    * @return state of the secure processing feature.
1042    */
isSecureProcessing()1043   public boolean isSecureProcessing()
1044   {
1045     return m_isSecureProcessing;
1046   }
1047 }
1048