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