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