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