View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2001-2004 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: TransformerImpl.java,v 1.10 2007/06/13 01:57:09 joehw Exp $
22   */
23  
24  package com.sun.org.apache.xalan.internal.xsltc.trax;
25  
26  import com.sun.org.apache.xalan.internal.XalanConstants;
27  import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
28  import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
29  import java.io.File;
30  import java.io.FileOutputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.OutputStream;
34  import java.io.Reader;
35  import java.io.Writer;
36  import java.net.URI;
37  import java.net.URL;
38  import java.net.URLConnection;
39  import java.net.UnknownServiceException;
40  import java.util.Enumeration;
41  import java.util.Properties;
42  import java.util.StringTokenizer;
43  import java.util.Vector;
44  import java.lang.reflect.Constructor;
45  
46  import javax.xml.parsers.DocumentBuilder;
47  import javax.xml.parsers.DocumentBuilderFactory;
48  import javax.xml.parsers.ParserConfigurationException;
49  import javax.xml.stream.XMLEventReader;
50  import javax.xml.stream.XMLStreamReader;
51  import javax.xml.transform.ErrorListener;
52  import javax.xml.transform.OutputKeys;
53  import javax.xml.transform.Result;
54  import javax.xml.transform.Source;
55  import javax.xml.transform.Transformer;
56  import javax.xml.transform.TransformerException;
57  import javax.xml.transform.URIResolver;
58  import javax.xml.transform.dom.DOMResult;
59  import javax.xml.transform.dom.DOMSource;
60  import javax.xml.transform.sax.SAXResult;
61  import javax.xml.transform.sax.SAXSource;
62  import javax.xml.transform.stax.StAXResult;
63  import javax.xml.transform.stax.StAXSource;
64  import javax.xml.transform.stream.StreamResult;
65  import javax.xml.transform.stream.StreamSource;
66  import javax.xml.XMLConstants;
67  
68  import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
69  
70  import com.sun.org.apache.xalan.internal.xsltc.DOM;
71  import com.sun.org.apache.xalan.internal.xsltc.DOMCache;
72  import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM;
73  import com.sun.org.apache.xalan.internal.xsltc.StripFilter;
74  import com.sun.org.apache.xalan.internal.xsltc.Translet;
75  import com.sun.org.apache.xalan.internal.xsltc.TransletException;
76  import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory;
77  import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
78  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
79  import com.sun.org.apache.xalan.internal.xsltc.dom.DOMWSFilter;
80  import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl;
81  import com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager;
82  import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
83  import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
84  import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
85  
86  import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
87  import com.sun.org.apache.xml.internal.utils.XMLReaderManager;
88  
89  import org.xml.sax.ContentHandler;
90  import org.xml.sax.InputSource;
91  import org.xml.sax.SAXException;
92  import org.xml.sax.XMLReader;
93  import org.xml.sax.ext.LexicalHandler;
94  
95  /**
96   * @author Morten Jorgensen
97   * @author G. Todd Miller
98   * @author Santiago Pericas-Geertsen
99   */
100 public final class TransformerImpl extends Transformer
101     implements DOMCache, ErrorListener
102 {
103 
104     private final static String LEXICAL_HANDLER_PROPERTY =
105         "http://xml.org/sax/properties/lexical-handler";
106     private static final String NAMESPACE_FEATURE =
107         "http://xml.org/sax/features/namespaces";
108 
109     /**
110      * Namespace prefixes feature for {@link XMLReader}.
111      */
112     private static final String NAMESPACE_PREFIXES_FEATURE =
113         "http://xml.org/sax/features/namespace-prefixes";
114 
115     /**
116      * A reference to the translet or null if the identity transform.
117      */
118     private AbstractTranslet _translet = null;
119 
120     /**
121      * The output method of this transformation.
122      */
123     private String _method = null;
124 
125     /**
126      * The output encoding of this transformation.
127      */
128     private String _encoding = null;
129 
130     /**
131      * The systemId set in input source.
132      */
133     private String _sourceSystemId = null;
134 
135     /**
136      * An error listener for runtime errors.
137      */
138     private ErrorListener _errorListener = this;
139 
140     /**
141      * A reference to a URI resolver for calls to document().
142      */
143     private URIResolver _uriResolver = null;
144 
145     /**
146      * Output properties of this transformer instance.
147      */
148     private Properties _properties, _propertiesClone;
149 
150     /**
151      * A reference to an output handler factory.
152      */
153     private TransletOutputHandlerFactory _tohFactory = null;
154 
155     /**
156      * A reference to a internal DOM representation of the input.
157      */
158     private DOM _dom = null;
159 
160     /**
161      * Number of indent spaces to add when indentation is on.
162      */
163     private int _indentNumber;
164 
165     /**
166      * A reference to the transformer factory that this templates
167      * object belongs to.
168      */
169     private TransformerFactoryImpl _tfactory = null;
170 
171     /**
172      * A reference to the output stream, if we create one in our code.
173      */
174     private OutputStream _ostream = null;
175 
176     /**
177      * A reference to the XSLTCDTMManager which is used to build the DOM/DTM
178      * for this transformer.
179      */
180     private XSLTCDTMManager _dtmManager = null;
181 
182     /**
183      * A reference to an object that creates and caches XMLReader objects.
184      */
185     private XMLReaderManager _readerManager;
186 
187     /**
188      * A flag indicating whether we use incremental building of the DTM.
189      */
190     //private boolean _isIncremental = false;
191 
192     /**
193      * A flag indicating whether this transformer implements the identity
194      * transform.
195      */
196     private boolean _isIdentity = false;
197 
198     /**
199      * State of the secure processing feature.
200      */
201     private boolean _isSecureProcessing = false;
202 
203     /**
204      * Indicates whether implementation parts should use
205      *   service loader (or similar).
206      * Note the default value (false) is the safe option..
207      */
208     private boolean _useServicesMechanism;
209     /**
210      * protocols allowed for external references set by the stylesheet processing instruction, Import and Include element.
211      */
212     private String _accessExternalStylesheet = XalanConstants.EXTERNAL_ACCESS_DEFAULT;
213      /**
214      * protocols allowed for external DTD references in source file and/or stylesheet.
215      */
216     private String _accessExternalDTD = XalanConstants.EXTERNAL_ACCESS_DEFAULT;
217 
218     private XMLSecurityManager _securityManager;
219     /**
220      * A hashtable to store parameters for the identity transform. These
221      * are not needed during the transformation, but we must keep track of
222      * them to be fully complaint with the JAXP API.
223      */
224     private Hashtable _parameters = null;
225 
226     /**
227      * This class wraps an ErrorListener into a MessageHandler in order to
228      * capture messages reported via xsl:message.
229      */
230     static class MessageHandler
231            extends com.sun.org.apache.xalan.internal.xsltc.runtime.MessageHandler
232     {
233         private ErrorListener _errorListener;
234 
235         public MessageHandler(ErrorListener errorListener) {
236             _errorListener = errorListener;
237         }
238 
239         @Override
240         public void displayMessage(String msg) {
241             if(_errorListener == null) {
242                 System.err.println(msg);
243             }
244             else {
245                 try {
246                     _errorListener.warning(new TransformerException(msg));
247                 }
248                 catch (TransformerException e) {
249                     // ignored
250                 }
251             }
252         }
253     }
254 
255     protected TransformerImpl(Properties outputProperties, int indentNumber,
256         TransformerFactoryImpl tfactory)
257     {
258         this(null, outputProperties, indentNumber, tfactory);
259         _isIdentity = true;
260         // _properties.put(OutputKeys.METHOD, "xml");
261     }
262 
263     protected TransformerImpl(Translet translet, Properties outputProperties,
264         int indentNumber, TransformerFactoryImpl tfactory)
265     {
266         _translet = (AbstractTranslet) translet;
267         _properties = createOutputProperties(outputProperties);
268         _propertiesClone = (Properties) _properties.clone();
269         _indentNumber = indentNumber;
270         _tfactory = tfactory;
271         _useServicesMechanism = _tfactory.useServicesMechnism();
272         _accessExternalStylesheet = (String)_tfactory.getAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET);
273         _accessExternalDTD = (String)_tfactory.getAttribute(XMLConstants.ACCESS_EXTERNAL_DTD);
274         _securityManager = (XMLSecurityManager)_tfactory.getAttribute(XalanConstants.SECURITY_MANAGER);
275         _readerManager = XMLReaderManager.getInstance(_useServicesMechanism);
276         _readerManager.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, _accessExternalDTD);
277         _readerManager.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, _isSecureProcessing);
278         _readerManager.setProperty(XalanConstants.SECURITY_MANAGER, _securityManager);
279         //_isIncremental = tfactory._incremental;
280     }
281 
282     /**
283      * Return the state of the secure processing feature.
284      */
285     public boolean isSecureProcessing() {
286         return _isSecureProcessing;
287     }
288 
289     /**
290      * Set the state of the secure processing feature.
291      */
292     public void setSecureProcessing(boolean flag) {
293         _isSecureProcessing = flag;
294         _readerManager.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, _isSecureProcessing);
295     }
296     /**
297      * Return the state of the services mechanism feature.
298      */
299     public boolean useServicesMechnism() {
300         return _useServicesMechanism;
301     }
302 
303     /**
304      * Set the state of the services mechanism feature.
305      */
306     public void setServicesMechnism(boolean flag) {
307         _useServicesMechanism = flag;
308     }
309 
310     /**
311      * Returns the translet wrapped inside this Transformer or
312      * null if this is the identity transform.
313      */
314     protected AbstractTranslet getTranslet() {
315         return _translet;
316     }
317 
318     public boolean isIdentity() {
319         return _isIdentity;
320     }
321 
322     /**
323      * Implements JAXP's Transformer.transform()
324      *
325      * @param source Contains the input XML document
326      * @param result Will contain the output from the transformation
327      * @throws TransformerException
328      */
329     @Override
330     public void transform(Source source, Result result)
331         throws TransformerException
332     {
333         if (!_isIdentity) {
334             if (_translet == null) {
335                 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_TRANSLET_ERR);
336                 throw new TransformerException(err.toString());
337             }
338             // Pass output properties to the translet
339             transferOutputProperties(_translet);
340         }
341 
342         final SerializationHandler toHandler = getOutputHandler(result);
343         if (toHandler == null) {
344             ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_HANDLER_ERR);
345             throw new TransformerException(err.toString());
346         }
347 
348         if (_uriResolver != null && !_isIdentity) {
349             _translet.setDOMCache(this);
350         }
351 
352         // Pass output properties to handler if identity
353         if (_isIdentity) {
354             transferOutputProperties(toHandler);
355         }
356 
357         transform(source, toHandler, _encoding);
358         try{
359             if (result instanceof DOMResult) {
360                 ((DOMResult)result).setNode(_tohFactory.getNode());
361             } else if (result instanceof StAXResult) {
362                   if (((StAXResult) result).getXMLEventWriter() != null)
363                 {
364                     (_tohFactory.getXMLEventWriter()).flush();
365                 }
366                 else if (((StAXResult) result).getXMLStreamWriter() != null) {
367                     (_tohFactory.getXMLStreamWriter()).flush();
368                     //result = new StAXResult(_tohFactory.getXMLStreamWriter());
369                 }
370             }
371         } catch (Exception e) {
372             System.out.println("Result writing error");
373         }
374     }
375 
376     /**
377      * Create an output handler for the transformation output based on
378      * the type and contents of the TrAX Result object passed to the
379      * transform() method.
380      */
381     public SerializationHandler getOutputHandler(Result result)
382         throws TransformerException
383     {
384         // Get output method using get() to ignore defaults
385         _method = (String) _properties.get(OutputKeys.METHOD);
386 
387         // Get encoding using getProperty() to use defaults
388         _encoding = (String) _properties.getProperty(OutputKeys.ENCODING);
389 
390         _tohFactory = TransletOutputHandlerFactory.newInstance(_useServicesMechanism);
391         _tohFactory.setEncoding(_encoding);
392         if (_method != null) {
393             _tohFactory.setOutputMethod(_method);
394         }
395 
396         // Set indentation number in the factory
397         if (_indentNumber >= 0) {
398             _tohFactory.setIndentNumber(_indentNumber);
399         }
400 
401         // Return the content handler for this Result object
402         try {
403             // Result object could be SAXResult, DOMResult, or StreamResult
404             if (result instanceof SAXResult) {
405                 final SAXResult target = (SAXResult)result;
406                 final ContentHandler handler = target.getHandler();
407 
408                 _tohFactory.setHandler(handler);
409 
410                 /**
411                  * Fix for bug 24414
412                  * If the lexicalHandler is set then we need to get that
413                  * for obtaining the lexical information
414                  */
415                 LexicalHandler lexicalHandler = target.getLexicalHandler();
416 
417                 if (lexicalHandler != null ) {
418                     _tohFactory.setLexicalHandler(lexicalHandler);
419                 }
420 
421                 _tohFactory.setOutputType(TransletOutputHandlerFactory.SAX);
422                 return _tohFactory.getSerializationHandler();
423             }
424             else if (result instanceof StAXResult) {
425                 if (((StAXResult) result).getXMLEventWriter() != null)
426                     _tohFactory.setXMLEventWriter(((StAXResult) result).getXMLEventWriter());
427                 else if (((StAXResult) result).getXMLStreamWriter() != null)
428                     _tohFactory.setXMLStreamWriter(((StAXResult) result).getXMLStreamWriter());
429                 _tohFactory.setOutputType(TransletOutputHandlerFactory.STAX);
430                 return _tohFactory.getSerializationHandler();
431             }
432             else if (result instanceof DOMResult) {
433                 _tohFactory.setNode(((DOMResult) result).getNode());
434                 _tohFactory.setNextSibling(((DOMResult) result).getNextSibling());
435                 _tohFactory.setOutputType(TransletOutputHandlerFactory.DOM);
436                 return _tohFactory.getSerializationHandler();
437             }
438             else if (result instanceof StreamResult) {
439                 // Get StreamResult
440                 final StreamResult target = (StreamResult) result;
441 
442                 // StreamResult may have been created with a java.io.File,
443                 // java.io.Writer, java.io.OutputStream or just a String
444                 // systemId.
445 
446                 _tohFactory.setOutputType(TransletOutputHandlerFactory.STREAM);
447 
448                 // try to get a Writer from Result object
449                 final Writer writer = target.getWriter();
450                 if (writer != null) {
451                     _tohFactory.setWriter(writer);
452                     return _tohFactory.getSerializationHandler();
453                 }
454 
455                 // or try to get an OutputStream from Result object
456                 final OutputStream ostream = target.getOutputStream();
457                 if (ostream != null) {
458                     _tohFactory.setOutputStream(ostream);
459                     return _tohFactory.getSerializationHandler();
460                 }
461 
462                 // or try to get just a systemId string from Result object
463                 String systemId = result.getSystemId();
464                 if (systemId == null) {
465                     ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_RESULT_ERR);
466                     throw new TransformerException(err.toString());
467                 }
468 
469                 // System Id may be in one of several forms, (1) a uri
470                 // that starts with 'file:', (2) uri that starts with 'http:'
471                 // or (3) just a filename on the local system.
472                 URL url;
473                 if (systemId.startsWith("file:")) {
474                     // if StreamResult(File) or setSystemID(File) was used,
475                     // the systemId will be URI encoded as a result of File.toURI(),
476                     // it must be decoded for use by URL
477                     try{
478                         URI uri = new URI(systemId) ;
479                         systemId = "file:";
480 
481                         String host = uri.getHost(); // decoded String
482                         String path = uri.getPath(); //decoded String
483                         if (path == null) {
484                          path = "";
485                         }
486 
487                         // if host (URI authority) then file:// + host + path
488                         // else just path (may be absolute or relative)
489                         if (host != null) {
490                          systemId += "//" + host + path;
491                         } else {
492                          systemId += "//" + path;
493                         }
494                     }
495                     catch (Exception  exception) {
496                         // URI exception which means nothing can be done so OK to ignore
497                     }
498 
499                     url = new URL(systemId);
500                     _ostream = new FileOutputStream(url.getFile());
501                     _tohFactory.setOutputStream(_ostream);
502                     return _tohFactory.getSerializationHandler();
503                 }
504                 else if (systemId.startsWith("http:")) {
505                     url = new URL(systemId);
506                     final URLConnection connection = url.openConnection();
507                     _tohFactory.setOutputStream(_ostream = connection.getOutputStream());
508                     return _tohFactory.getSerializationHandler();
509                 }
510                 else {
511                     // system id is just a filename
512                     _tohFactory.setOutputStream(
513                         _ostream = new FileOutputStream(new File(systemId)));
514                     return _tohFactory.getSerializationHandler();
515                 }
516             }
517         }
518         // If we cannot write to the location specified by the SystemId
519         catch (UnknownServiceException e) {
520             throw new TransformerException(e);
521         }
522         catch (ParserConfigurationException e) {
523             throw new TransformerException(e);
524         }
525         // If we cannot create the file specified by the SystemId
526         catch (IOException e) {
527             throw new TransformerException(e);
528         }
529         return null;
530     }
531 
532     /**
533      * Set the internal DOM that will be used for the next transformation
534      */
535     protected void setDOM(DOM dom) {
536         _dom = dom;
537     }
538 
539     /**
540      * Builds an internal DOM from a TrAX Source object
541      */
542     private DOM getDOM(Source source) throws TransformerException {
543         try {
544             DOM dom;
545 
546             if (source != null) {
547                 DTMWSFilter wsfilter;
548                 if (_translet != null && _translet instanceof StripFilter) {
549                     wsfilter = new DOMWSFilter(_translet);
550                  } else {
551                     wsfilter = null;
552                  }
553 
554                  boolean hasIdCall = (_translet != null) ? _translet.hasIdCall()
555                                                          : false;
556 
557                  if (_dtmManager == null) {
558                      _dtmManager =
559                          _tfactory.createNewDTMManagerInstance();
560                      _dtmManager.setServicesMechnism(_useServicesMechanism);
561                  }
562                  dom = (DOM)_dtmManager.getDTM(source, false, wsfilter, true,
563                                               false, false, 0, hasIdCall);
564             } else if (_dom != null) {
565                  dom = _dom;
566                  _dom = null;  // use only once, so reset to 'null'
567             } else {
568                  return null;
569             }
570 
571             if (!_isIdentity) {
572                 // Give the translet the opportunity to make a prepass of
573                 // the document, in case it can extract useful information early
574                 _translet.prepassDocument(dom);
575             }
576 
577             return dom;
578 
579         }
580         catch (Exception e) {
581             if (_errorListener != null) {
582                 postErrorToListener(e.getMessage());
583             }
584             throw new TransformerException(e);
585         }
586     }
587 
588     /**
589      * Returns the {@link com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl}
590      * object that create this <code>Transformer</code>.
591      */
592     protected TransformerFactoryImpl getTransformerFactory() {
593         return _tfactory;
594     }
595 
596     /**
597      * Returns the {@link com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory}
598      * object that create the <code>TransletOutputHandler</code>.
599      */
600     protected TransletOutputHandlerFactory getTransletOutputHandlerFactory() {
601         return _tohFactory;
602     }
603 
604     private void transformIdentity(Source source, SerializationHandler handler)
605         throws Exception
606     {
607         // Get systemId from source
608         if (source != null) {
609             _sourceSystemId = source.getSystemId();
610         }
611 
612         if (source instanceof StreamSource) {
613             final StreamSource stream = (StreamSource) source;
614             final InputStream streamInput = stream.getInputStream();
615             final Reader streamReader = stream.getReader();
616             final XMLReader reader = _readerManager.getXMLReader();
617 
618             try {
619                 // Hook up reader and output handler
620                 try {
621                     reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
622                     reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
623                 } catch (SAXException e) {
624                     // Falls through
625                 }
626                 reader.setContentHandler(handler);
627 
628                 // Create input source from source
629                 InputSource input;
630                 if (streamInput != null) {
631                     input = new InputSource(streamInput);
632                     input.setSystemId(_sourceSystemId);
633                 }
634                 else if (streamReader != null) {
635                     input = new InputSource(streamReader);
636                     input.setSystemId(_sourceSystemId);
637                 }
638                 else if (_sourceSystemId != null) {
639                     input = new InputSource(_sourceSystemId);
640                 }
641                 else {
642                     ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_SOURCE_ERR);
643                     throw new TransformerException(err.toString());
644                 }
645 
646                 // Start pushing SAX events
647                 reader.parse(input);
648             } finally {
649                 _readerManager.releaseXMLReader(reader);
650             }
651         } else if (source instanceof SAXSource) {
652             final SAXSource sax = (SAXSource) source;
653             XMLReader reader = sax.getXMLReader();
654             final InputSource input = sax.getInputSource();
655             boolean userReader = true;
656 
657             try {
658                 // Create a reader if not set by user
659                 if (reader == null) {
660                     reader = _readerManager.getXMLReader();
661                     userReader = false;
662                 }
663 
664                 // Hook up reader and output handler
665                 try {
666                     reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
667                     reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
668                 } catch (SAXException e) {
669                     // Falls through
670                 }
671                 reader.setContentHandler(handler);
672 
673                 // Start pushing SAX events
674                 reader.parse(input);
675             } finally {
676                 if (!userReader) {
677                     _readerManager.releaseXMLReader(reader);
678                 }
679             }
680         } else if (source instanceof StAXSource) {
681             final StAXSource staxSource = (StAXSource)source;
682             StAXEvent2SAX staxevent2sax;
683             StAXStream2SAX staxStream2SAX;
684             if (staxSource.getXMLEventReader() != null) {
685                 final XMLEventReader xmlEventReader = staxSource.getXMLEventReader();
686                 staxevent2sax = new StAXEvent2SAX(xmlEventReader);
687                 staxevent2sax.setContentHandler(handler);
688                 staxevent2sax.parse();
689                 handler.flushPending();
690             } else if (staxSource.getXMLStreamReader() != null) {
691                 final XMLStreamReader xmlStreamReader = staxSource.getXMLStreamReader();
692                 staxStream2SAX = new StAXStream2SAX(xmlStreamReader);
693                 staxStream2SAX.setContentHandler(handler);
694                 staxStream2SAX.parse();
695                 handler.flushPending();
696             }
697         } else if (source instanceof DOMSource) {
698             final DOMSource domsrc = (DOMSource) source;
699             new DOM2TO(domsrc.getNode(), handler).parse();
700         } else if (source instanceof XSLTCSource) {
701             final DOM dom = ((XSLTCSource) source).getDOM(null, _translet);
702             ((SAXImpl)dom).copy(handler);
703         } else {
704             ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_SOURCE_ERR);
705             throw new TransformerException(err.toString());
706         }
707     }
708 
709     /**
710      * Internal transformation method - uses the internal APIs of XSLTC
711      */
712     private void transform(Source source, SerializationHandler handler,
713         String encoding) throws TransformerException
714     {
715         try {
716             /*
717              * According to JAXP1.2, new SAXSource()/StreamSource()
718              * should create an empty input tree, with a default root node.
719              * new DOMSource()creates an empty document using DocumentBuilder.
720              * newDocument(); Use DocumentBuilder.newDocument() for all 3
721              * situations, since there is no clear spec. how to create
722              * an empty tree when both SAXSource() and StreamSource() are used.
723              */
724             if ((source instanceof StreamSource && source.getSystemId()==null
725                 && ((StreamSource)source).getInputStream()==null &&
726                 ((StreamSource)source).getReader()==null)||
727                 (source instanceof SAXSource &&
728                 ((SAXSource)source).getInputSource()==null &&
729                 ((SAXSource)source).getXMLReader()==null )||
730                 (source instanceof DOMSource &&
731                 ((DOMSource)source).getNode()==null)){
732                         DocumentBuilderFactory builderF = FactoryImpl.getDOMFactory(_useServicesMechanism);
733                         DocumentBuilder builder = builderF.newDocumentBuilder();
734                         String systemID = source.getSystemId();
735                         source = new DOMSource(builder.newDocument());
736 
737                         // Copy system ID from original, empty Source to new
738                         if (systemID != null) {
739                           source.setSystemId(systemID);
740                         }
741             }
742             if (_isIdentity) {
743                 transformIdentity(source, handler);
744             } else {
745                 _translet.transform(getDOM(source), handler);
746             }
747         } catch (TransletException e) {
748             if (_errorListener != null) postErrorToListener(e.getMessage());
749             throw new TransformerException(e);
750         } catch (RuntimeException e) {
751             if (_errorListener != null) postErrorToListener(e.getMessage());
752             throw new TransformerException(e);
753         } catch (Exception e) {
754             if (_errorListener != null) postErrorToListener(e.getMessage());
755             throw new TransformerException(e);
756         } finally {
757             _dtmManager = null;
758         }
759 
760         // If we create an output stream for the Result, we need to close it after the transformation.
761         if (_ostream != null) {
762             try {
763                 _ostream.close();
764             }
765             catch (IOException e) {}
766             _ostream = null;
767         }
768     }
769 
770     /**
771      * Implements JAXP's Transformer.getErrorListener()
772      * Get the error event handler in effect for the transformation.
773      *
774      * @return The error event handler currently in effect
775      */
776     @Override
777     public ErrorListener getErrorListener() {
778         return _errorListener;
779     }
780 
781     /**
782      * Implements JAXP's Transformer.setErrorListener()
783      * Set the error event listener in effect for the transformation.
784      * Register a message handler in the translet in order to forward
785      * xsl:messages to error listener.
786      *
787      * @param listener The error event listener to use
788      * @throws IllegalArgumentException
789      */
790     @Override
791     public void setErrorListener(ErrorListener listener)
792         throws IllegalArgumentException {
793         if (listener == null) {
794             ErrorMsg err = new ErrorMsg(ErrorMsg.ERROR_LISTENER_NULL_ERR,
795                                         "Transformer");
796             throw new IllegalArgumentException(err.toString());
797         }
798         _errorListener = listener;
799 
800         // Register a message handler to report xsl:messages
801     if (_translet != null)
802         _translet.setMessageHandler(new MessageHandler(_errorListener));
803     }
804 
805     /**
806      * Inform TrAX error listener of an error
807      */
808     private void postErrorToListener(String message) {
809         try {
810             _errorListener.error(new TransformerException(message));
811         }
812         catch (TransformerException e) {
813             // ignored - transformation cannot be continued
814         }
815     }
816 
817     /**
818      * Inform TrAX error listener of a warning
819      */
820     private void postWarningToListener(String message) {
821         try {
822             _errorListener.warning(new TransformerException(message));
823         }
824         catch (TransformerException e) {
825             // ignored - transformation cannot be continued
826         }
827     }
828 
829     /**
830      * The translet stores all CDATA sections set in the <xsl:output> element
831      * in a Hashtable. This method will re-construct the whitespace separated
832      * list of elements given in the <xsl:output> element.
833      */
834     private String makeCDATAString(Hashtable cdata) {
835         // Return a 'null' string if no CDATA section elements were specified
836         if (cdata == null) return null;
837 
838         final StringBuilder result = new StringBuilder();
839 
840         // Get an enumeration of all the elements in the hashtable
841         Enumeration elements = cdata.keys();
842         if (elements.hasMoreElements()) {
843             result.append((String)elements.nextElement());
844             while (elements.hasMoreElements()) {
845                 String element = (String)elements.nextElement();
846                 result.append(' ');
847                 result.append(element);
848             }
849         }
850 
851         return(result.toString());
852     }
853 
854     /**
855      * Implements JAXP's Transformer.getOutputProperties().
856      * Returns a copy of the output properties for the transformation. This is
857      * a set of layered properties. The first layer contains properties set by
858      * calls to setOutputProperty() and setOutputProperties() on this class,
859      * and the output settings defined in the stylesheet's <xsl:output>
860      * element makes up the second level, while the default XSLT output
861      * settings are returned on the third level.
862      *
863      * @return Properties in effect for this Transformer
864      */
865     @Override
866     public Properties getOutputProperties() {
867         return (Properties) _properties.clone();
868     }
869 
870     /**
871      * Implements JAXP's Transformer.getOutputProperty().
872      * Get an output property that is in effect for the transformation. The
873      * property specified may be a property that was set with setOutputProperty,
874      * or it may be a property specified in the stylesheet.
875      *
876      * @param name A non-null string that contains the name of the property
877      * @throws IllegalArgumentException if the property name is not known
878      */
879     @Override
880     public String getOutputProperty(String name)
881         throws IllegalArgumentException
882     {
883         if (!validOutputProperty(name)) {
884             ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
885             throw new IllegalArgumentException(err.toString());
886         }
887         return _properties.getProperty(name);
888     }
889 
890     /**
891      * Implements JAXP's Transformer.setOutputProperties().
892      * Set the output properties for the transformation. These properties
893      * will override properties set in the Templates with xsl:output.
894      * Unrecognised properties will be quitely ignored.
895      *
896      * @param properties The properties to use for the Transformer
897      * @throws IllegalArgumentException Never, errors are ignored
898      */
899     @Override
900     public void setOutputProperties(Properties properties)
901         throws IllegalArgumentException
902     {
903         if (properties != null) {
904             final Enumeration names = properties.propertyNames();
905 
906             while (names.hasMoreElements()) {
907                 final String name = (String) names.nextElement();
908 
909                 // Ignore lower layer properties
910                 if (isDefaultProperty(name, properties)) continue;
911 
912                 if (validOutputProperty(name)) {
913                     _properties.setProperty(name, properties.getProperty(name));
914                 }
915                 else {
916                     ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
917                     throw new IllegalArgumentException(err.toString());
918                 }
919             }
920         }
921         else {
922             _properties = _propertiesClone;
923         }
924     }
925 
926     /**
927      * Implements JAXP's Transformer.setOutputProperty().
928      * Get an output property that is in effect for the transformation. The
929      * property specified may be a property that was set with
930      * setOutputProperty(), or it may be a property specified in the stylesheet.
931      *
932      * @param name The name of the property to set
933      * @param value The value to assign to the property
934      * @throws IllegalArgumentException Never, errors are ignored
935      */
936     @Override
937     public void setOutputProperty(String name, String value)
938         throws IllegalArgumentException
939     {
940         if (!validOutputProperty(name)) {
941             ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
942             throw new IllegalArgumentException(err.toString());
943         }
944         _properties.setProperty(name, value);
945     }
946 
947     /**
948      * Internal method to pass any properties to the translet prior to
949      * initiating the transformation
950      */
951     private void transferOutputProperties(AbstractTranslet translet)
952     {
953         // Return right now if no properties are set
954         if (_properties == null) return;
955 
956         // Get a list of all the defined properties
957         Enumeration names = _properties.propertyNames();
958         while (names.hasMoreElements()) {
959             // Note the use of get() instead of getProperty()
960             String name  = (String) names.nextElement();
961             String value = (String) _properties.get(name);
962 
963             // Ignore default properties
964             if (value == null) continue;
965 
966             // Pass property value to translet - override previous setting
967             if (name.equals(OutputKeys.ENCODING)) {
968                 translet._encoding = value;
969             }
970             else if (name.equals(OutputKeys.METHOD)) {
971                 translet._method = value;
972             }
973             else if (name.equals(OutputKeys.DOCTYPE_PUBLIC)) {
974                 translet._doctypePublic = value;
975             }
976             else if (name.equals(OutputKeys.DOCTYPE_SYSTEM)) {
977                 translet._doctypeSystem = value;
978             }
979             else if (name.equals(OutputKeys.MEDIA_TYPE)) {
980                 translet._mediaType = value;
981             }
982             else if (name.equals(OutputKeys.STANDALONE)) {
983                 translet._standalone = value;
984             }
985             else if (name.equals(OutputKeys.VERSION)) {
986                 translet._version = value;
987             }
988             else if (name.equals(OutputKeys.OMIT_XML_DECLARATION)) {
989                 translet._omitHeader =
990                     (value != null && value.toLowerCase().equals("yes"));
991             }
992             else if (name.equals(OutputKeys.INDENT)) {
993                 translet._indent =
994                     (value != null && value.toLowerCase().equals("yes"));
995             }
996             else if (name.equals(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL +"indent-amount")) {
997                  if (value != null) {
998                      translet._indentamount = Integer.parseInt(value);
999                  }
1000             }
1001             else if (name.equals(OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL +"indent-amount")) {
1002                  if (value != null) {
1003                      translet._indentamount = Integer.parseInt(value);
1004                  }
1005             }
1006             else if (name.equals(OutputKeys.CDATA_SECTION_ELEMENTS)) {
1007                 if (value != null) {
1008                     translet._cdata = null; // clear previous setting
1009                     StringTokenizer e = new StringTokenizer(value);
1010                     while (e.hasMoreTokens()) {
1011                         translet.addCdataElement(e.nextToken());
1012                     }
1013                 }
1014             }
1015             else if (name.equals(OutputPropertiesFactory.ORACLE_IS_STANDALONE)) {
1016                  if (value != null && value.equals("yes")) {
1017                      translet._isStandalone = true;
1018                  }
1019             }
1020         }
1021     }
1022 
1023     /**
1024      * This method is used to pass any properties to the output handler
1025      * when running the identity transform.
1026      */
1027     public void transferOutputProperties(SerializationHandler handler)
1028     {
1029         // Return right now if no properties are set
1030         if (_properties == null) return;
1031 
1032         String doctypePublic = null;
1033         String doctypeSystem = null;
1034 
1035         // Get a list of all the defined properties
1036         Enumeration names = _properties.propertyNames();
1037         while (names.hasMoreElements()) {
1038             // Note the use of get() instead of getProperty()
1039             String name  = (String) names.nextElement();
1040             String value = (String) _properties.get(name);
1041 
1042             // Ignore default properties
1043             if (value == null) continue;
1044 
1045             // Pass property value to translet - override previous setting
1046             if (name.equals(OutputKeys.DOCTYPE_PUBLIC)) {
1047                 doctypePublic = value;
1048             }
1049             else if (name.equals(OutputKeys.DOCTYPE_SYSTEM)) {
1050                 doctypeSystem = value;
1051             }
1052             else if (name.equals(OutputKeys.MEDIA_TYPE)) {
1053                 handler.setMediaType(value);
1054             }
1055             else if (name.equals(OutputKeys.STANDALONE)) {
1056                 handler.setStandalone(value);
1057             }
1058             else if (name.equals(OutputKeys.VERSION)) {
1059                 handler.setVersion(value);
1060             }
1061             else if (name.equals(OutputKeys.OMIT_XML_DECLARATION)) {
1062                 handler.setOmitXMLDeclaration(
1063                     value != null && value.toLowerCase().equals("yes"));
1064             }
1065             else if (name.equals(OutputKeys.INDENT)) {
1066                 handler.setIndent(
1067                     value != null && value.toLowerCase().equals("yes"));
1068             }
1069             else if (name.equals(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL +"indent-amount")) {
1070                 if (value != null) {
1071                     handler.setIndentAmount(Integer.parseInt(value));
1072                 }
1073             }
1074             else if (name.equals(OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL +"indent-amount")) {
1075                 if (value != null) {
1076                     handler.setIndentAmount(Integer.parseInt(value));
1077                 }
1078             }
1079             else if (name.equals(OutputPropertiesFactory.ORACLE_IS_STANDALONE)) {
1080                 if (value != null && value.equals("yes")) {
1081                     handler.setIsStandalone(true);
1082                 }
1083             }
1084             else if (name.equals(OutputKeys.CDATA_SECTION_ELEMENTS)) {
1085                 if (value != null) {
1086                     StringTokenizer e = new StringTokenizer(value);
1087                     Vector uriAndLocalNames = null;
1088                     while (e.hasMoreTokens()) {
1089                         final String token = e.nextToken();
1090 
1091                         // look for the last colon, as the String may be
1092                         // something like "http://abc.com:local"
1093                         int lastcolon = token.lastIndexOf(':');
1094                         String uri;
1095                         String localName;
1096                         if (lastcolon > 0) {
1097                             uri = token.substring(0, lastcolon);
1098                             localName = token.substring(lastcolon+1);
1099                         } else {
1100                             // no colon at all, lets hope this is the
1101                             // local name itself then
1102                             uri = null;
1103                             localName = token;
1104                         }
1105 
1106                         if (uriAndLocalNames == null) {
1107                             uriAndLocalNames = new Vector();
1108                         }
1109                         // add the uri/localName as a pair, in that order
1110                         uriAndLocalNames.addElement(uri);
1111                         uriAndLocalNames.addElement(localName);
1112                     }
1113                     handler.setCdataSectionElements(uriAndLocalNames);
1114                 }
1115             }
1116         }
1117 
1118         // Call setDoctype() if needed
1119         if (doctypePublic != null || doctypeSystem != null) {
1120             handler.setDoctype(doctypeSystem, doctypePublic);
1121         }
1122     }
1123 
1124     /**
1125      * Internal method to create the initial set of properties. There
1126      * are two layers of properties: the default layer and the base layer.
1127      * The latter contains properties defined in the stylesheet or by
1128      * the user using this API.
1129      */
1130     private Properties createOutputProperties(Properties outputProperties) {
1131         final Properties defaults = new Properties();
1132         setDefaults(defaults, "xml");
1133 
1134         // Copy propeties set in stylesheet to base
1135         final Properties base = new Properties(defaults);
1136         if (outputProperties != null) {
1137             final Enumeration names = outputProperties.propertyNames();
1138             while (names.hasMoreElements()) {
1139                 final String name = (String) names.nextElement();
1140                 base.setProperty(name, outputProperties.getProperty(name));
1141             }
1142         }
1143         else {
1144             base.setProperty(OutputKeys.ENCODING, _translet._encoding);
1145             if (_translet._method != null)
1146                 base.setProperty(OutputKeys.METHOD, _translet._method);
1147         }
1148 
1149         // Update defaults based on output method
1150         final String method = base.getProperty(OutputKeys.METHOD);
1151         if (method != null) {
1152             if (method.equals("html")) {
1153                 setDefaults(defaults,"html");
1154             }
1155             else if (method.equals("text")) {
1156                 setDefaults(defaults,"text");
1157             }
1158         }
1159 
1160         return base;
1161     }
1162 
1163         /**
1164          * Internal method to get the default properties from the
1165          * serializer factory and set them on the property object.
1166          * @param props a java.util.Property object on which the properties are set.
1167          * @param method The output method type, one of "xml", "text", "html" ...
1168          */
1169         private void setDefaults(Properties props, String method)
1170         {
1171                 final Properties method_props =
1172                         OutputPropertiesFactory.getDefaultMethodProperties(method);
1173                 {
1174                         final Enumeration names = method_props.propertyNames();
1175                         while (names.hasMoreElements())
1176                         {
1177                                 final String name = (String)names.nextElement();
1178                                 props.setProperty(name, method_props.getProperty(name));
1179                         }
1180                 }
1181         }
1182     /**
1183      * Verifies if a given output property name is a property defined in
1184      * the JAXP 1.1 / TrAX spec
1185      */
1186     private boolean validOutputProperty(String name) {
1187         return (name.equals(OutputKeys.ENCODING) ||
1188                 name.equals(OutputKeys.METHOD) ||
1189                 name.equals(OutputKeys.INDENT) ||
1190                 name.equals(OutputKeys.DOCTYPE_PUBLIC) ||
1191                 name.equals(OutputKeys.DOCTYPE_SYSTEM) ||
1192                 name.equals(OutputKeys.CDATA_SECTION_ELEMENTS) ||
1193                 name.equals(OutputKeys.MEDIA_TYPE) ||
1194                 name.equals(OutputKeys.OMIT_XML_DECLARATION)   ||
1195                 name.equals(OutputKeys.STANDALONE) ||
1196                 name.equals(OutputKeys.VERSION) ||
1197                 name.equals(OutputPropertiesFactory.ORACLE_IS_STANDALONE) ||
1198                 name.charAt(0) == '{');
1199     }
1200 
1201     /**
1202      * Checks if a given output property is default (2nd layer only)
1203      */
1204     private boolean isDefaultProperty(String name, Properties properties) {
1205         return (properties.get(name) == null);
1206     }
1207 
1208     /**
1209      * Implements JAXP's Transformer.setParameter()
1210      * Add a parameter for the transformation. The parameter is simply passed
1211      * on to the translet - no validation is performed - so any unused
1212      * parameters are quitely ignored by the translet.
1213      *
1214      * @param name The name of the parameter
1215      * @param value The value to assign to the parameter
1216      */
1217     @Override
1218     public void setParameter(String name, Object value) {
1219 
1220         if (value == null) {
1221             ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_SET_PARAM_VALUE, name);
1222             throw new IllegalArgumentException(err.toString());
1223         }
1224 
1225         if (_isIdentity) {
1226             if (_parameters == null) {
1227                 _parameters = new Hashtable();
1228             }
1229             _parameters.put(name, value);
1230         }
1231         else {
1232             _translet.addParameter(name, value);
1233         }
1234     }
1235 
1236     /**
1237      * Implements JAXP's Transformer.clearParameters()
1238      * Clear all parameters set with setParameter. Clears the translet's
1239      * parameter stack.
1240      */
1241     @Override
1242     public void clearParameters() {
1243         if (_isIdentity && _parameters != null) {
1244             _parameters.clear();
1245         }
1246         else {
1247             _translet.clearParameters();
1248         }
1249     }
1250 
1251     /**
1252      * Implements JAXP's Transformer.getParameter()
1253      * Returns the value of a given parameter. Note that the translet will not
1254      * keep values for parameters that were not defined in the stylesheet.
1255      *
1256      * @param name The name of the parameter
1257      * @return An object that contains the value assigned to the parameter
1258      */
1259     @Override
1260     public final Object getParameter(String name) {
1261         if (_isIdentity) {
1262             return (_parameters != null) ? _parameters.get(name) : null;
1263         }
1264         else {
1265             return _translet.getParameter(name);
1266         }
1267     }
1268 
1269     /**
1270      * Implements JAXP's Transformer.getURIResolver()
1271      * Set the object currently used to resolve URIs used in document().
1272      *
1273      * @return  The URLResolver object currently in use
1274      */
1275     @Override
1276     public URIResolver getURIResolver() {
1277         return _uriResolver;
1278     }
1279 
1280     /**
1281      * Implements JAXP's Transformer.setURIResolver()
1282      * Set an object that will be used to resolve URIs used in document().
1283      *
1284      * @param resolver The URIResolver to use in document()
1285      */
1286     @Override
1287     public void setURIResolver(URIResolver resolver) {
1288         _uriResolver = resolver;
1289     }
1290 
1291     /**
1292      * This class should only be used as a DOMCache for the translet if the
1293      * URIResolver has been set.
1294      *
1295      * The method implements XSLTC's DOMCache interface, which is used to
1296      * plug in an external document loader into a translet. This method acts
1297      * as an adapter between TrAX's URIResolver interface and XSLTC's
1298      * DOMCache interface. This approach is simple, but removes the
1299      * possibility of using external document caches with XSLTC.
1300      *
1301      * @param baseURI The base URI used by the document call.
1302      * @param href The href argument passed to the document function.
1303      * @param translet A reference to the translet requesting the document
1304      */
1305     @Override
1306     public DOM retrieveDocument(String baseURI, String href, Translet translet) {
1307         try {
1308             // Argument to document function was: document('');
1309             if (href.length() == 0) {
1310                 href = baseURI;
1311             }
1312 
1313             /*
1314              *  Fix for bug 24188
1315              *  Incase the _uriResolver.resolve(href,base) is null
1316              *  try to still  retrieve the document before returning null
1317              *  and throwing the FileNotFoundException in
1318              *  com.sun.org.apache.xalan.internal.xsltc.dom.LoadDocument
1319              *
1320              */
1321             Source resolvedSource = _uriResolver.resolve(href, baseURI);
1322             if (resolvedSource == null)  {
1323                 StreamSource streamSource = new StreamSource(
1324                      SystemIDResolver.getAbsoluteURI(href, baseURI));
1325                 return getDOM(streamSource) ;
1326             }
1327 
1328             return getDOM(resolvedSource);
1329         }
1330         catch (TransformerException e) {
1331             if (_errorListener != null)
1332                 postErrorToListener("File not found: " + e.getMessage());
1333             return(null);
1334         }
1335     }
1336 
1337     /**
1338      * Receive notification of a recoverable error.
1339      * The transformer must continue to provide normal parsing events after
1340      * invoking this method. It should still be possible for the application
1341      * to process the document through to the end.
1342      *
1343      * @param e The warning information encapsulated in a transformer
1344      * exception.
1345      * @throws TransformerException if the application chooses to discontinue
1346      * the transformation (always does in our case).
1347      */
1348     @Override
1349     public void error(TransformerException e)
1350         throws TransformerException
1351     {
1352         Throwable wrapped = e.getException();
1353         if (wrapped != null) {
1354             System.err.println(new ErrorMsg(ErrorMsg.ERROR_PLUS_WRAPPED_MSG,
1355                                             e.getMessageAndLocation(),
1356                                             wrapped.getMessage()));
1357         } else {
1358             System.err.println(new ErrorMsg(ErrorMsg.ERROR_MSG,
1359                                             e.getMessageAndLocation()));
1360         }
1361         throw e;
1362     }
1363 
1364     /**
1365      * Receive notification of a non-recoverable error.
1366      * The application must assume that the transformation cannot continue
1367      * after the Transformer has invoked this method, and should continue
1368      * (if at all) only to collect addition error messages. In fact,
1369      * Transformers are free to stop reporting events once this method has
1370      * been invoked.
1371      *
1372      * @param e The warning information encapsulated in a transformer
1373      * exception.
1374      * @throws TransformerException if the application chooses to discontinue
1375      * the transformation (always does in our case).
1376      */
1377     @Override
1378     public void fatalError(TransformerException e)
1379         throws TransformerException
1380     {
1381         Throwable wrapped = e.getException();
1382         if (wrapped != null) {
1383             System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_PLUS_WRAPPED_MSG,
1384                                             e.getMessageAndLocation(),
1385                                             wrapped.getMessage()));
1386         } else {
1387             System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_MSG,
1388                                             e.getMessageAndLocation()));
1389         }
1390         throw e;
1391     }
1392 
1393     /**
1394      * Receive notification of a warning.
1395      * Transformers can use this method to report conditions that are not
1396      * errors or fatal errors. The default behaviour is to take no action.
1397      * After invoking this method, the Transformer must continue with the
1398      * transformation. It should still be possible for the application to
1399      * process the document through to the end.
1400      *
1401      * @param e The warning information encapsulated in a transformer
1402      * exception.
1403      * @throws TransformerException if the application chooses to discontinue
1404      * the transformation (never does in our case).
1405      */
1406     @Override
1407     public void warning(TransformerException e)
1408         throws TransformerException
1409     {
1410         Throwable wrapped = e.getException();
1411         if (wrapped != null) {
1412             System.err.println(new ErrorMsg(ErrorMsg.WARNING_PLUS_WRAPPED_MSG,
1413                                             e.getMessageAndLocation(),
1414                                             wrapped.getMessage()));
1415         } else {
1416             System.err.println(new ErrorMsg(ErrorMsg.WARNING_MSG,
1417                                             e.getMessageAndLocation()));
1418         }
1419     }
1420 
1421     /**
1422      * This method resets  the Transformer to its original configuration
1423      * Transformer code is reset to the same state it was when it was
1424      * created
1425      * @since 1.5
1426      */
1427     @Override
1428     public void reset() {
1429 
1430         _method = null;
1431         _encoding = null;
1432         _sourceSystemId = null;
1433         _errorListener = this;
1434         _uriResolver = null;
1435         _dom = null;
1436         _parameters = null;
1437         _indentNumber = 0;
1438         setOutputProperties (null);
1439         _tohFactory = null;
1440         _ostream = null;
1441 
1442     }
1443 }