001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: TransformerIdentityImpl.java 575747 2007-09-14 16:28:37Z kcormier $ 020 */ 021 package org.apache.xalan.transformer; 022 023 import java.io.IOException; 024 import java.util.Hashtable; 025 import java.util.Properties; 026 027 import javax.xml.XMLConstants; 028 import javax.xml.parsers.DocumentBuilder; 029 import javax.xml.parsers.DocumentBuilderFactory; 030 import javax.xml.parsers.ParserConfigurationException; 031 import javax.xml.transform.ErrorListener; 032 import javax.xml.transform.OutputKeys; 033 import javax.xml.transform.Result; 034 import javax.xml.transform.Source; 035 import javax.xml.transform.Transformer; 036 import javax.xml.transform.TransformerException; 037 import javax.xml.transform.URIResolver; 038 import javax.xml.transform.dom.DOMResult; 039 import javax.xml.transform.dom.DOMSource; 040 import javax.xml.transform.sax.SAXResult; 041 import javax.xml.transform.sax.SAXSource; 042 import javax.xml.transform.sax.TransformerHandler; 043 import javax.xml.transform.stream.StreamSource; 044 import javax.xml.transform.stream.StreamResult; 045 046 import org.apache.xalan.res.XSLMessages; 047 import org.apache.xalan.res.XSLTErrorResources; 048 import org.apache.xalan.templates.OutputProperties; 049 import org.apache.xml.serializer.Serializer; 050 import org.apache.xml.serializer.SerializerFactory; 051 import org.apache.xml.serializer.Method; 052 import org.apache.xml.utils.DOMBuilder; 053 import org.apache.xml.utils.XMLReaderManager; 054 055 import org.w3c.dom.Document; 056 import org.w3c.dom.DocumentFragment; 057 import org.w3c.dom.Node; 058 059 import org.xml.sax.Attributes; 060 import org.xml.sax.ContentHandler; 061 import org.xml.sax.DTDHandler; 062 import org.xml.sax.InputSource; 063 import org.xml.sax.Locator; 064 import org.xml.sax.SAXException; 065 import org.xml.sax.XMLReader; 066 import org.xml.sax.ext.DeclHandler; 067 import org.xml.sax.ext.LexicalHandler; 068 069 /** 070 * This class implements an identity transformer for 071 * {@link javax.xml.transform.sax.SAXTransformerFactory#newTransformerHandler()} 072 * and {@link javax.xml.transform.TransformerFactory#newTransformer()}. It 073 * simply feeds SAX events directly to a serializer ContentHandler, if the 074 * result is a stream. If the result is a DOM, it will send the events to 075 * {@link org.apache.xml.utils.DOMBuilder}. If the result is another 076 * content handler, it will simply pass the events on. 077 */ 078 public class TransformerIdentityImpl extends Transformer 079 implements TransformerHandler, DeclHandler 080 { 081 082 /** 083 * Constructor TransformerIdentityImpl creates an identity transform. 084 * 085 */ 086 public TransformerIdentityImpl(boolean isSecureProcessing) 087 { 088 m_outputFormat = new OutputProperties(Method.XML); 089 m_isSecureProcessing = isSecureProcessing; 090 } 091 092 /** 093 * Constructor TransformerIdentityImpl creates an identity transform. 094 * 095 */ 096 public TransformerIdentityImpl() 097 { 098 this(false); 099 } 100 101 /** 102 * Enables the user of the TransformerHandler to set the 103 * to set the Result for the transformation. 104 * 105 * @param result A Result instance, should not be null. 106 * 107 * @throws IllegalArgumentException if result is invalid for some reason. 108 */ 109 public void setResult(Result result) throws IllegalArgumentException 110 { 111 if(null == result) 112 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_NULL, null)); //"Result should not be null"); 113 m_result = result; 114 } 115 116 /** 117 * Set the base ID (URI or system ID) from where relative 118 * URLs will be resolved. 119 * @param systemID Base URI for the source tree. 120 */ 121 public void setSystemId(String systemID) 122 { 123 m_systemID = systemID; 124 } 125 126 /** 127 * Get the base ID (URI or system ID) from where relative 128 * URLs will be resolved. 129 * @return The systemID that was set with {@link #setSystemId}. 130 */ 131 public String getSystemId() 132 { 133 return m_systemID; 134 } 135 136 /** 137 * Get the Transformer associated with this handler, which 138 * is needed in order to set parameters and output properties. 139 * 140 * @return non-null reference to the transformer. 141 */ 142 public Transformer getTransformer() 143 { 144 return this; 145 } 146 147 /** 148 * Reset the status of the transformer. 149 */ 150 public void reset() 151 { 152 m_flushedStartDoc = false; 153 m_foundFirstElement = false; 154 m_outputStream = null; 155 clearParameters(); 156 m_result = null; 157 m_resultContentHandler = null; 158 m_resultDeclHandler = null; 159 m_resultDTDHandler = null; 160 m_resultLexicalHandler = null; 161 m_serializer = null; 162 m_systemID = null; 163 m_URIResolver = null; 164 m_outputFormat = new OutputProperties(Method.XML); 165 } 166 167 /** 168 * Create a result ContentHandler from a Result object, based 169 * on the current OutputProperties. 170 * 171 * @param outputTarget Where the transform result should go, 172 * should not be null. 173 * 174 * @return A valid ContentHandler that will create the 175 * result tree when it is fed SAX events. 176 * 177 * @throws TransformerException 178 */ 179 private void createResultContentHandler(Result outputTarget) 180 throws TransformerException 181 { 182 183 if (outputTarget instanceof SAXResult) 184 { 185 SAXResult saxResult = (SAXResult) outputTarget; 186 187 m_resultContentHandler = saxResult.getHandler(); 188 m_resultLexicalHandler = saxResult.getLexicalHandler(); 189 190 if (m_resultContentHandler instanceof Serializer) 191 { 192 193 // Dubious but needed, I think. 194 m_serializer = (Serializer) m_resultContentHandler; 195 } 196 } 197 else if (outputTarget instanceof DOMResult) 198 { 199 DOMResult domResult = (DOMResult) outputTarget; 200 Node outputNode = domResult.getNode(); 201 Node nextSibling = domResult.getNextSibling(); 202 Document doc; 203 short type; 204 205 if (null != outputNode) 206 { 207 type = outputNode.getNodeType(); 208 doc = (Node.DOCUMENT_NODE == type) 209 ? (Document) outputNode : outputNode.getOwnerDocument(); 210 } 211 else 212 { 213 try 214 { 215 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 216 217 dbf.setNamespaceAware(true); 218 219 if (m_isSecureProcessing) 220 { 221 try 222 { 223 dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 224 } 225 catch (ParserConfigurationException pce) {} 226 } 227 228 DocumentBuilder db = dbf.newDocumentBuilder(); 229 230 doc = db.newDocument(); 231 } 232 catch (ParserConfigurationException pce) 233 { 234 throw new TransformerException(pce); 235 } 236 237 outputNode = doc; 238 type = outputNode.getNodeType(); 239 240 ((DOMResult) outputTarget).setNode(outputNode); 241 } 242 243 DOMBuilder domBuilder = 244 (Node.DOCUMENT_FRAGMENT_NODE == type) 245 ? new DOMBuilder(doc, (DocumentFragment) outputNode) 246 : new DOMBuilder(doc, outputNode); 247 248 if (nextSibling != null) 249 domBuilder.setNextSibling(nextSibling); 250 251 m_resultContentHandler = domBuilder; 252 m_resultLexicalHandler = domBuilder; 253 } 254 else if (outputTarget instanceof StreamResult) 255 { 256 StreamResult sresult = (StreamResult) outputTarget; 257 258 try 259 { 260 Serializer serializer = 261 SerializerFactory.getSerializer(m_outputFormat.getProperties()); 262 263 m_serializer = serializer; 264 265 if (null != sresult.getWriter()) 266 serializer.setWriter(sresult.getWriter()); 267 else if (null != sresult.getOutputStream()) 268 serializer.setOutputStream(sresult.getOutputStream()); 269 else if (null != sresult.getSystemId()) 270 { 271 String fileURL = sresult.getSystemId(); 272 273 if (fileURL.startsWith("file:///")) { 274 if (fileURL.substring(8).indexOf(":") >0) { 275 fileURL = fileURL.substring(8); 276 } else { 277 fileURL = fileURL.substring(7); 278 } 279 } else if (fileURL.startsWith("file:/")) { 280 if (fileURL.substring(6).indexOf(":") >0) { 281 fileURL = fileURL.substring(6); 282 } else { 283 fileURL = fileURL.substring(5); 284 } 285 } 286 287 m_outputStream = new java.io.FileOutputStream(fileURL); 288 serializer.setOutputStream(m_outputStream); 289 } 290 else 291 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!"); 292 293 m_resultContentHandler = serializer.asContentHandler(); 294 } 295 catch (IOException ioe) 296 { 297 throw new TransformerException(ioe); 298 } 299 } 300 else 301 { 302 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type " 303 // + outputTarget.getClass().getName() 304 // + "!"); 305 } 306 307 if (m_resultContentHandler instanceof DTDHandler) 308 m_resultDTDHandler = (DTDHandler) m_resultContentHandler; 309 310 if (m_resultContentHandler instanceof DeclHandler) 311 m_resultDeclHandler = (DeclHandler) m_resultContentHandler; 312 313 if (m_resultContentHandler instanceof LexicalHandler) 314 m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler; 315 } 316 317 /** 318 * Process the source tree to the output result. 319 * @param source The input for the source tree. 320 * 321 * @param outputTarget The output target. 322 * 323 * @throws TransformerException If an unrecoverable error occurs 324 * during the course of the transformation. 325 */ 326 public void transform(Source source, Result outputTarget) 327 throws TransformerException 328 { 329 330 createResultContentHandler(outputTarget); 331 332 /* 333 * According to JAXP1.2, new SAXSource()/StreamSource() 334 * should create an empty input tree, with a default root node. 335 * new DOMSource()creates an empty document using DocumentBuilder. 336 * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations, 337 * since there is no clear spec. how to create an empty tree when 338 * both SAXSource() and StreamSource() are used. 339 */ 340 if ((source instanceof StreamSource && source.getSystemId()==null && 341 ((StreamSource)source).getInputStream()==null && 342 ((StreamSource)source).getReader()==null)|| 343 (source instanceof SAXSource && 344 ((SAXSource)source).getInputSource()==null && 345 ((SAXSource)source).getXMLReader()==null )|| 346 (source instanceof DOMSource && ((DOMSource)source).getNode()==null)){ 347 try { 348 DocumentBuilderFactory builderF = DocumentBuilderFactory.newInstance(); 349 DocumentBuilder builder = builderF.newDocumentBuilder(); 350 String systemID = source.getSystemId(); 351 source = new DOMSource(builder.newDocument()); 352 353 // Copy system ID from original, empty Source to new Source 354 if (systemID != null) { 355 source.setSystemId(systemID); 356 } 357 } catch (ParserConfigurationException e){ 358 throw new TransformerException(e.getMessage()); 359 } 360 } 361 362 try 363 { 364 if (source instanceof DOMSource) 365 { 366 DOMSource dsource = (DOMSource) source; 367 368 m_systemID = dsource.getSystemId(); 369 370 Node dNode = dsource.getNode(); 371 372 if (null != dNode) 373 { 374 try 375 { 376 if(dNode.getNodeType() == Node.ATTRIBUTE_NODE) 377 this.startDocument(); 378 try 379 { 380 if(dNode.getNodeType() == Node.ATTRIBUTE_NODE) 381 { 382 String data = dNode.getNodeValue(); 383 char[] chars = data.toCharArray(); 384 characters(chars, 0, chars.length); 385 } 386 else 387 { 388 org.apache.xml.serializer.TreeWalker walker; 389 walker = new org.apache.xml.serializer.TreeWalker(this, m_systemID); 390 walker.traverse(dNode); 391 } 392 } 393 finally 394 { 395 if(dNode.getNodeType() == Node.ATTRIBUTE_NODE) 396 this.endDocument(); 397 } 398 } 399 catch (SAXException se) 400 { 401 throw new TransformerException(se); 402 } 403 404 return; 405 } 406 else 407 { 408 String messageStr = XSLMessages.createMessage( 409 XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null); 410 411 throw new IllegalArgumentException(messageStr); 412 } 413 } 414 415 InputSource xmlSource = SAXSource.sourceToInputSource(source); 416 417 if (null == xmlSource) 418 { 419 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_SOURCE_TYPE, new Object[]{source.getClass().getName()})); //"Can't transform a Source of type " 420 //+ source.getClass().getName() + "!"); 421 } 422 423 if (null != xmlSource.getSystemId()) 424 m_systemID = xmlSource.getSystemId(); 425 426 XMLReader reader = null; 427 boolean managedReader = false; 428 429 try 430 { 431 if (source instanceof SAXSource) { 432 reader = ((SAXSource) source).getXMLReader(); 433 } 434 435 if (null == reader) { 436 try { 437 reader = XMLReaderManager.getInstance().getXMLReader(); 438 managedReader = true; 439 } catch (SAXException se) { 440 throw new TransformerException(se); 441 } 442 } else { 443 try { 444 reader.setFeature("http://xml.org/sax/features/namespace-prefixes", 445 true); 446 } catch (org.xml.sax.SAXException se) { 447 // We don't care. 448 } 449 } 450 451 // Get the input content handler, which will handle the 452 // parse events and create the source tree. 453 ContentHandler inputHandler = this; 454 455 reader.setContentHandler(inputHandler); 456 457 if (inputHandler instanceof org.xml.sax.DTDHandler) 458 reader.setDTDHandler((org.xml.sax.DTDHandler) inputHandler); 459 460 try 461 { 462 if (inputHandler instanceof org.xml.sax.ext.LexicalHandler) 463 reader.setProperty("http://xml.org/sax/properties/lexical-handler", 464 inputHandler); 465 466 if (inputHandler instanceof org.xml.sax.ext.DeclHandler) 467 reader.setProperty( 468 "http://xml.org/sax/properties/declaration-handler", 469 inputHandler); 470 } 471 catch (org.xml.sax.SAXException se){} 472 473 try 474 { 475 if (inputHandler instanceof org.xml.sax.ext.LexicalHandler) 476 reader.setProperty("http://xml.org/sax/handlers/LexicalHandler", 477 inputHandler); 478 479 if (inputHandler instanceof org.xml.sax.ext.DeclHandler) 480 reader.setProperty("http://xml.org/sax/handlers/DeclHandler", 481 inputHandler); 482 } 483 catch (org.xml.sax.SAXNotRecognizedException snre){} 484 485 reader.parse(xmlSource); 486 } 487 catch (org.apache.xml.utils.WrappedRuntimeException wre) 488 { 489 Throwable throwable = wre.getException(); 490 491 while (throwable 492 instanceof org.apache.xml.utils.WrappedRuntimeException) 493 { 494 throwable = 495 ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException(); 496 } 497 498 throw new TransformerException(wre.getException()); 499 } 500 catch (org.xml.sax.SAXException se) 501 { 502 throw new TransformerException(se); 503 } 504 catch (IOException ioe) 505 { 506 throw new TransformerException(ioe); 507 } finally { 508 if (managedReader) { 509 XMLReaderManager.getInstance().releaseXMLReader(reader); 510 } 511 } 512 } 513 finally 514 { 515 if(null != m_outputStream) 516 { 517 try 518 { 519 m_outputStream.close(); 520 } 521 catch(IOException ioe){} 522 m_outputStream = null; 523 } 524 } 525 } 526 527 /** 528 * Add a parameter for the transformation. 529 * 530 * <p>Pass a qualified name as a two-part string, the namespace URI 531 * enclosed in curly braces ({}), followed by the local name. If the 532 * name has a null URL, the String only contain the local name. An 533 * application can safely check for a non-null URI by testing to see if the first 534 * character of the name is a '{' character.</p> 535 * <p>For example, if a URI and local name were obtained from an element 536 * defined with <xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/>, 537 * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that 538 * no prefix is used.</p> 539 * 540 * @param name The name of the parameter, which may begin with a namespace URI 541 * in curly braces ({}). 542 * @param value The value object. This can be any valid Java object. It is 543 * up to the processor to provide the proper object coersion or to simply 544 * pass the object on for use in an extension. 545 */ 546 public void setParameter(String name, Object value) 547 { 548 if (value == null) { 549 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name})); 550 } 551 552 if (null == m_params) 553 { 554 m_params = new Hashtable(); 555 } 556 557 m_params.put(name, value); 558 } 559 560 /** 561 * Get a parameter that was explicitly set with setParameter 562 * or setParameters. 563 * 564 * <p>This method does not return a default parameter value, which 565 * cannot be determined until the node context is evaluated during 566 * the transformation process. 567 * 568 * 569 * @param name Name of the parameter. 570 * @return A parameter that has been set with setParameter. 571 */ 572 public Object getParameter(String name) 573 { 574 575 if (null == m_params) 576 return null; 577 578 return m_params.get(name); 579 } 580 581 /** 582 * Clear all parameters set with setParameter. 583 */ 584 public void clearParameters() 585 { 586 587 if (null == m_params) 588 return; 589 590 m_params.clear(); 591 } 592 593 /** 594 * Set an object that will be used to resolve URIs used in 595 * document(). 596 * 597 * <p>If the resolver argument is null, the URIResolver value will 598 * be cleared, and the default behavior will be used.</p> 599 * 600 * @param resolver An object that implements the URIResolver interface, 601 * or null. 602 */ 603 public void setURIResolver(URIResolver resolver) 604 { 605 m_URIResolver = resolver; 606 } 607 608 /** 609 * Get an object that will be used to resolve URIs used in 610 * document(), etc. 611 * 612 * @return An object that implements the URIResolver interface, 613 * or null. 614 */ 615 public URIResolver getURIResolver() 616 { 617 return m_URIResolver; 618 } 619 620 /** 621 * Set the output properties for the transformation. These 622 * properties will override properties set in the Templates 623 * with xsl:output. 624 * 625 * <p>If argument to this function is null, any properties 626 * previously set are removed, and the value will revert to the value 627 * defined in the templates object.</p> 628 * 629 * <p>Pass a qualified property key name as a two-part string, the namespace URI 630 * enclosed in curly braces ({}), followed by the local name. If the 631 * name has a null URL, the String only contain the local name. An 632 * application can safely check for a non-null URI by testing to see if the first 633 * character of the name is a '{' character.</p> 634 * <p>For example, if a URI and local name were obtained from an element 635 * defined with <xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/>, 636 * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that 637 * no prefix is used.</p> 638 * 639 * @param oformat A set of output properties that will be 640 * used to override any of the same properties in affect 641 * for the transformation. 642 * 643 * @see javax.xml.transform.OutputKeys 644 * @see java.util.Properties 645 * 646 * @throws IllegalArgumentException if any of the argument keys are not 647 * recognized and are not namespace qualified. 648 */ 649 public void setOutputProperties(Properties oformat) 650 throws IllegalArgumentException 651 { 652 653 if (null != oformat) 654 { 655 656 // See if an *explicit* method was set. 657 String method = (String) oformat.get(OutputKeys.METHOD); 658 659 if (null != method) 660 m_outputFormat = new OutputProperties(method); 661 else 662 m_outputFormat = new OutputProperties(); 663 664 m_outputFormat.copyFrom(oformat); 665 } 666 else { 667 // if oformat is null JAXP says that any props previously set are removed 668 // and we are to revert back to those in the templates object (i.e. Stylesheet). 669 m_outputFormat = null; 670 } 671 } 672 673 /** 674 * Get a copy of the output properties for the transformation. 675 * 676 * <p>The properties returned should contain properties set by the user, 677 * and properties set by the stylesheet, and these properties 678 * are "defaulted" by default properties specified by <a href="http://www.w3.org/TR/xslt#output">section 16 of the 679 * XSL Transformations (XSLT) W3C Recommendation</a>. The properties that 680 * were specifically set by the user or the stylesheet should be in the base 681 * Properties list, while the XSLT default properties that were not 682 * specifically set should be the default Properties list. Thus, 683 * getOutputProperties().getProperty(String key) will obtain any 684 * property in that was set by {@link #setOutputProperty}, 685 * {@link #setOutputProperties}, in the stylesheet, <em>or</em> the default 686 * properties, while 687 * getOutputProperties().get(String key) will only retrieve properties 688 * that were explicitly set by {@link #setOutputProperty}, 689 * {@link #setOutputProperties}, or in the stylesheet.</p> 690 * 691 * <p>Note that mutation of the Properties object returned will not 692 * effect the properties that the transformation contains.</p> 693 * 694 * <p>If any of the argument keys are not recognized and are not 695 * namespace qualified, the property will be ignored. In other words the 696 * behaviour is not orthogonal with setOutputProperties.</p> 697 * 698 * @return A copy of the set of output properties in effect 699 * for the next transformation. 700 * 701 * @see javax.xml.transform.OutputKeys 702 * @see java.util.Properties 703 */ 704 public Properties getOutputProperties() 705 { 706 return (Properties) m_outputFormat.getProperties().clone(); 707 } 708 709 /** 710 * Set an output property that will be in effect for the 711 * transformation. 712 * 713 * <p>Pass a qualified property name as a two-part string, the namespace URI 714 * enclosed in curly braces ({}), followed by the local name. If the 715 * name has a null URL, the String only contain the local name. An 716 * application can safely check for a non-null URI by testing to see if the first 717 * character of the name is a '{' character.</p> 718 * <p>For example, if a URI and local name were obtained from an element 719 * defined with <xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/>, 720 * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that 721 * no prefix is used.</p> 722 * 723 * <p>The Properties object that was passed to {@link #setOutputProperties} won't 724 * be effected by calling this method.</p> 725 * 726 * @param name A non-null String that specifies an output 727 * property name, which may be namespace qualified. 728 * @param value The non-null string value of the output property. 729 * 730 * @throws IllegalArgumentException If the property is not supported, and is 731 * not qualified with a namespace. 732 * 733 * @see javax.xml.transform.OutputKeys 734 */ 735 public void setOutputProperty(String name, String value) 736 throws IllegalArgumentException 737 { 738 739 if (!OutputProperties.isLegalPropertyKey(name)) 740 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: " 741 //+ name); 742 743 m_outputFormat.setProperty(name, value); 744 } 745 746 /** 747 * Get an output property that is in effect for the 748 * transformation. The property specified may be a property 749 * that was set with setOutputProperty, or it may be a 750 * property specified in the stylesheet. 751 * 752 * @param name A non-null String that specifies an output 753 * property name, which may be namespace qualified. 754 * 755 * @return The string value of the output property, or null 756 * if no property was found. 757 * 758 * @throws IllegalArgumentException If the property is not supported. 759 * 760 * @see javax.xml.transform.OutputKeys 761 */ 762 public String getOutputProperty(String name) throws IllegalArgumentException 763 { 764 765 String value = null; 766 OutputProperties props = m_outputFormat; 767 768 value = props.getProperty(name); 769 770 if (null == value) 771 { 772 if (!OutputProperties.isLegalPropertyKey(name)) 773 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: " 774 // + name); 775 } 776 777 return value; 778 } 779 780 /** 781 * Set the error event listener in effect for the transformation. 782 * 783 * @param listener The new error listener. 784 * @throws IllegalArgumentException if listener is null. 785 */ 786 public void setErrorListener(ErrorListener listener) 787 throws IllegalArgumentException 788 { 789 if (listener == null) 790 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null)); 791 else 792 m_errorListener = listener; 793 } 794 795 /** 796 * Get the error event handler in effect for the transformation. 797 * 798 * @return The current error handler, which should never be null. 799 */ 800 public ErrorListener getErrorListener() 801 { 802 return m_errorListener; 803 } 804 805 //////////////////////////////////////////////////////////////////// 806 // Default implementation of DTDHandler interface. 807 //////////////////////////////////////////////////////////////////// 808 809 /** 810 * Receive notification of a notation declaration. 811 * 812 * <p>By default, do nothing. Application writers may override this 813 * method in a subclass if they wish to keep track of the notations 814 * declared in a document.</p> 815 * 816 * @param name The notation name. 817 * @param publicId The notation public identifier, or null if not 818 * available. 819 * @param systemId The notation system identifier. 820 * @throws org.xml.sax.SAXException Any SAX exception, possibly 821 * wrapping another exception. 822 * @see org.xml.sax.DTDHandler#notationDecl 823 * 824 * @throws SAXException 825 */ 826 public void notationDecl(String name, String publicId, String systemId) 827 throws SAXException 828 { 829 if (null != m_resultDTDHandler) 830 m_resultDTDHandler.notationDecl(name, publicId, systemId); 831 } 832 833 /** 834 * Receive notification of an unparsed entity declaration. 835 * 836 * <p>By default, do nothing. Application writers may override this 837 * method in a subclass to keep track of the unparsed entities 838 * declared in a document.</p> 839 * 840 * @param name The entity name. 841 * @param publicId The entity public identifier, or null if not 842 * available. 843 * @param systemId The entity system identifier. 844 * @param notationName The name of the associated notation. 845 * @throws org.xml.sax.SAXException Any SAX exception, possibly 846 * wrapping another exception. 847 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 848 * 849 * @throws SAXException 850 */ 851 public void unparsedEntityDecl( 852 String name, String publicId, String systemId, String notationName) 853 throws SAXException 854 { 855 856 if (null != m_resultDTDHandler) 857 m_resultDTDHandler.unparsedEntityDecl(name, publicId, systemId, 858 notationName); 859 } 860 861 //////////////////////////////////////////////////////////////////// 862 // Default implementation of ContentHandler interface. 863 //////////////////////////////////////////////////////////////////// 864 865 /** 866 * Receive a Locator object for document events. 867 * 868 * <p>By default, do nothing. Application writers may override this 869 * method in a subclass if they wish to store the locator for use 870 * with other document events.</p> 871 * 872 * @param locator A locator for all SAX document events. 873 * @see org.xml.sax.ContentHandler#setDocumentLocator 874 * @see org.xml.sax.Locator 875 */ 876 public void setDocumentLocator(Locator locator) 877 { 878 try 879 { 880 if (null == m_resultContentHandler) 881 createResultContentHandler(m_result); 882 } 883 catch (TransformerException te) 884 { 885 throw new org.apache.xml.utils.WrappedRuntimeException(te); 886 } 887 888 m_resultContentHandler.setDocumentLocator(locator); 889 } 890 891 /** 892 * Receive notification of the beginning of the document. 893 * 894 * <p>By default, do nothing. Application writers may override this 895 * method in a subclass to take specific actions at the beginning 896 * of a document (such as allocating the root node of a tree or 897 * creating an output file).</p> 898 * 899 * @throws org.xml.sax.SAXException Any SAX exception, possibly 900 * wrapping another exception. 901 * @see org.xml.sax.ContentHandler#startDocument 902 * 903 * @throws SAXException 904 */ 905 public void startDocument() throws SAXException 906 { 907 908 try 909 { 910 if (null == m_resultContentHandler) 911 createResultContentHandler(m_result); 912 } 913 catch (TransformerException te) 914 { 915 throw new SAXException(te.getMessage(), te); 916 } 917 918 // Reset for multiple transforms with this transformer. 919 m_flushedStartDoc = false; 920 m_foundFirstElement = false; 921 } 922 923 boolean m_flushedStartDoc = false; 924 925 protected final void flushStartDoc() 926 throws SAXException 927 { 928 if(!m_flushedStartDoc) 929 { 930 if (m_resultContentHandler == null) 931 { 932 try 933 { 934 createResultContentHandler(m_result); 935 } 936 catch(TransformerException te) 937 { 938 throw new SAXException(te); 939 } 940 } 941 m_resultContentHandler.startDocument(); 942 m_flushedStartDoc = true; 943 } 944 } 945 946 /** 947 * Receive notification of the end of the document. 948 * 949 * <p>By default, do nothing. Application writers may override this 950 * method in a subclass to take specific actions at the end 951 * of a document (such as finalising a tree or closing an output 952 * file).</p> 953 * 954 * @throws org.xml.sax.SAXException Any SAX exception, possibly 955 * wrapping another exception. 956 * @see org.xml.sax.ContentHandler#endDocument 957 * 958 * @throws SAXException 959 */ 960 public void endDocument() throws SAXException 961 { 962 flushStartDoc(); 963 m_resultContentHandler.endDocument(); 964 } 965 966 /** 967 * Receive notification of the start of a Namespace mapping. 968 * 969 * <p>By default, do nothing. Application writers may override this 970 * method in a subclass to take specific actions at the start of 971 * each Namespace prefix scope (such as storing the prefix mapping).</p> 972 * 973 * @param prefix The Namespace prefix being declared. 974 * @param uri The Namespace URI mapped to the prefix. 975 * @throws org.xml.sax.SAXException Any SAX exception, possibly 976 * wrapping another exception. 977 * @see org.xml.sax.ContentHandler#startPrefixMapping 978 * 979 * @throws SAXException 980 */ 981 public void startPrefixMapping(String prefix, String uri) 982 throws SAXException 983 { 984 flushStartDoc(); 985 m_resultContentHandler.startPrefixMapping(prefix, uri); 986 } 987 988 /** 989 * Receive notification of the end of a Namespace mapping. 990 * 991 * <p>By default, do nothing. Application writers may override this 992 * method in a subclass to take specific actions at the end of 993 * each prefix mapping.</p> 994 * 995 * @param prefix The Namespace prefix being declared. 996 * @throws org.xml.sax.SAXException Any SAX exception, possibly 997 * wrapping another exception. 998 * @see org.xml.sax.ContentHandler#endPrefixMapping 999 * 1000 * @throws SAXException 1001 */ 1002 public void endPrefixMapping(String prefix) throws SAXException 1003 { 1004 flushStartDoc(); 1005 m_resultContentHandler.endPrefixMapping(prefix); 1006 } 1007 1008 /** 1009 * Receive notification of the start of an element. 1010 * 1011 * <p>By default, do nothing. Application writers may override this 1012 * method in a subclass to take specific actions at the start of 1013 * each element (such as allocating a new tree node or writing 1014 * output to a file).</p> 1015 * 1016 * @param uri The Namespace URI, or the empty string if the 1017 * element has no Namespace URI or if Namespace 1018 * processing is not being performed. 1019 * @param localName The local name (without prefix), or the 1020 * empty string if Namespace processing is not being 1021 * performed. 1022 * @param qName The qualified name (with prefix), or the 1023 * empty string if qualified names are not available. 1024 * @param attributes The specified or defaulted attributes. 1025 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1026 * wrapping another exception. 1027 * @see org.xml.sax.ContentHandler#startElement 1028 * 1029 * @throws SAXException 1030 */ 1031 public void startElement( 1032 String uri, String localName, String qName, Attributes attributes) 1033 throws SAXException 1034 { 1035 1036 if (!m_foundFirstElement && null != m_serializer) 1037 { 1038 m_foundFirstElement = true; 1039 1040 Serializer newSerializer; 1041 1042 try 1043 { 1044 newSerializer = SerializerSwitcher.switchSerializerIfHTML(uri, 1045 localName, m_outputFormat.getProperties(), m_serializer); 1046 } 1047 catch (TransformerException te) 1048 { 1049 throw new SAXException(te); 1050 } 1051 1052 if (newSerializer != m_serializer) 1053 { 1054 try 1055 { 1056 m_resultContentHandler = newSerializer.asContentHandler(); 1057 } 1058 catch (IOException ioe) // why? 1059 { 1060 throw new SAXException(ioe); 1061 } 1062 1063 if (m_resultContentHandler instanceof DTDHandler) 1064 m_resultDTDHandler = (DTDHandler) m_resultContentHandler; 1065 1066 if (m_resultContentHandler instanceof LexicalHandler) 1067 m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler; 1068 1069 m_serializer = newSerializer; 1070 } 1071 } 1072 flushStartDoc(); 1073 m_resultContentHandler.startElement(uri, localName, qName, attributes); 1074 } 1075 1076 /** 1077 * Receive notification of the end of an element. 1078 * 1079 * <p>By default, do nothing. Application writers may override this 1080 * method in a subclass to take specific actions at the end of 1081 * each element (such as finalising a tree node or writing 1082 * output to a file).</p> 1083 * 1084 * @param uri The Namespace URI, or the empty string if the 1085 * element has no Namespace URI or if Namespace 1086 * processing is not being performed. 1087 * @param localName The local name (without prefix), or the 1088 * empty string if Namespace processing is not being 1089 * performed. 1090 * @param qName The qualified name (with prefix), or the 1091 * empty string if qualified names are not available. 1092 * 1093 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1094 * wrapping another exception. 1095 * @see org.xml.sax.ContentHandler#endElement 1096 * 1097 * @throws SAXException 1098 */ 1099 public void endElement(String uri, String localName, String qName) 1100 throws SAXException 1101 { 1102 m_resultContentHandler.endElement(uri, localName, qName); 1103 } 1104 1105 /** 1106 * Receive notification of character data inside an element. 1107 * 1108 * <p>By default, do nothing. Application writers may override this 1109 * method to take specific actions for each chunk of character data 1110 * (such as adding the data to a node or buffer, or printing it to 1111 * a file).</p> 1112 * 1113 * @param ch The characters. 1114 * @param start The start position in the character array. 1115 * @param length The number of characters to use from the 1116 * character array. 1117 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1118 * wrapping another exception. 1119 * @see org.xml.sax.ContentHandler#characters 1120 * 1121 * @throws SAXException 1122 */ 1123 public void characters(char ch[], int start, int length) throws SAXException 1124 { 1125 flushStartDoc(); 1126 m_resultContentHandler.characters(ch, start, length); 1127 } 1128 1129 /** 1130 * Receive notification of ignorable whitespace in element content. 1131 * 1132 * <p>By default, do nothing. Application writers may override this 1133 * method to take specific actions for each chunk of ignorable 1134 * whitespace (such as adding data to a node or buffer, or printing 1135 * it to a file).</p> 1136 * 1137 * @param ch The whitespace characters. 1138 * @param start The start position in the character array. 1139 * @param length The number of characters to use from the 1140 * character array. 1141 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1142 * wrapping another exception. 1143 * @see org.xml.sax.ContentHandler#ignorableWhitespace 1144 * 1145 * @throws SAXException 1146 */ 1147 public void ignorableWhitespace(char ch[], int start, int length) 1148 throws SAXException 1149 { 1150 m_resultContentHandler.ignorableWhitespace(ch, start, length); 1151 } 1152 1153 /** 1154 * Receive notification of a processing instruction. 1155 * 1156 * <p>By default, do nothing. Application writers may override this 1157 * method in a subclass to take specific actions for each 1158 * processing instruction, such as setting status variables or 1159 * invoking other methods.</p> 1160 * 1161 * @param target The processing instruction target. 1162 * @param data The processing instruction data, or null if 1163 * none is supplied. 1164 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1165 * wrapping another exception. 1166 * @see org.xml.sax.ContentHandler#processingInstruction 1167 * 1168 * @throws SAXException 1169 */ 1170 public void processingInstruction(String target, String data) 1171 throws SAXException 1172 { 1173 flushStartDoc(); 1174 m_resultContentHandler.processingInstruction(target, data); 1175 } 1176 1177 /** 1178 * Receive notification of a skipped entity. 1179 * 1180 * <p>By default, do nothing. Application writers may override this 1181 * method in a subclass to take specific actions for each 1182 * processing instruction, such as setting status variables or 1183 * invoking other methods.</p> 1184 * 1185 * @param name The name of the skipped entity. 1186 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1187 * wrapping another exception. 1188 * @see org.xml.sax.ContentHandler#processingInstruction 1189 * 1190 * @throws SAXException 1191 */ 1192 public void skippedEntity(String name) throws SAXException 1193 { 1194 flushStartDoc(); 1195 m_resultContentHandler.skippedEntity(name); 1196 } 1197 1198 /** 1199 * Report the start of DTD declarations, if any. 1200 * 1201 * <p>Any declarations are assumed to be in the internal subset 1202 * unless otherwise indicated by a {@link #startEntity startEntity} 1203 * event.</p> 1204 * 1205 * <p>Note that the start/endDTD events will appear within 1206 * the start/endDocument events from ContentHandler and 1207 * before the first startElement event.</p> 1208 * 1209 * @param name The document type name. 1210 * @param publicId The declared public identifier for the 1211 * external DTD subset, or null if none was declared. 1212 * @param systemId The declared system identifier for the 1213 * external DTD subset, or null if none was declared. 1214 * @throws SAXException The application may raise an 1215 * exception. 1216 * @see #endDTD 1217 * @see #startEntity 1218 */ 1219 public void startDTD(String name, String publicId, String systemId) 1220 throws SAXException 1221 { 1222 flushStartDoc(); 1223 if (null != m_resultLexicalHandler) 1224 m_resultLexicalHandler.startDTD(name, publicId, systemId); 1225 } 1226 1227 /** 1228 * Report the end of DTD declarations. 1229 * 1230 * @throws SAXException The application may raise an exception. 1231 * @see #startDTD 1232 */ 1233 public void endDTD() throws SAXException 1234 { 1235 if (null != m_resultLexicalHandler) 1236 m_resultLexicalHandler.endDTD(); 1237 } 1238 1239 /** 1240 * Report the beginning of an entity in content. 1241 * 1242 * <p><strong>NOTE:</entity> entity references in attribute 1243 * values -- and the start and end of the document entity -- 1244 * are never reported.</p> 1245 * 1246 * <p>The start and end of the external DTD subset are reported 1247 * using the pseudo-name "[dtd]". All other events must be 1248 * properly nested within start/end entity events.</p> 1249 * 1250 * <p>Note that skipped entities will be reported through the 1251 * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity} 1252 * event, which is part of the ContentHandler interface.</p> 1253 * 1254 * @param name The name of the entity. If it is a parameter 1255 * entity, the name will begin with '%'. 1256 * @throws SAXException The application may raise an exception. 1257 * @see #endEntity 1258 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl 1259 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl 1260 */ 1261 public void startEntity(String name) throws SAXException 1262 { 1263 if (null != m_resultLexicalHandler) 1264 m_resultLexicalHandler.startEntity(name); 1265 } 1266 1267 /** 1268 * Report the end of an entity. 1269 * 1270 * @param name The name of the entity that is ending. 1271 * @throws SAXException The application may raise an exception. 1272 * @see #startEntity 1273 */ 1274 public void endEntity(String name) throws SAXException 1275 { 1276 if (null != m_resultLexicalHandler) 1277 m_resultLexicalHandler.endEntity(name); 1278 } 1279 1280 /** 1281 * Report the start of a CDATA section. 1282 * 1283 * <p>The contents of the CDATA section will be reported through 1284 * the regular {@link org.xml.sax.ContentHandler#characters 1285 * characters} event.</p> 1286 * 1287 * @throws SAXException The application may raise an exception. 1288 * @see #endCDATA 1289 */ 1290 public void startCDATA() throws SAXException 1291 { 1292 if (null != m_resultLexicalHandler) 1293 m_resultLexicalHandler.startCDATA(); 1294 } 1295 1296 /** 1297 * Report the end of a CDATA section. 1298 * 1299 * @throws SAXException The application may raise an exception. 1300 * @see #startCDATA 1301 */ 1302 public void endCDATA() throws SAXException 1303 { 1304 if (null != m_resultLexicalHandler) 1305 m_resultLexicalHandler.endCDATA(); 1306 } 1307 1308 /** 1309 * Report an XML comment anywhere in the document. 1310 * 1311 * <p>This callback will be used for comments inside or outside the 1312 * document element, including comments in the external DTD 1313 * subset (if read).</p> 1314 * 1315 * @param ch An array holding the characters in the comment. 1316 * @param start The starting position in the array. 1317 * @param length The number of characters to use from the array. 1318 * @throws SAXException The application may raise an exception. 1319 */ 1320 public void comment(char ch[], int start, int length) throws SAXException 1321 { 1322 flushStartDoc(); 1323 if (null != m_resultLexicalHandler) 1324 m_resultLexicalHandler.comment(ch, start, length); 1325 } 1326 1327 // Implement DeclHandler 1328 1329 /** 1330 * Report an element type declaration. 1331 * 1332 * <p>The content model will consist of the string "EMPTY", the 1333 * string "ANY", or a parenthesised group, optionally followed 1334 * by an occurrence indicator. The model will be normalized so 1335 * that all whitespace is removed,and will include the enclosing 1336 * parentheses.</p> 1337 * 1338 * @param name The element type name. 1339 * @param model The content model as a normalized string. 1340 * @exception SAXException The application may raise an exception. 1341 */ 1342 public void elementDecl (String name, String model) 1343 throws SAXException 1344 { 1345 if (null != m_resultDeclHandler) 1346 m_resultDeclHandler.elementDecl(name, model); 1347 } 1348 1349 1350 /** 1351 * Report an attribute type declaration. 1352 * 1353 * <p>Only the effective (first) declaration for an attribute will 1354 * be reported. The type will be one of the strings "CDATA", 1355 * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", 1356 * "ENTITIES", or "NOTATION", or a parenthesized token group with 1357 * the separator "|" and all whitespace removed.</p> 1358 * 1359 * @param eName The name of the associated element. 1360 * @param aName The name of the attribute. 1361 * @param type A string representing the attribute type. 1362 * @param valueDefault A string representing the attribute default 1363 * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if 1364 * none of these applies. 1365 * @param value A string representing the attribute's default value, 1366 * or null if there is none. 1367 * @exception SAXException The application may raise an exception. 1368 */ 1369 public void attributeDecl (String eName, 1370 String aName, 1371 String type, 1372 String valueDefault, 1373 String value) 1374 throws SAXException 1375 { 1376 if (null != m_resultDeclHandler) 1377 m_resultDeclHandler.attributeDecl(eName, aName, type, valueDefault, value); 1378 } 1379 1380 1381 /** 1382 * Report an internal entity declaration. 1383 * 1384 * <p>Only the effective (first) declaration for each entity 1385 * will be reported.</p> 1386 * 1387 * @param name The name of the entity. If it is a parameter 1388 * entity, the name will begin with '%'. 1389 * @param value The replacement text of the entity. 1390 * @exception SAXException The application may raise an exception. 1391 * @see #externalEntityDecl 1392 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 1393 */ 1394 public void internalEntityDecl (String name, String value) 1395 throws SAXException 1396 { 1397 if (null != m_resultDeclHandler) 1398 m_resultDeclHandler.internalEntityDecl(name, value); 1399 } 1400 1401 1402 /** 1403 * Report a parsed external entity declaration. 1404 * 1405 * <p>Only the effective (first) declaration for each entity 1406 * will be reported.</p> 1407 * 1408 * @param name The name of the entity. If it is a parameter 1409 * entity, the name will begin with '%'. 1410 * @param publicId The declared public identifier of the entity, or 1411 * null if none was declared. 1412 * @param systemId The declared system identifier of the entity. 1413 * @exception SAXException The application may raise an exception. 1414 * @see #internalEntityDecl 1415 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 1416 */ 1417 public void externalEntityDecl (String name, String publicId, 1418 String systemId) 1419 throws SAXException 1420 { 1421 if (null != m_resultDeclHandler) 1422 m_resultDeclHandler.externalEntityDecl(name, publicId, systemId); 1423 } 1424 1425 /** 1426 * This is null unless we own the stream. 1427 */ 1428 private java.io.FileOutputStream m_outputStream = null; 1429 1430 /** The content handler where result events will be sent. */ 1431 private ContentHandler m_resultContentHandler; 1432 1433 /** The lexical handler where result events will be sent. */ 1434 private LexicalHandler m_resultLexicalHandler; 1435 1436 /** The DTD handler where result events will be sent. */ 1437 private DTDHandler m_resultDTDHandler; 1438 1439 /** The Decl handler where result events will be sent. */ 1440 private DeclHandler m_resultDeclHandler; 1441 1442 /** The Serializer, which may or may not be null. */ 1443 private Serializer m_serializer; 1444 1445 /** The Result object. */ 1446 private Result m_result; 1447 1448 /** 1449 * The system ID, which is unused, but must be returned to fullfill the 1450 * TransformerHandler interface. 1451 */ 1452 private String m_systemID; 1453 1454 /** 1455 * The parameters, which is unused, but must be returned to fullfill the 1456 * Transformer interface. 1457 */ 1458 private Hashtable m_params; 1459 1460 /** The error listener for TrAX errors and warnings. */ 1461 private ErrorListener m_errorListener = 1462 new org.apache.xml.utils.DefaultErrorHandler(false); 1463 1464 /** 1465 * The URIResolver, which is unused, but must be returned to fullfill the 1466 * TransformerHandler interface. 1467 */ 1468 URIResolver m_URIResolver; 1469 1470 /** The output properties. */ 1471 private OutputProperties m_outputFormat; 1472 1473 /** Flag to set if we've found the first element, so we can tell if we have 1474 * to check to see if we should create an HTML serializer. */ 1475 boolean m_foundFirstElement; 1476 1477 /** 1478 * State of the secure processing feature. 1479 */ 1480 private boolean m_isSecureProcessing = false; 1481 }