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