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