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 package com.sun.org.apache.xalan.internal.xsltc.trax;
  22 
  23 import com.sun.org.apache.xalan.internal.XalanConstants;
  24 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  25 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
  26 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  27 import com.sun.org.apache.xalan.internal.xsltc.DOMCache;
  28 import com.sun.org.apache.xalan.internal.xsltc.StripFilter;
  29 import com.sun.org.apache.xalan.internal.xsltc.Translet;
  30 import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  32 import com.sun.org.apache.xalan.internal.xsltc.dom.DOMWSFilter;
  33 import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl;
  34 import com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager;
  35 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  36 import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
  37 import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
  38 import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory;
  39 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  40 import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
  41 import com.sun.org.apache.xml.internal.utils.XMLReaderManager;
  42 import java.io.File;
  43 import java.io.FileOutputStream;
  44 import java.io.IOException;
  45 import java.io.InputStream;
  46 import java.io.OutputStream;
  47 import java.io.Reader;
  48 import java.io.Writer;
  49 import java.net.URI;
  50 import java.net.URL;
  51 import java.net.URLConnection;
  52 import java.net.UnknownServiceException;
  53 import java.util.ArrayList;
  54 import java.util.Enumeration;
  55 import java.util.HashMap;
  56 import java.util.Map;
  57 import java.util.Properties;
  58 import java.util.StringTokenizer;
  59 import javax.xml.XMLConstants;
  60 import javax.xml.catalog.CatalogException;
  61 import javax.xml.catalog.CatalogFeatures;
  62 import javax.xml.catalog.CatalogManager;
  63 import javax.xml.catalog.CatalogResolver;
  64 import javax.xml.parsers.DocumentBuilder;
  65 import javax.xml.parsers.DocumentBuilderFactory;
  66 import javax.xml.parsers.ParserConfigurationException;
  67 import javax.xml.stream.XMLEventReader;
  68 import javax.xml.stream.XMLStreamReader;
  69 import javax.xml.transform.ErrorListener;
  70 import javax.xml.transform.OutputKeys;
  71 import javax.xml.transform.Result;
  72 import javax.xml.transform.Source;
  73 import javax.xml.transform.Transformer;
  74 import javax.xml.transform.TransformerException;
  75 import javax.xml.transform.URIResolver;
  76 import javax.xml.transform.dom.DOMResult;
  77 import javax.xml.transform.dom.DOMSource;
  78 import javax.xml.transform.sax.SAXResult;
  79 import javax.xml.transform.sax.SAXSource;
  80 import javax.xml.transform.stax.StAXResult;
  81 import javax.xml.transform.stax.StAXSource;
  82 import javax.xml.transform.stream.StreamResult;
  83 import javax.xml.transform.stream.StreamSource;
  84 import jdk.xml.internal.JdkXmlFeatures;
  85 import jdk.xml.internal.JdkXmlUtils;
  86 import org.xml.sax.ContentHandler;
  87 import org.xml.sax.InputSource;
  88 import org.xml.sax.SAXException;
  89 import org.xml.sax.XMLReader;
  90 import org.xml.sax.ext.LexicalHandler;
  91 
  92 /**
  93  * @author Morten Jorgensen
  94  * @author G. Todd Miller
  95  * @author Santiago Pericas-Geertsen
  96  */
  97 public 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 }