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