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