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 }