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 */
21package org.apache.xalan.processor;
22
23import java.io.BufferedInputStream;
24import java.io.IOException;
25import java.io.InputStream;
26import java.util.Enumeration;
27import java.util.Properties;
28
29import javax.xml.XMLConstants;
30import javax.xml.transform.ErrorListener;
31import javax.xml.transform.Source;
32import javax.xml.transform.Templates;
33import javax.xml.transform.Transformer;
34import javax.xml.transform.TransformerConfigurationException;
35import javax.xml.transform.TransformerException;
36import javax.xml.transform.URIResolver;
37import javax.xml.transform.dom.DOMResult;
38import javax.xml.transform.dom.DOMSource;
39import javax.xml.transform.sax.SAXResult;
40import javax.xml.transform.sax.SAXSource;
41import javax.xml.transform.sax.SAXTransformerFactory;
42import javax.xml.transform.sax.TemplatesHandler;
43import javax.xml.transform.sax.TransformerHandler;
44import javax.xml.transform.stream.StreamResult;
45import javax.xml.transform.stream.StreamSource;
46
47import org.apache.xalan.res.XSLMessages;
48import org.apache.xalan.res.XSLTErrorResources;
49import org.apache.xalan.transformer.TrAXFilter;
50import org.apache.xalan.transformer.TransformerIdentityImpl;
51import org.apache.xalan.transformer.TransformerImpl;
52import org.apache.xalan.transformer.XalanProperties;
53
54import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
55import org.apache.xml.utils.DefaultErrorHandler;
56import org.apache.xml.utils.SystemIDResolver;
57import org.apache.xml.utils.TreeWalker;
58import org.apache.xml.utils.StylesheetPIHandler;
59import org.apache.xml.utils.StopParseException;
60
61import org.w3c.dom.Node;
62
63import org.xml.sax.InputSource;
64import org.xml.sax.XMLFilter;
65import org.xml.sax.XMLReader;
66import 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 */
73public 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   */
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
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
1043  public boolean isSecureProcessing()
1044  {
1045    return m_isSecureProcessing;
1046  }
1047}
1048