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