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