1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: TransformerFactoryImpl.java 468640 2006-10-28 06:53:53Z minchau $ 20 */ 21 package org.apache.xalan.processor; 22 23 import java.io.BufferedInputStream; 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.util.Enumeration; 27 import java.util.Properties; 28 29 import javax.xml.XMLConstants; 30 import javax.xml.transform.ErrorListener; 31 import javax.xml.transform.Source; 32 import javax.xml.transform.Templates; 33 import javax.xml.transform.Transformer; 34 import javax.xml.transform.TransformerConfigurationException; 35 import javax.xml.transform.TransformerException; 36 import javax.xml.transform.URIResolver; 37 import javax.xml.transform.dom.DOMResult; 38 import javax.xml.transform.dom.DOMSource; 39 import javax.xml.transform.sax.SAXResult; 40 import javax.xml.transform.sax.SAXSource; 41 import javax.xml.transform.sax.SAXTransformerFactory; 42 import javax.xml.transform.sax.TemplatesHandler; 43 import javax.xml.transform.sax.TransformerHandler; 44 import javax.xml.transform.stream.StreamResult; 45 import javax.xml.transform.stream.StreamSource; 46 47 import org.apache.xalan.res.XSLMessages; 48 import org.apache.xalan.res.XSLTErrorResources; 49 import org.apache.xalan.transformer.TrAXFilter; 50 import org.apache.xalan.transformer.TransformerIdentityImpl; 51 import org.apache.xalan.transformer.TransformerImpl; 52 import org.apache.xalan.transformer.XalanProperties; 53 54 import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM; 55 import org.apache.xml.utils.DefaultErrorHandler; 56 import org.apache.xml.utils.SystemIDResolver; 57 import org.apache.xml.utils.TreeWalker; 58 import org.apache.xml.utils.StylesheetPIHandler; 59 import org.apache.xml.utils.StopParseException; 60 61 import org.w3c.dom.Node; 62 63 import org.xml.sax.InputSource; 64 import org.xml.sax.XMLFilter; 65 import org.xml.sax.XMLReader; 66 import org.xml.sax.helpers.XMLReaderFactory; 67 68 /** 69 * The TransformerFactoryImpl, which implements the TRaX TransformerFactory 70 * interface, processes XSLT stylesheets into a Templates object 71 * (a StylesheetRoot). 72 */ 73 public class TransformerFactoryImpl extends SAXTransformerFactory 74 { 75 /** 76 * The path/filename of the property file: XSLTInfo.properties 77 * Maintenance note: see also 78 * <code>org.apache.xpath.functions.FuncSystemProperty.XSLT_PROPERTIES</code> 79 */ 80 public static final String XSLT_PROPERTIES = 81 "org/apache/xalan/res/XSLTInfo.properties"; 82 83 /** 84 * <p>State of secure processing feature.</p> 85 */ 86 private boolean m_isSecureProcessing = false; 87 88 /** 89 * Constructor TransformerFactoryImpl 90 * 91 */ 92 public TransformerFactoryImpl() 93 { 94 } 95 96 /** Static string to be used for incremental feature */ 97 public static final String FEATURE_INCREMENTAL = 98 "http://xml.apache.org/xalan/features/incremental"; 99 100 /** Static string to be used for optimize feature */ 101 public static final String FEATURE_OPTIMIZE = 102 "http://xml.apache.org/xalan/features/optimize"; 103 104 /** Static string to be used for source_location feature */ 105 public static final String FEATURE_SOURCE_LOCATION = 106 XalanProperties.SOURCE_LOCATION; 107 108 public javax.xml.transform.Templates processFromNode(Node node) 109 throws TransformerConfigurationException 110 { 111 112 try 113 { 114 TemplatesHandler builder = newTemplatesHandler(); 115 TreeWalker walker = new TreeWalker(builder, 116 new org.apache.xml.utils.DOM2Helper(), 117 builder.getSystemId()); 118 119 walker.traverse(node); 120 121 return builder.getTemplates(); 122 } 123 catch (org.xml.sax.SAXException se) 124 { 125 if (m_errorListener != null) 126 { 127 try 128 { 129 m_errorListener.fatalError(new TransformerException(se)); 130 } 131 catch (TransformerConfigurationException ex) 132 { 133 throw ex; 134 } 135 catch (TransformerException ex) 136 { 137 throw new TransformerConfigurationException(ex); 138 } 139 140 return null; 141 } 142 else 143 { 144 145 // Should remove this later... but right now diagnostics from 146 // TransformerConfigurationException are not good. 147 // se.printStackTrace(); 148 throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), se); 149 //"processFromNode failed", se); 150 } 151 } 152 catch (TransformerConfigurationException tce) 153 { 154 // Assume it's already been reported to the error listener. 155 throw tce; 156 } 157 /* catch (TransformerException tce) 158 { 159 // Assume it's already been reported to the error listener. 160 throw new TransformerConfigurationException(tce.getMessage(), tce); 161 }*/ 162 catch (Exception e) 163 { 164 if (m_errorListener != null) 165 { 166 try 167 { 168 m_errorListener.fatalError(new TransformerException(e)); 169 } 170 catch (TransformerConfigurationException ex) 171 { 172 throw ex; 173 } 174 catch (TransformerException ex) 175 { 176 throw new TransformerConfigurationException(ex); 177 } 178 179 return null; 180 } 181 else 182 { 183 // Should remove this later... but right now diagnostics from 184 // TransformerConfigurationException are not good. 185 // se.printStackTrace(); 186 throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), e); //"processFromNode failed", 187 //e); 188 } 189 } 190 } 191 192 /** 193 * The systemID that was specified in 194 * processFromNode(Node node, String systemID). 195 */ 196 private String m_DOMsystemID = null; 197 198 /** 199 * The systemID that was specified in 200 * processFromNode(Node node, String systemID). 201 * 202 * @return The systemID, or null. 203 */ 204 String getDOMsystemID() 205 { 206 return m_DOMsystemID; 207 } 208 209 /** 210 * Process the stylesheet from a DOM tree, if the 211 * processor supports the "http://xml.org/trax/features/dom/input" 212 * feature. 213 * 214 * @param node A DOM tree which must contain 215 * valid transform instructions that this processor understands. 216 * @param systemID The systemID from where xsl:includes and xsl:imports 217 * should be resolved from. 218 * 219 * @return A Templates object capable of being used for transformation purposes. 220 * 221 * @throws TransformerConfigurationException 222 */ 223 javax.xml.transform.Templates processFromNode(Node node, String systemID) 224 throws TransformerConfigurationException 225 { 226 227 m_DOMsystemID = systemID; 228 229 return processFromNode(node); 230 } 231 232 /** 233 * Get InputSource specification(s) that are associated with the 234 * given document specified in the source param, 235 * via the xml-stylesheet processing instruction 236 * (see http://www.w3.org/TR/xml-stylesheet/), and that matches 237 * the given criteria. Note that it is possible to return several stylesheets 238 * that match the criteria, in which case they are applied as if they were 239 * a list of imports or cascades. 240 * 241 * <p>Note that DOM2 has it's own mechanism for discovering stylesheets. 242 * Therefore, there isn't a DOM version of this method.</p> 243 * 244 * 245 * @param source The XML source that is to be searched. 246 * @param media The media attribute to be matched. May be null, in which 247 * case the prefered templates will be used (i.e. alternate = no). 248 * @param title The value of the title attribute to match. May be null. 249 * @param charset The value of the charset attribute to match. May be null. 250 * 251 * @return A Source object capable of being used to create a Templates object. 252 * 253 * @throws TransformerConfigurationException 254 */ 255 public Source getAssociatedStylesheet( 256 Source source, String media, String title, String charset) 257 throws TransformerConfigurationException 258 { 259 260 String baseID; 261 InputSource isource = null; 262 Node node = null; 263 XMLReader reader = null; 264 265 if (source instanceof DOMSource) 266 { 267 DOMSource dsource = (DOMSource) source; 268 269 node = dsource.getNode(); 270 baseID = dsource.getSystemId(); 271 } 272 else 273 { 274 isource = SAXSource.sourceToInputSource(source); 275 baseID = isource.getSystemId(); 276 } 277 278 // What I try to do here is parse until the first startElement 279 // is found, then throw a special exception in order to terminate 280 // the parse. 281 StylesheetPIHandler handler = new StylesheetPIHandler(baseID, media, 282 title, charset); 283 284 // Use URIResolver. Patch from Dmitri Ilyin 285 if (m_uriResolver != null) 286 { 287 handler.setURIResolver(m_uriResolver); 288 } 289 290 try 291 { 292 if (null != node) 293 { 294 TreeWalker walker = new TreeWalker(handler, new org.apache.xml.utils.DOM2Helper(), baseID); 295 296 walker.traverse(node); 297 } 298 else 299 { 300 301 // Use JAXP1.1 ( if possible ) 302 try 303 { 304 javax.xml.parsers.SAXParserFactory factory = 305 javax.xml.parsers.SAXParserFactory.newInstance(); 306 307 factory.setNamespaceAware(true); 308 309 if (m_isSecureProcessing) 310 { 311 try 312 { 313 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 314 } 315 catch (org.xml.sax.SAXException e) {} 316 } 317 318 javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser(); 319 320 reader = jaxpParser.getXMLReader(); 321 } 322 catch (javax.xml.parsers.ParserConfigurationException ex) 323 { 324 throw new org.xml.sax.SAXException(ex); 325 } 326 catch (javax.xml.parsers.FactoryConfigurationError ex1) 327 { 328 throw new org.xml.sax.SAXException(ex1.toString()); 329 } 330 catch (NoSuchMethodError ex2){} 331 catch (AbstractMethodError ame){} 332 333 if (null == reader) 334 { 335 reader = XMLReaderFactory.createXMLReader(); 336 } 337 338 if(m_isSecureProcessing) 339 { 340 reader.setFeature("http://xml.org/sax/features/external-general-entities",false); 341 } 342 // Need to set options! 343 reader.setContentHandler(handler); 344 reader.parse(isource); 345 } 346 } 347 catch (StopParseException spe) 348 { 349 350 // OK, good. 351 } 352 catch (org.xml.sax.SAXException se) 353 { 354 throw new TransformerConfigurationException( 355 "getAssociatedStylesheets failed", se); 356 } 357 catch (IOException ioe) 358 { 359 throw new TransformerConfigurationException( 360 "getAssociatedStylesheets failed", ioe); 361 } 362 363 return handler.getAssociatedStylesheet(); 364 } 365 366 /** 367 * Create a new Transformer object that performs a copy 368 * of the source to the result. 369 * 370 * @return A Transformer object that may be used to perform a transformation 371 * in a single thread, never null. 372 * 373 * @throws TransformerConfigurationException May throw this during 374 * the parse when it is constructing the 375 * Templates object and fails. 376 */ 377 public TemplatesHandler newTemplatesHandler() 378 throws TransformerConfigurationException 379 { 380 return new StylesheetHandler(this); 381 } 382 383 /** 384 * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s 385 * or <code>Template</code>s created by this factory.</p> 386 * 387 * <p> 388 * Feature names are fully qualified {@link java.net.URI}s. 389 * Implementations may define their own features. 390 * An {@link TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the 391 * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature. 392 * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state. 393 * </p> 394 * 395 * <p>See {@link javax.xml.transform.TransformerFactory} for full documentation of specific features.</p> 396 * 397 * @param name Feature name. 398 * @param value Is feature state <code>true</code> or <code>false</code>. 399 * 400 * @throws TransformerConfigurationException if this <code>TransformerFactory</code> 401 * or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature. 402 * @throws NullPointerException If the <code>name</code> parameter is null. 403 */ 404 public void setFeature(String name, boolean value) 405 throws TransformerConfigurationException { 406 407 // feature name cannot be null 408 if (name == null) { 409 throw new NullPointerException( 410 XSLMessages.createMessage( 411 XSLTErrorResources.ER_SET_FEATURE_NULL_NAME, null)); 412 } 413 414 // secure processing? 415 if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { 416 m_isSecureProcessing = value; 417 } 418 // This implementation does not support the setting of a feature other than 419 // the secure processing feature. 420 else 421 { 422 throw new TransformerConfigurationException( 423 XSLMessages.createMessage( 424 XSLTErrorResources.ER_UNSUPPORTED_FEATURE, 425 new Object[] {name})); 426 } 427 } 428 429 /** 430 * Look up the value of a feature. 431 * <p>The feature name is any fully-qualified URI. It is 432 * possible for an TransformerFactory to recognize a feature name but 433 * to be unable to return its value; this is especially true 434 * in the case of an adapter for a SAX1 Parser, which has 435 * no way of knowing whether the underlying parser is 436 * validating, for example.</p> 437 * 438 * @param name The feature name, which is a fully-qualified URI. 439 * @return The current state of the feature (true or false). 440 */ 441 public boolean getFeature(String name) { 442 443 // feature name cannot be null 444 if (name == null) 445 { 446 throw new NullPointerException( 447 XSLMessages.createMessage( 448 XSLTErrorResources.ER_GET_FEATURE_NULL_NAME, null)); 449 } 450 451 // Try first with identity comparison, which 452 // will be faster. 453 if ((DOMResult.FEATURE == name) || (DOMSource.FEATURE == name) 454 || (SAXResult.FEATURE == name) || (SAXSource.FEATURE == name) 455 || (StreamResult.FEATURE == name) 456 || (StreamSource.FEATURE == name) 457 || (SAXTransformerFactory.FEATURE == name) 458 || (SAXTransformerFactory.FEATURE_XMLFILTER == name)) 459 return true; 460 else if ((DOMResult.FEATURE.equals(name)) 461 || (DOMSource.FEATURE.equals(name)) 462 || (SAXResult.FEATURE.equals(name)) 463 || (SAXSource.FEATURE.equals(name)) 464 || (StreamResult.FEATURE.equals(name)) 465 || (StreamSource.FEATURE.equals(name)) 466 || (SAXTransformerFactory.FEATURE.equals(name)) 467 || (SAXTransformerFactory.FEATURE_XMLFILTER.equals(name))) 468 return true; 469 // secure processing? 470 else if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) 471 return m_isSecureProcessing; 472 else 473 // unknown feature 474 return false; 475 } 476 477 /** 478 * Flag set by FEATURE_OPTIMIZE. 479 * This feature specifies whether to Optimize stylesheet processing. By 480 * default it is set to true. 481 */ 482 private boolean m_optimize = true; 483 484 /** Flag set by FEATURE_SOURCE_LOCATION. 485 * This feature specifies whether the transformation phase should 486 * keep track of line and column numbers for the input source 487 * document. Note that this works only when that 488 * information is available from the source -- in other words, if you 489 * pass in a DOM, there's little we can do for you. 490 * 491 * The default is false. Setting it true may significantly 492 * increase storage cost per node. 493 */ 494 private boolean m_source_location = false; 495 496 /** 497 * Flag set by FEATURE_INCREMENTAL. 498 * This feature specifies whether to produce output incrementally, rather than 499 * waiting to finish parsing the input before generating any output. By 500 * default this attribute is set to false. 501 */ 502 private boolean m_incremental = false; 503 504 /** 505 * Allows the user to set specific attributes on the underlying 506 * implementation. 507 * 508 * @param name The name of the attribute. 509 * @param value The value of the attribute; Boolean or String="true"|"false" 510 * 511 * @throws IllegalArgumentException thrown if the underlying 512 * implementation doesn't recognize the attribute. 513 */ 514 public void setAttribute(String name, Object value) 515 throws IllegalArgumentException 516 { 517 if (name.equals(FEATURE_INCREMENTAL)) 518 { 519 if(value instanceof Boolean) 520 { 521 // Accept a Boolean object.. 522 m_incremental = ((Boolean)value).booleanValue(); 523 } 524 else if(value instanceof String) 525 { 526 // .. or a String object 527 m_incremental = (new Boolean((String)value)).booleanValue(); 528 } 529 else 530 { 531 // Give a more meaningful error message 532 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value); 533 } 534 } 535 else if (name.equals(FEATURE_OPTIMIZE)) 536 { 537 if(value instanceof Boolean) 538 { 539 // Accept a Boolean object.. 540 m_optimize = ((Boolean)value).booleanValue(); 541 } 542 else if(value instanceof String) 543 { 544 // .. or a String object 545 m_optimize = (new Boolean((String)value)).booleanValue(); 546 } 547 else 548 { 549 // Give a more meaningful error message 550 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value); 551 } 552 } 553 554 // Custom Xalan feature: annotate DTM with SAX source locator fields. 555 // This gets used during SAX2DTM instantiation. 556 // 557 // %REVIEW% Should the name of this field really be in XalanProperties? 558 // %REVIEW% I hate that it's a global static, but didn't want to change APIs yet. 559 else if(name.equals(FEATURE_SOURCE_LOCATION)) 560 { 561 if(value instanceof Boolean) 562 { 563 // Accept a Boolean object.. 564 m_source_location = ((Boolean)value).booleanValue(); 565 } 566 else if(value instanceof String) 567 { 568 // .. or a String object 569 m_source_location = (new Boolean((String)value)).booleanValue(); 570 } 571 else 572 { 573 // Give a more meaningful error message 574 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value); 575 } 576 } 577 578 else 579 { 580 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_SUPPORTED, new Object[]{name})); //name + "not supported"); 581 } 582 } 583 584 /** 585 * Allows the user to retrieve specific attributes on the underlying 586 * implementation. 587 * 588 * @param name The name of the attribute. 589 * @return value The value of the attribute. 590 * 591 * @throws IllegalArgumentException thrown if the underlying 592 * implementation doesn't recognize the attribute. 593 */ 594 public Object getAttribute(String name) throws IllegalArgumentException 595 { 596 if (name.equals(FEATURE_INCREMENTAL)) 597 { 598 return new Boolean(m_incremental); 599 } 600 else if (name.equals(FEATURE_OPTIMIZE)) 601 { 602 return new Boolean(m_optimize); 603 } 604 else if (name.equals(FEATURE_SOURCE_LOCATION)) 605 { 606 return new Boolean(m_source_location); 607 } 608 else 609 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ATTRIB_VALUE_NOT_RECOGNIZED, new Object[]{name})); //name + " attribute not recognized"); 610 } 611 612 /** 613 * Create an XMLFilter that uses the given source as the 614 * transformation instructions. 615 * 616 * @param src The source of the transformation instructions. 617 * 618 * @return An XMLFilter object, or null if this feature is not supported. 619 * 620 * @throws TransformerConfigurationException 621 */ 622 public XMLFilter newXMLFilter(Source src) 623 throws TransformerConfigurationException 624 { 625 626 Templates templates = newTemplates(src); 627 if( templates==null ) return null; 628 629 return newXMLFilter(templates); 630 } 631 632 /** 633 * Create an XMLFilter that uses the given source as the 634 * transformation instructions. 635 * 636 * @param templates non-null reference to Templates object. 637 * 638 * @return An XMLFilter object, or null if this feature is not supported. 639 * 640 * @throws TransformerConfigurationException 641 */ 642 public XMLFilter newXMLFilter(Templates templates) 643 throws TransformerConfigurationException 644 { 645 try 646 { 647 return new TrAXFilter(templates); 648 } 649 catch( TransformerConfigurationException ex ) 650 { 651 if( m_errorListener != null) 652 { 653 try 654 { 655 m_errorListener.fatalError( ex ); 656 return null; 657 } 658 catch( TransformerConfigurationException ex1 ) 659 { 660 throw ex1; 661 } 662 catch( TransformerException ex1 ) 663 { 664 throw new TransformerConfigurationException(ex1); 665 } 666 } 667 throw ex; 668 } 669 } 670 671 /** 672 * Get a TransformerHandler object that can process SAX 673 * ContentHandler events into a Result, based on the transformation 674 * instructions specified by the argument. 675 * 676 * @param src The source of the transformation instructions. 677 * 678 * @return TransformerHandler ready to transform SAX events. 679 * 680 * @throws TransformerConfigurationException 681 */ 682 public TransformerHandler newTransformerHandler(Source src) 683 throws TransformerConfigurationException 684 { 685 686 Templates templates = newTemplates(src); 687 if( templates==null ) return null; 688 689 return newTransformerHandler(templates); 690 } 691 692 /** 693 * Get a TransformerHandler object that can process SAX 694 * ContentHandler events into a Result, based on the Templates argument. 695 * 696 * @param templates The source of the transformation instructions. 697 * 698 * @return TransformerHandler ready to transform SAX events. 699 * @throws TransformerConfigurationException 700 */ 701 public TransformerHandler newTransformerHandler(Templates templates) 702 throws TransformerConfigurationException 703 { 704 try { 705 TransformerImpl transformer = 706 (TransformerImpl) templates.newTransformer(); 707 transformer.setURIResolver(m_uriResolver); 708 TransformerHandler th = 709 (TransformerHandler) transformer.getInputContentHandler(true); 710 711 return th; 712 } 713 catch( TransformerConfigurationException ex ) 714 { 715 if( m_errorListener != null ) 716 { 717 try 718 { 719 m_errorListener.fatalError( ex ); 720 return null; 721 } 722 catch (TransformerConfigurationException ex1 ) 723 { 724 throw ex1; 725 } 726 catch (TransformerException ex1 ) 727 { 728 throw new TransformerConfigurationException(ex1); 729 } 730 } 731 732 throw ex; 733 } 734 735 } 736 737 // /** The identity transform string, for support of newTransformerHandler() 738 // * and newTransformer(). */ 739 // private static final String identityTransform = 740 // "<xsl:stylesheet " + "xmlns:xsl='http://www.w3.org/1999/XSL/Transform' " 741 // + "version='1.0'>" + "<xsl:template match='/|node()'>" 742 // + "<xsl:copy-of select='.'/>" + "</xsl:template>" + "</xsl:stylesheet>"; 743 // 744 // /** The identity transform Templates, built from identityTransform, 745 // * for support of newTransformerHandler() and newTransformer(). */ 746 // private static Templates m_identityTemplate = null; 747 748 /** 749 * Get a TransformerHandler object that can process SAX 750 * ContentHandler events into a Result. 751 * 752 * @return TransformerHandler ready to transform SAX events. 753 * 754 * @throws TransformerConfigurationException 755 */ 756 public TransformerHandler newTransformerHandler() 757 throws TransformerConfigurationException 758 { 759 return new TransformerIdentityImpl(m_isSecureProcessing); 760 } 761 762 /** 763 * Process the source into a Transformer object. Care must 764 * be given to know that this object can not be used concurrently 765 * in multiple threads. 766 * 767 * @param source An object that holds a URL, input stream, etc. 768 * 769 * @return A Transformer object capable of 770 * being used for transformation purposes in a single thread. 771 * 772 * @throws TransformerConfigurationException May throw this during the parse when it 773 * is constructing the Templates object and fails. 774 */ 775 public Transformer newTransformer(Source source) 776 throws TransformerConfigurationException 777 { 778 try 779 { 780 Templates tmpl=newTemplates( source ); 781 /* this can happen if an ErrorListener is present and it doesn't 782 throw any exception in fatalError. 783 The spec says: "a Transformer must use this interface 784 instead of throwing an exception" - the newTemplates() does 785 that, and returns null. 786 */ 787 if( tmpl==null ) return null; 788 Transformer transformer = tmpl.newTransformer(); 789 transformer.setURIResolver(m_uriResolver); 790 return transformer; 791 } 792 catch( TransformerConfigurationException ex ) 793 { 794 if( m_errorListener != null ) 795 { 796 try 797 { 798 m_errorListener.fatalError( ex ); 799 return null; // TODO: but the API promises to never return null... 800 } 801 catch( TransformerConfigurationException ex1 ) 802 { 803 throw ex1; 804 } 805 catch( TransformerException ex1 ) 806 { 807 throw new TransformerConfigurationException( ex1 ); 808 } 809 } 810 throw ex; 811 } 812 } 813 814 /** 815 * Create a new Transformer object that performs a copy 816 * of the source to the result. 817 * 818 * @return A Transformer object capable of 819 * being used for transformation purposes in a single thread. 820 * 821 * @throws TransformerConfigurationException May throw this during 822 * the parse when it is constructing the 823 * Templates object and it fails. 824 */ 825 public Transformer newTransformer() throws TransformerConfigurationException 826 { 827 return new TransformerIdentityImpl(m_isSecureProcessing); 828 } 829 830 /** 831 * Process the source into a Templates object, which is likely 832 * a compiled representation of the source. This Templates object 833 * may then be used concurrently across multiple threads. Creating 834 * a Templates object allows the TransformerFactory to do detailed 835 * performance optimization of transformation instructions, without 836 * penalizing runtime transformation. 837 * 838 * @param source An object that holds a URL, input stream, etc. 839 * @return A Templates object capable of being used for transformation purposes. 840 * 841 * @throws TransformerConfigurationException May throw this during the parse when it 842 * is constructing the Templates object and fails. 843 */ 844 public Templates newTemplates(Source source) 845 throws TransformerConfigurationException 846 { 847 848 String baseID = source.getSystemId(); 849 850 if (null != baseID) { 851 baseID = SystemIDResolver.getAbsoluteURI(baseID); 852 } 853 854 855 if (source instanceof DOMSource) 856 { 857 DOMSource dsource = (DOMSource) source; 858 Node node = dsource.getNode(); 859 860 if (null != node) 861 return processFromNode(node, baseID); 862 else 863 { 864 String messageStr = XSLMessages.createMessage( 865 XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null); 866 867 throw new IllegalArgumentException(messageStr); 868 } 869 } 870 871 TemplatesHandler builder = newTemplatesHandler(); 872 builder.setSystemId(baseID); 873 874 try 875 { 876 InputSource isource = SAXSource.sourceToInputSource(source); 877 isource.setSystemId(baseID); 878 XMLReader reader = null; 879 880 if (source instanceof SAXSource) 881 reader = ((SAXSource) source).getXMLReader(); 882 883 if (null == reader) 884 { 885 886 // Use JAXP1.1 ( if possible ) 887 try 888 { 889 javax.xml.parsers.SAXParserFactory factory = 890 javax.xml.parsers.SAXParserFactory.newInstance(); 891 892 factory.setNamespaceAware(true); 893 894 if (m_isSecureProcessing) 895 { 896 try 897 { 898 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 899 } 900 catch (org.xml.sax.SAXException se) {} 901 } 902 903 javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser(); 904 905 reader = jaxpParser.getXMLReader(); 906 } 907 catch (javax.xml.parsers.ParserConfigurationException ex) 908 { 909 throw new org.xml.sax.SAXException(ex); 910 } 911 catch (javax.xml.parsers.FactoryConfigurationError ex1) 912 { 913 throw new org.xml.sax.SAXException(ex1.toString()); 914 } 915 catch (NoSuchMethodError ex2){} 916 catch (AbstractMethodError ame){} 917 } 918 919 if (null == reader) 920 reader = XMLReaderFactory.createXMLReader(); 921 922 // If you set the namespaces to true, we'll end up getting double 923 // xmlns attributes. Needs to be fixed. -sb 924 // reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); 925 reader.setContentHandler(builder); 926 reader.parse(isource); 927 } 928 catch (org.xml.sax.SAXException se) 929 { 930 if (m_errorListener != null) 931 { 932 try 933 { 934 m_errorListener.fatalError(new TransformerException(se)); 935 } 936 catch (TransformerConfigurationException ex1) 937 { 938 throw ex1; 939 } 940 catch (TransformerException ex1) 941 { 942 throw new TransformerConfigurationException(ex1); 943 } 944 } 945 else 946 { 947 throw new TransformerConfigurationException(se.getMessage(), se); 948 } 949 } 950 catch (Exception e) 951 { 952 if (m_errorListener != null) 953 { 954 try 955 { 956 m_errorListener.fatalError(new TransformerException(e)); 957 return null; 958 } 959 catch (TransformerConfigurationException ex1) 960 { 961 throw ex1; 962 } 963 catch (TransformerException ex1) 964 { 965 throw new TransformerConfigurationException(ex1); 966 } 967 } 968 else 969 { 970 throw new TransformerConfigurationException(e.getMessage(), e); 971 } 972 } 973 974 return builder.getTemplates(); 975 } 976 977 /** 978 * The object that implements the URIResolver interface, 979 * or null. 980 */ 981 URIResolver m_uriResolver; 982 983 /** 984 * Set an object that will be used to resolve URIs used in 985 * xsl:import, etc. This will be used as the default for the 986 * transformation. 987 * @param resolver An object that implements the URIResolver interface, 988 * or null. 989 */ 990 public void setURIResolver(URIResolver resolver) 991 { 992 m_uriResolver = resolver; 993 } 994 995 /** 996 * Get the object that will be used to resolve URIs used in 997 * xsl:import, etc. This will be used as the default for the 998 * transformation. 999 * 1000 * @return The URIResolver that was set with setURIResolver. 1001 */ 1002 public URIResolver getURIResolver() 1003 { 1004 return m_uriResolver; 1005 } 1006 1007 /** The error listener. */ 1008 private ErrorListener m_errorListener = new org.apache.xml.utils.DefaultErrorHandler(false); 1009 1010 /** 1011 * Get the error listener in effect for the TransformerFactory. 1012 * 1013 * @return A non-null reference to an error listener. 1014 */ 1015 public ErrorListener getErrorListener() 1016 { 1017 return m_errorListener; 1018 } 1019 1020 /** 1021 * Set an error listener for the TransformerFactory. 1022 * 1023 * @param listener Must be a non-null reference to an ErrorListener. 1024 * 1025 * @throws IllegalArgumentException if the listener argument is null. 1026 */ 1027 public void setErrorListener(ErrorListener listener) 1028 throws IllegalArgumentException 1029 { 1030 1031 if (null == listener) 1032 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ERRORLISTENER, null)); 1033 // "ErrorListener"); 1034 1035 m_errorListener = listener; 1036 } 1037 1038 /** 1039 * Return the state of the secure processing feature. 1040 * 1041 * @return state of the secure processing feature. 1042 */ 1043 public boolean isSecureProcessing() 1044 { 1045 return m_isSecureProcessing; 1046 } 1047 } 1048