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        // Need to set options!
339        reader.setContentHandler(handler);
340        reader.parse(isource);
341      }
342    }
343    catch (StopParseException spe)
344    {
345
346      // OK, good.
347    }
348    catch (org.xml.sax.SAXException se)
349    {
350      throw new TransformerConfigurationException(
351        "getAssociatedStylesheets failed", se);
352    }
353    catch (IOException ioe)
354    {
355      throw new TransformerConfigurationException(
356        "getAssociatedStylesheets failed", ioe);
357    }
358
359    return handler.getAssociatedStylesheet();
360  }
361
362  /**
363   * Create a new Transformer object that performs a copy
364   * of the source to the result.
365   *
366   * @return A Transformer object that may be used to perform a transformation
367   * in a single thread, never null.
368   *
369   * @throws TransformerConfigurationException May throw this during
370   *            the parse when it is constructing the
371   *            Templates object and fails.
372   */
373  public TemplatesHandler newTemplatesHandler()
374          throws TransformerConfigurationException
375  {
376    return new StylesheetHandler(this);
377  }
378
379  /**
380   * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s
381   * or <code>Template</code>s created by this factory.</p>
382   *
383   * <p>
384   * Feature names are fully qualified {@link java.net.URI}s.
385   * Implementations may define their own features.
386   * An {@link TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the
387   * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature.
388   * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state.
389   * </p>
390   *
391   * <p>See {@link javax.xml.transform.TransformerFactory} for full documentation of specific features.</p>
392   *
393   * @param name Feature name.
394   * @param value Is feature state <code>true</code> or <code>false</code>.
395   *
396   * @throws TransformerConfigurationException if this <code>TransformerFactory</code>
397   *   or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature.
398   * @throws NullPointerException If the <code>name</code> parameter is null.
399   */
400  public void setFeature(String name, boolean value)
401	  throws TransformerConfigurationException {
402
403  	// feature name cannot be null
404  	if (name == null) {
405  	    throw new NullPointerException(
406                  XSLMessages.createMessage(
407                      XSLTErrorResources.ER_SET_FEATURE_NULL_NAME, null));
408  	}
409
410  	// secure processing?
411  	if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
412  	    m_isSecureProcessing = value;
413  	}
414  	// This implementation does not support the setting of a feature other than
415  	// the secure processing feature.
416  	else
417    {
418      throw new TransformerConfigurationException(
419          XSLMessages.createMessage(
420            XSLTErrorResources.ER_UNSUPPORTED_FEATURE,
421            new Object[] {name}));
422    }
423  }
424
425  /**
426   * Look up the value of a feature.
427   * <p>The feature name is any fully-qualified URI.  It is
428   * possible for an TransformerFactory to recognize a feature name but
429   * to be unable to return its value; this is especially true
430   * in the case of an adapter for a SAX1 Parser, which has
431   * no way of knowing whether the underlying parser is
432   * validating, for example.</p>
433   *
434   * @param name The feature name, which is a fully-qualified URI.
435   * @return The current state of the feature (true or false).
436   */
437  public boolean getFeature(String name) {
438
439    // feature name cannot be null
440    if (name == null)
441    {
442    	throw new NullPointerException(
443            XSLMessages.createMessage(
444            XSLTErrorResources.ER_GET_FEATURE_NULL_NAME, null));
445    }
446
447    // Try first with identity comparison, which
448    // will be faster.
449    if ((DOMResult.FEATURE == name) || (DOMSource.FEATURE == name)
450            || (SAXResult.FEATURE == name) || (SAXSource.FEATURE == name)
451            || (StreamResult.FEATURE == name)
452            || (StreamSource.FEATURE == name)
453            || (SAXTransformerFactory.FEATURE == name)
454            || (SAXTransformerFactory.FEATURE_XMLFILTER == name))
455      return true;
456    else if ((DOMResult.FEATURE.equals(name))
457             || (DOMSource.FEATURE.equals(name))
458             || (SAXResult.FEATURE.equals(name))
459             || (SAXSource.FEATURE.equals(name))
460             || (StreamResult.FEATURE.equals(name))
461             || (StreamSource.FEATURE.equals(name))
462             || (SAXTransformerFactory.FEATURE.equals(name))
463             || (SAXTransformerFactory.FEATURE_XMLFILTER.equals(name)))
464      return true;
465    // secure processing?
466    else if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING))
467      return m_isSecureProcessing;
468    else
469      // unknown feature
470      return false;
471  }
472
473  /**
474   * Flag set by FEATURE_OPTIMIZE.
475   * This feature specifies whether to Optimize stylesheet processing. By
476   * default it is set to true.
477   */
478  private boolean m_optimize = true;
479
480  /** Flag set by FEATURE_SOURCE_LOCATION.
481   * This feature specifies whether the transformation phase should
482   * keep track of line and column numbers for the input source
483   * document. Note that this works only when that
484   * information is available from the source -- in other words, if you
485   * pass in a DOM, there's little we can do for you.
486   *
487   * The default is false. Setting it true may significantly
488   * increase storage cost per node.
489   */
490  private boolean m_source_location = false;
491
492  /**
493   * Flag set by FEATURE_INCREMENTAL.
494   * This feature specifies whether to produce output incrementally, rather than
495   * waiting to finish parsing the input before generating any output. By
496   * default this attribute is set to false.
497   */
498  private boolean m_incremental = false;
499
500  /**
501   * Allows the user to set specific attributes on the underlying
502   * implementation.
503   *
504   * @param name The name of the attribute.
505   * @param value The value of the attribute; Boolean or String="true"|"false"
506   *
507   * @throws IllegalArgumentException thrown if the underlying
508   * implementation doesn't recognize the attribute.
509   */
510  public void setAttribute(String name, Object value)
511          throws IllegalArgumentException
512  {
513    if (name.equals(FEATURE_INCREMENTAL))
514    {
515      if(value instanceof Boolean)
516      {
517        // Accept a Boolean object..
518        m_incremental = ((Boolean)value).booleanValue();
519      }
520      else if(value instanceof String)
521      {
522        // .. or a String object
523        m_incremental = (new Boolean((String)value)).booleanValue();
524      }
525      else
526      {
527        // Give a more meaningful error message
528        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
529      }
530	}
531    else if (name.equals(FEATURE_OPTIMIZE))
532    {
533      if(value instanceof Boolean)
534      {
535        // Accept a Boolean object..
536        m_optimize = ((Boolean)value).booleanValue();
537      }
538      else if(value instanceof String)
539      {
540        // .. or a String object
541        m_optimize = (new Boolean((String)value)).booleanValue();
542      }
543      else
544      {
545        // Give a more meaningful error message
546        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
547      }
548    }
549
550    // Custom Xalan feature: annotate DTM with SAX source locator fields.
551    // This gets used during SAX2DTM instantiation.
552    //
553    // %REVIEW% Should the name of this field really be in XalanProperties?
554    // %REVIEW% I hate that it's a global static, but didn't want to change APIs yet.
555    else if(name.equals(FEATURE_SOURCE_LOCATION))
556    {
557      if(value instanceof Boolean)
558      {
559        // Accept a Boolean object..
560        m_source_location = ((Boolean)value).booleanValue();
561      }
562      else if(value instanceof String)
563      {
564        // .. or a String object
565        m_source_location = (new Boolean((String)value)).booleanValue();
566      }
567      else
568      {
569        // Give a more meaningful error message
570        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
571      }
572    }
573
574    else
575    {
576      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_SUPPORTED, new Object[]{name})); //name + "not supported");
577    }
578  }
579
580  /**
581   * Allows the user to retrieve specific attributes on the underlying
582   * implementation.
583   *
584   * @param name The name of the attribute.
585   * @return value The value of the attribute.
586   *
587   * @throws IllegalArgumentException thrown if the underlying
588   * implementation doesn't recognize the attribute.
589   */
590  public Object getAttribute(String name) throws IllegalArgumentException
591  {
592    if (name.equals(FEATURE_INCREMENTAL))
593    {
594      return new Boolean(m_incremental);
595    }
596    else if (name.equals(FEATURE_OPTIMIZE))
597    {
598      return new Boolean(m_optimize);
599    }
600    else if (name.equals(FEATURE_SOURCE_LOCATION))
601    {
602      return new Boolean(m_source_location);
603    }
604    else
605      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ATTRIB_VALUE_NOT_RECOGNIZED, new Object[]{name})); //name + " attribute not recognized");
606  }
607
608  /**
609   * Create an XMLFilter that uses the given source as the
610   * transformation instructions.
611   *
612   * @param src The source of the transformation instructions.
613   *
614   * @return An XMLFilter object, or null if this feature is not supported.
615   *
616   * @throws TransformerConfigurationException
617   */
618  public XMLFilter newXMLFilter(Source src)
619          throws TransformerConfigurationException
620  {
621
622    Templates templates = newTemplates(src);
623    if( templates==null ) return null;
624
625    return newXMLFilter(templates);
626  }
627
628  /**
629   * Create an XMLFilter that uses the given source as the
630   * transformation instructions.
631   *
632   * @param templates non-null reference to Templates object.
633   *
634   * @return An XMLFilter object, or null if this feature is not supported.
635   *
636   * @throws TransformerConfigurationException
637   */
638  public XMLFilter newXMLFilter(Templates templates)
639          throws TransformerConfigurationException
640  {
641    try
642    {
643      return new TrAXFilter(templates);
644    }
645    catch( TransformerConfigurationException ex )
646    {
647      if( m_errorListener != null)
648      {
649        try
650        {
651          m_errorListener.fatalError( ex );
652          return null;
653        }
654        catch( TransformerConfigurationException ex1 )
655        {
656          throw ex1;
657        }
658        catch( TransformerException ex1 )
659        {
660          throw new TransformerConfigurationException(ex1);
661        }
662      }
663      throw ex;
664    }
665  }
666
667  /**
668   * Get a TransformerHandler object that can process SAX
669   * ContentHandler events into a Result, based on the transformation
670   * instructions specified by the argument.
671   *
672   * @param src The source of the transformation instructions.
673   *
674   * @return TransformerHandler ready to transform SAX events.
675   *
676   * @throws TransformerConfigurationException
677   */
678  public TransformerHandler newTransformerHandler(Source src)
679          throws TransformerConfigurationException
680  {
681
682    Templates templates = newTemplates(src);
683    if( templates==null ) return null;
684
685    return newTransformerHandler(templates);
686  }
687
688  /**
689   * Get a TransformerHandler object that can process SAX
690   * ContentHandler events into a Result, based on the Templates argument.
691   *
692   * @param templates The source of the transformation instructions.
693   *
694   * @return TransformerHandler ready to transform SAX events.
695   * @throws TransformerConfigurationException
696   */
697  public TransformerHandler newTransformerHandler(Templates templates)
698          throws TransformerConfigurationException
699  {
700    try {
701      TransformerImpl transformer =
702        (TransformerImpl) templates.newTransformer();
703      transformer.setURIResolver(m_uriResolver);
704      TransformerHandler th =
705        (TransformerHandler) transformer.getInputContentHandler(true);
706
707      return th;
708    }
709    catch( TransformerConfigurationException ex )
710    {
711      if( m_errorListener != null )
712      {
713        try
714        {
715          m_errorListener.fatalError( ex );
716          return null;
717        }
718        catch (TransformerConfigurationException ex1 )
719        {
720          throw ex1;
721        }
722        catch (TransformerException ex1 )
723        {
724          throw new TransformerConfigurationException(ex1);
725        }
726      }
727
728      throw ex;
729    }
730
731  }
732
733//  /** The identity transform string, for support of newTransformerHandler()
734//   *  and newTransformer().  */
735//  private static final String identityTransform =
736//    "<xsl:stylesheet " + "xmlns:xsl='http://www.w3.org/1999/XSL/Transform' "
737//    + "version='1.0'>" + "<xsl:template match='/|node()'>"
738//    + "<xsl:copy-of select='.'/>" + "</xsl:template>" + "</xsl:stylesheet>";
739//
740//  /** The identity transform Templates, built from identityTransform,
741//   *  for support of newTransformerHandler() and newTransformer().  */
742//  private static Templates m_identityTemplate = null;
743
744  /**
745   * Get a TransformerHandler object that can process SAX
746   * ContentHandler events into a Result.
747   *
748   * @return TransformerHandler ready to transform SAX events.
749   *
750   * @throws TransformerConfigurationException
751   */
752  public TransformerHandler newTransformerHandler()
753          throws TransformerConfigurationException
754  {
755    return new TransformerIdentityImpl(m_isSecureProcessing);
756  }
757
758  /**
759   * Process the source into a Transformer object.  Care must
760   * be given to know that this object can not be used concurrently
761   * in multiple threads.
762   *
763   * @param source An object that holds a URL, input stream, etc.
764   *
765   * @return A Transformer object capable of
766   * being used for transformation purposes in a single thread.
767   *
768   * @throws TransformerConfigurationException May throw this during the parse when it
769   *            is constructing the Templates object and fails.
770   */
771  public Transformer newTransformer(Source source)
772          throws TransformerConfigurationException
773  {
774    try
775    {
776      Templates tmpl=newTemplates( source );
777      /* this can happen if an ErrorListener is present and it doesn't
778         throw any exception in fatalError.
779         The spec says: "a Transformer must use this interface
780         instead of throwing an exception" - the newTemplates() does
781         that, and returns null.
782      */
783      if( tmpl==null ) return null;
784      Transformer transformer = tmpl.newTransformer();
785      transformer.setURIResolver(m_uriResolver);
786      return transformer;
787    }
788    catch( TransformerConfigurationException ex )
789    {
790      if( m_errorListener != null )
791      {
792        try
793        {
794          m_errorListener.fatalError( ex );
795          return null; // TODO: but the API promises to never return null...
796        }
797        catch( TransformerConfigurationException ex1 )
798        {
799          throw ex1;
800        }
801        catch( TransformerException ex1 )
802        {
803          throw new TransformerConfigurationException( ex1 );
804        }
805      }
806      throw ex;
807    }
808  }
809
810  /**
811   * Create a new Transformer object that performs a copy
812   * of the source to the result.
813   *
814   * @return A Transformer object capable of
815   * being used for transformation purposes in a single thread.
816   *
817   * @throws TransformerConfigurationException May throw this during
818   *            the parse when it is constructing the
819   *            Templates object and it fails.
820   */
821  public Transformer newTransformer() throws TransformerConfigurationException
822  {
823      return new TransformerIdentityImpl(m_isSecureProcessing);
824  }
825
826  /**
827   * Process the source into a Templates object, which is likely
828   * a compiled representation of the source. This Templates object
829   * may then be used concurrently across multiple threads.  Creating
830   * a Templates object allows the TransformerFactory to do detailed
831   * performance optimization of transformation instructions, without
832   * penalizing runtime transformation.
833   *
834   * @param source An object that holds a URL, input stream, etc.
835   * @return A Templates object capable of being used for transformation purposes.
836   *
837   * @throws TransformerConfigurationException May throw this during the parse when it
838   *            is constructing the Templates object and fails.
839   */
840  public Templates newTemplates(Source source)
841          throws TransformerConfigurationException
842  {
843
844    String baseID = source.getSystemId();
845
846    if (null != baseID) {
847       baseID = SystemIDResolver.getAbsoluteURI(baseID);
848    }
849
850
851    if (source instanceof DOMSource)
852    {
853      DOMSource dsource = (DOMSource) source;
854      Node node = dsource.getNode();
855
856      if (null != node)
857        return processFromNode(node, baseID);
858      else
859      {
860        String messageStr = XSLMessages.createMessage(
861          XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null);
862
863        throw new IllegalArgumentException(messageStr);
864      }
865    }
866
867    TemplatesHandler builder = newTemplatesHandler();
868    builder.setSystemId(baseID);
869
870    try
871    {
872      InputSource isource = SAXSource.sourceToInputSource(source);
873      isource.setSystemId(baseID);
874      XMLReader reader = null;
875
876      if (source instanceof SAXSource)
877        reader = ((SAXSource) source).getXMLReader();
878
879      if (null == reader)
880      {
881
882        // Use JAXP1.1 ( if possible )
883        try
884        {
885          javax.xml.parsers.SAXParserFactory factory =
886            javax.xml.parsers.SAXParserFactory.newInstance();
887
888          factory.setNamespaceAware(true);
889
890          if (m_isSecureProcessing)
891          {
892            try
893            {
894              factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
895            }
896            catch (org.xml.sax.SAXException se) {}
897          }
898
899          javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser();
900
901          reader = jaxpParser.getXMLReader();
902        }
903        catch (javax.xml.parsers.ParserConfigurationException ex)
904        {
905          throw new org.xml.sax.SAXException(ex);
906        }
907        catch (javax.xml.parsers.FactoryConfigurationError ex1)
908        {
909          throw new org.xml.sax.SAXException(ex1.toString());
910        }
911        catch (NoSuchMethodError ex2){}
912        catch (AbstractMethodError ame){}
913      }
914
915      if (null == reader)
916        reader = XMLReaderFactory.createXMLReader();
917
918      // If you set the namespaces to true, we'll end up getting double
919      // xmlns attributes.  Needs to be fixed.  -sb
920      // reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
921      reader.setContentHandler(builder);
922      reader.parse(isource);
923    }
924    catch (org.xml.sax.SAXException se)
925    {
926      if (m_errorListener != null)
927      {
928        try
929        {
930          m_errorListener.fatalError(new TransformerException(se));
931        }
932        catch (TransformerConfigurationException ex1)
933        {
934          throw ex1;
935        }
936        catch (TransformerException ex1)
937        {
938          throw new TransformerConfigurationException(ex1);
939        }
940      }
941      else
942      {
943        throw new TransformerConfigurationException(se.getMessage(), se);
944      }
945    }
946    catch (Exception e)
947    {
948      if (m_errorListener != null)
949      {
950        try
951        {
952          m_errorListener.fatalError(new TransformerException(e));
953          return null;
954        }
955        catch (TransformerConfigurationException ex1)
956        {
957          throw ex1;
958        }
959        catch (TransformerException ex1)
960        {
961          throw new TransformerConfigurationException(ex1);
962        }
963      }
964      else
965      {
966        throw new TransformerConfigurationException(e.getMessage(), e);
967      }
968    }
969
970    return builder.getTemplates();
971  }
972
973  /**
974   * The object that implements the URIResolver interface,
975   * or null.
976   */
977  URIResolver m_uriResolver;
978
979  /**
980   * Set an object that will be used to resolve URIs used in
981   * xsl:import, etc.  This will be used as the default for the
982   * transformation.
983   * @param resolver An object that implements the URIResolver interface,
984   * or null.
985   */
986  public void setURIResolver(URIResolver resolver)
987  {
988    m_uriResolver = resolver;
989  }
990
991  /**
992   * Get the object that will be used to resolve URIs used in
993   * xsl:import, etc.  This will be used as the default for the
994   * transformation.
995   *
996   * @return The URIResolver that was set with setURIResolver.
997   */
998  public URIResolver getURIResolver()
999  {
1000    return m_uriResolver;
1001  }
1002
1003  /** The error listener.   */
1004  private ErrorListener m_errorListener = new org.apache.xml.utils.DefaultErrorHandler(false);
1005
1006  /**
1007   * Get the error listener in effect for the TransformerFactory.
1008   *
1009   * @return A non-null reference to an error listener.
1010   */
1011  public ErrorListener getErrorListener()
1012  {
1013    return m_errorListener;
1014  }
1015
1016  /**
1017   * Set an error listener for the TransformerFactory.
1018   *
1019   * @param listener Must be a non-null reference to an ErrorListener.
1020   *
1021   * @throws IllegalArgumentException if the listener argument is null.
1022   */
1023  public void setErrorListener(ErrorListener listener)
1024          throws IllegalArgumentException
1025  {
1026
1027    if (null == listener)
1028      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ERRORLISTENER, null));
1029      // "ErrorListener");
1030
1031    m_errorListener = listener;
1032  }
1033
1034  /**
1035   * Return the state of the secure processing feature.
1036   *
1037   * @return state of the secure processing feature.
1038   */
1039  public boolean isSecureProcessing()
1040  {
1041    return m_isSecureProcessing;
1042  }
1043}
1044