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