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: TransformerFactoryImpl.java 1225273 2011-12-28 18:46:51Z mrglavas $ 020 */ 021 022 package org.apache.xalan.xsltc.trax; 023 024 import java.io.File; 025 import java.io.FileInputStream; 026 import java.io.FileNotFoundException; 027 import java.io.FilenameFilter; 028 import java.io.IOException; 029 import java.io.InputStream; 030 import java.net.MalformedURLException; 031 import java.net.URL; 032 import java.util.ArrayList; 033 import java.util.Enumeration; 034 import java.util.Hashtable; 035 import java.util.List; 036 import java.util.Properties; 037 import java.util.Vector; 038 import java.util.zip.ZipEntry; 039 import java.util.zip.ZipFile; 040 041 import javax.xml.XMLConstants; 042 import javax.xml.parsers.SAXParser; 043 import javax.xml.parsers.SAXParserFactory; 044 import javax.xml.transform.ErrorListener; 045 import javax.xml.transform.Source; 046 import javax.xml.transform.Templates; 047 import javax.xml.transform.Transformer; 048 import javax.xml.transform.TransformerConfigurationException; 049 import javax.xml.transform.TransformerException; 050 import javax.xml.transform.URIResolver; 051 import javax.xml.transform.dom.DOMResult; 052 import javax.xml.transform.dom.DOMSource; 053 import javax.xml.transform.sax.SAXResult; 054 import javax.xml.transform.sax.SAXSource; 055 import javax.xml.transform.sax.SAXTransformerFactory; 056 import javax.xml.transform.sax.TemplatesHandler; 057 import javax.xml.transform.sax.TransformerHandler; 058 import javax.xml.transform.stream.StreamResult; 059 import javax.xml.transform.stream.StreamSource; 060 061 import org.apache.xalan.xsltc.compiler.SourceLoader; 062 import org.apache.xalan.xsltc.compiler.XSLTC; 063 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 064 import org.apache.xalan.xsltc.dom.XSLTCDTMManager; 065 import org.apache.xml.utils.StopParseException; 066 import org.apache.xml.utils.StylesheetPIHandler; 067 import org.xml.sax.InputSource; 068 import org.xml.sax.XMLFilter; 069 import org.xml.sax.XMLReader; 070 import org.xml.sax.helpers.XMLReaderFactory; 071 072 /** 073 * Implementation of a JAXP1.1 TransformerFactory for Translets. 074 * @author G. Todd Miller 075 * @author Morten Jorgensen 076 * @author Santiago Pericas-Geertsen 077 */ 078 public class TransformerFactoryImpl 079 extends SAXTransformerFactory implements SourceLoader, ErrorListener 080 { 081 // Public constants for attributes supported by the XSLTC TransformerFactory. 082 public final static String TRANSLET_NAME = "translet-name"; 083 public final static String DESTINATION_DIRECTORY = "destination-directory"; 084 public final static String PACKAGE_NAME = "package-name"; 085 public final static String JAR_NAME = "jar-name"; 086 public final static String GENERATE_TRANSLET = "generate-translet"; 087 public final static String AUTO_TRANSLET = "auto-translet"; 088 public final static String USE_CLASSPATH = "use-classpath"; 089 public final static String DEBUG = "debug"; 090 public final static String ENABLE_INLINING = "enable-inlining"; 091 public final static String INDENT_NUMBER = "indent-number"; 092 093 /** 094 * This error listener is used only for this factory and is not passed to 095 * the Templates or Transformer objects that we create. 096 */ 097 private ErrorListener _errorListener = this; 098 099 /** 100 * This URIResolver is passed to all created Templates and Transformers 101 */ 102 private URIResolver _uriResolver = null; 103 104 /** 105 * As Gregor Samsa awoke one morning from uneasy dreams he found himself 106 * transformed in his bed into a gigantic insect. He was lying on his hard, 107 * as it were armour plated, back, and if he lifted his head a little he 108 * could see his big, brown belly divided into stiff, arched segments, on 109 * top of which the bed quilt could hardly keep in position and was about 110 * to slide off completely. His numerous legs, which were pitifully thin 111 * compared to the rest of his bulk, waved helplessly before his eyes. 112 * "What has happened to me?", he thought. It was no dream.... 113 */ 114 protected final static String DEFAULT_TRANSLET_NAME = "GregorSamsa"; 115 116 /** 117 * The class name of the translet 118 */ 119 private String _transletName = DEFAULT_TRANSLET_NAME; 120 121 /** 122 * The destination directory for the translet 123 */ 124 private String _destinationDirectory = null; 125 126 /** 127 * The package name prefix for all generated translet classes 128 */ 129 private String _packageName = null; 130 131 /** 132 * The jar file name which the translet classes are packaged into 133 */ 134 private String _jarFileName = null; 135 136 /** 137 * This Hashtable is used to store parameters for locating 138 * <?xml-stylesheet ...?> processing instructions in XML docs. 139 */ 140 private Hashtable _piParams = null; 141 142 /** 143 * The above hashtable stores objects of this class. 144 */ 145 private static class PIParamWrapper { 146 public String _media = null; 147 public String _title = null; 148 public String _charset = null; 149 150 public PIParamWrapper(String media, String title, String charset) { 151 _media = media; 152 _title = title; 153 _charset = charset; 154 } 155 } 156 157 /** 158 * Set to <code>true</code> when debugging is enabled. 159 */ 160 private boolean _debug = false; 161 162 /** 163 * Set to <code>true</code> when templates are inlined. 164 */ 165 private boolean _enableInlining = false; 166 167 /** 168 * Set to <code>true</code> when we want to generate 169 * translet classes from the stylesheet. 170 */ 171 private boolean _generateTranslet = false; 172 173 /** 174 * If this is set to <code>true</code>, we attempt to use translet classes 175 * for transformation if possible without compiling the stylesheet. The 176 * translet class is only used if its timestamp is newer than the timestamp 177 * of the stylesheet. 178 */ 179 private boolean _autoTranslet = false; 180 181 /** 182 * If this is set to <code>true</code>, we attempt to load the translet 183 * from the CLASSPATH. 184 */ 185 private boolean _useClasspath = false; 186 187 /** 188 * Number of indent spaces when indentation is turned on. 189 */ 190 private int _indentNumber = -1; 191 192 /** 193 * The provider of the XSLTC DTM Manager service. This is fixed for any 194 * instance of this class. In order to change service providers, a new 195 * XSLTC <code>TransformerFactory</code> must be instantiated. 196 * @see XSLTCDTMManager#getDTMManagerClass() 197 */ 198 private Class m_DTMManagerClass; 199 200 /** 201 * <p>State of secure processing feature.</p> 202 */ 203 private boolean _isSecureProcessing = false; 204 205 /** 206 * javax.xml.transform.sax.TransformerFactory implementation. 207 */ 208 public TransformerFactoryImpl() { 209 m_DTMManagerClass = XSLTCDTMManager.getDTMManagerClass(); 210 } 211 212 /** 213 * javax.xml.transform.sax.TransformerFactory implementation. 214 * Set the error event listener for the TransformerFactory, which is used 215 * for the processing of transformation instructions, and not for the 216 * transformation itself. 217 * 218 * @param listener The error listener to use with the TransformerFactory 219 * @throws IllegalArgumentException 220 */ 221 public void setErrorListener(ErrorListener listener) 222 throws IllegalArgumentException 223 { 224 if (listener == null) { 225 ErrorMsg err = new ErrorMsg(ErrorMsg.ERROR_LISTENER_NULL_ERR, 226 "TransformerFactory"); 227 throw new IllegalArgumentException(err.toString()); 228 } 229 _errorListener = listener; 230 } 231 232 /** 233 * javax.xml.transform.sax.TransformerFactory implementation. 234 * Get the error event handler for the TransformerFactory. 235 * 236 * @return The error listener used with the TransformerFactory 237 */ 238 public ErrorListener getErrorListener() { 239 return _errorListener; 240 } 241 242 /** 243 * javax.xml.transform.sax.TransformerFactory implementation. 244 * Returns the value set for a TransformerFactory attribute 245 * 246 * @param name The attribute name 247 * @return An object representing the attribute value 248 * @throws IllegalArgumentException 249 */ 250 public Object getAttribute(String name) 251 throws IllegalArgumentException 252 { 253 // Return value for attribute 'translet-name' 254 if (name.equals(TRANSLET_NAME)) { 255 return _transletName; 256 } 257 else if (name.equals(GENERATE_TRANSLET)) { 258 return _generateTranslet ? Boolean.TRUE : Boolean.FALSE; 259 } 260 else if (name.equals(AUTO_TRANSLET)) { 261 return _autoTranslet ? Boolean.TRUE : Boolean.FALSE; 262 } 263 else if (name.equals(ENABLE_INLINING)) { 264 if (_enableInlining) 265 return Boolean.TRUE; 266 else 267 return Boolean.FALSE; 268 } 269 270 // Throw an exception for all other attributes 271 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_ATTR_ERR, name); 272 throw new IllegalArgumentException(err.toString()); 273 } 274 275 /** 276 * javax.xml.transform.sax.TransformerFactory implementation. 277 * Sets the value for a TransformerFactory attribute. 278 * 279 * @param name The attribute name 280 * @param value An object representing the attribute value 281 * @throws IllegalArgumentException 282 */ 283 public void setAttribute(String name, Object value) 284 throws IllegalArgumentException 285 { 286 // Set the default translet name (ie. class name), which will be used 287 // for translets that cannot be given a name from their system-id. 288 if (name.equals(TRANSLET_NAME) && value instanceof String) { 289 _transletName = (String) value; 290 return; 291 } 292 else if (name.equals(DESTINATION_DIRECTORY) && value instanceof String) { 293 _destinationDirectory = (String) value; 294 return; 295 } 296 else if (name.equals(PACKAGE_NAME) && value instanceof String) { 297 _packageName = (String) value; 298 return; 299 } 300 else if (name.equals(JAR_NAME) && value instanceof String) { 301 _jarFileName = (String) value; 302 return; 303 } 304 else if (name.equals(GENERATE_TRANSLET)) { 305 if (value instanceof Boolean) { 306 _generateTranslet = ((Boolean) value).booleanValue(); 307 return; 308 } 309 else if (value instanceof String) { 310 _generateTranslet = ((String) value).equalsIgnoreCase("true"); 311 return; 312 } 313 } 314 else if (name.equals(AUTO_TRANSLET)) { 315 if (value instanceof Boolean) { 316 _autoTranslet = ((Boolean) value).booleanValue(); 317 return; 318 } 319 else if (value instanceof String) { 320 _autoTranslet = ((String) value).equalsIgnoreCase("true"); 321 return; 322 } 323 } 324 else if (name.equals(USE_CLASSPATH)) { 325 if (value instanceof Boolean) { 326 _useClasspath = ((Boolean) value).booleanValue(); 327 return; 328 } 329 else if (value instanceof String) { 330 _useClasspath = ((String) value).equalsIgnoreCase("true"); 331 return; 332 } 333 } 334 else if (name.equals(DEBUG)) { 335 if (value instanceof Boolean) { 336 _debug = ((Boolean) value).booleanValue(); 337 return; 338 } 339 else if (value instanceof String) { 340 _debug = ((String) value).equalsIgnoreCase("true"); 341 return; 342 } 343 } 344 else if (name.equals(ENABLE_INLINING)) { 345 if (value instanceof Boolean) { 346 _enableInlining = ((Boolean) value).booleanValue(); 347 return; 348 } 349 else if (value instanceof String) { 350 _enableInlining = ((String) value).equalsIgnoreCase("true"); 351 return; 352 } 353 } 354 else if (name.equals(INDENT_NUMBER)) { 355 if (value instanceof String) { 356 try { 357 _indentNumber = Integer.parseInt((String) value); 358 return; 359 } 360 catch (NumberFormatException e) { 361 // Falls through 362 } 363 } 364 else if (value instanceof Integer) { 365 _indentNumber = ((Integer) value).intValue(); 366 return; 367 } 368 } 369 370 // Throw an exception for all other attributes 371 final ErrorMsg err 372 = new ErrorMsg(ErrorMsg.JAXP_INVALID_ATTR_ERR, name); 373 throw new IllegalArgumentException(err.toString()); 374 } 375 376 /** 377 * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s 378 * or <code>Template</code>s created by this factory.</p> 379 * 380 * <p> 381 * Feature names are fully qualified {@link java.net.URI}s. 382 * Implementations may define their own features. 383 * An {@link TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the 384 * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature. 385 * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state. 386 * </p> 387 * 388 * <p>See {@link javax.xml.transform.TransformerFactory} for full documentation of specific features.</p> 389 * 390 * @param name Feature name. 391 * @param value Is feature state <code>true</code> or <code>false</code>. 392 * 393 * @throws TransformerConfigurationException if this <code>TransformerFactory</code> 394 * or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature. 395 * @throws NullPointerException If the <code>name</code> parameter is null. 396 */ 397 public void setFeature(String name, boolean value) 398 throws TransformerConfigurationException { 399 400 // feature name cannot be null 401 if (name == null) { 402 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_SET_FEATURE_NULL_NAME); 403 throw new NullPointerException(err.toString()); 404 } 405 // secure processing? 406 else if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { 407 _isSecureProcessing = value; 408 // all done processing feature 409 return; 410 } 411 else { 412 // unknown feature 413 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNSUPPORTED_FEATURE, name); 414 throw new TransformerConfigurationException(err.toString()); 415 } 416 } 417 418 /** 419 * javax.xml.transform.sax.TransformerFactory implementation. 420 * Look up the value of a feature (to see if it is supported). 421 * This method must be updated as the various methods and features of this 422 * class are implemented. 423 * 424 * @param name The feature name 425 * @return 'true' if feature is supported, 'false' if not 426 */ 427 public boolean getFeature(String name) { 428 // All supported features should be listed here 429 String[] features = { 430 DOMSource.FEATURE, 431 DOMResult.FEATURE, 432 SAXSource.FEATURE, 433 SAXResult.FEATURE, 434 StreamSource.FEATURE, 435 StreamResult.FEATURE, 436 SAXTransformerFactory.FEATURE, 437 SAXTransformerFactory.FEATURE_XMLFILTER 438 }; 439 440 // feature name cannot be null 441 if (name == null) { 442 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_GET_FEATURE_NULL_NAME); 443 throw new NullPointerException(err.toString()); 444 } 445 446 // Inefficient, but array is small 447 for (int i =0; i < features.length; i++) { 448 if (name.equals(features[i])) { 449 return true; 450 } 451 } 452 // secure processing? 453 if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { 454 return _isSecureProcessing; 455 } 456 457 // Feature not supported 458 return false; 459 } 460 461 /** 462 * javax.xml.transform.sax.TransformerFactory implementation. 463 * Get the object that is used by default during the transformation to 464 * resolve URIs used in document(), xsl:import, or xsl:include. 465 * 466 * @return The URLResolver used for this TransformerFactory and all 467 * Templates and Transformer objects created using this factory 468 */ 469 public URIResolver getURIResolver() { 470 return _uriResolver; 471 } 472 473 /** 474 * javax.xml.transform.sax.TransformerFactory implementation. 475 * Set the object that is used by default during the transformation to 476 * resolve URIs used in document(), xsl:import, or xsl:include. Note that 477 * this does not affect Templates and Transformers that are already 478 * created with this factory. 479 * 480 * @param resolver The URLResolver used for this TransformerFactory and all 481 * Templates and Transformer objects created using this factory 482 */ 483 public void setURIResolver(URIResolver resolver) { 484 _uriResolver = resolver; 485 } 486 487 /** 488 * javax.xml.transform.sax.TransformerFactory implementation. 489 * Get the stylesheet specification(s) associated via the xml-stylesheet 490 * processing instruction (see http://www.w3.org/TR/xml-stylesheet/) with 491 * the document document specified in the source parameter, and that match 492 * the given criteria. 493 * 494 * @param source The XML source document. 495 * @param media The media attribute to be matched. May be null, in which 496 * case the prefered templates will be used (i.e. alternate = no). 497 * @param title The value of the title attribute to match. May be null. 498 * @param charset The value of the charset attribute to match. May be null. 499 * @return A Source object suitable for passing to the TransformerFactory. 500 * @throws TransformerConfigurationException 501 */ 502 public Source getAssociatedStylesheet(Source source, String media, 503 String title, String charset) 504 throws TransformerConfigurationException { 505 506 String baseId; 507 XMLReader reader = null; 508 InputSource isource = null; 509 510 511 /** 512 * Fix for bugzilla bug 24187 513 */ 514 StylesheetPIHandler _stylesheetPIHandler = new StylesheetPIHandler(null,media,title,charset); 515 516 try { 517 518 if (source instanceof DOMSource ) { 519 final DOMSource domsrc = (DOMSource) source; 520 baseId = domsrc.getSystemId(); 521 final org.w3c.dom.Node node = domsrc.getNode(); 522 final DOM2SAX dom2sax = new DOM2SAX(node); 523 524 _stylesheetPIHandler.setBaseId(baseId); 525 526 dom2sax.setContentHandler( _stylesheetPIHandler); 527 dom2sax.parse(); 528 } else { 529 isource = SAXSource.sourceToInputSource(source); 530 baseId = isource.getSystemId(); 531 532 SAXParserFactory factory = SAXParserFactory.newInstance(); 533 factory.setNamespaceAware(true); 534 535 if (_isSecureProcessing) { 536 try { 537 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 538 } 539 catch (org.xml.sax.SAXException e) {} 540 } 541 542 SAXParser jaxpParser = factory.newSAXParser(); 543 544 reader = jaxpParser.getXMLReader(); 545 if (reader == null) { 546 reader = XMLReaderFactory.createXMLReader(); 547 } 548 549 _stylesheetPIHandler.setBaseId(baseId); 550 reader.setContentHandler(_stylesheetPIHandler); 551 reader.parse(isource); 552 553 } 554 555 if (_uriResolver != null ) { 556 _stylesheetPIHandler.setURIResolver(_uriResolver); 557 } 558 559 } catch (StopParseException e ) { 560 // startElement encountered so do not parse further 561 562 } catch (javax.xml.parsers.ParserConfigurationException e) { 563 564 throw new TransformerConfigurationException( 565 "getAssociatedStylesheets failed", e); 566 567 } catch (org.xml.sax.SAXException se) { 568 569 throw new TransformerConfigurationException( 570 "getAssociatedStylesheets failed", se); 571 572 573 } catch (IOException ioe ) { 574 throw new TransformerConfigurationException( 575 "getAssociatedStylesheets failed", ioe); 576 577 } 578 579 return _stylesheetPIHandler.getAssociatedStylesheet(); 580 581 } 582 583 /** 584 * javax.xml.transform.sax.TransformerFactory implementation. 585 * Create a Transformer object that copies the input document to the result. 586 * 587 * @return A Transformer object that simply copies the source to the result. 588 * @throws TransformerConfigurationException 589 */ 590 public Transformer newTransformer() 591 throws TransformerConfigurationException 592 { 593 TransformerImpl result = new TransformerImpl(new Properties(), 594 _indentNumber, this); 595 if (_uriResolver != null) { 596 result.setURIResolver(_uriResolver); 597 } 598 599 if (_isSecureProcessing) { 600 result.setSecureProcessing(true); 601 } 602 return result; 603 } 604 605 /** 606 * javax.xml.transform.sax.TransformerFactory implementation. 607 * Process the Source into a Templates object, which is a a compiled 608 * representation of the source. Note that this method should not be 609 * used with XSLTC, as the time-consuming compilation is done for each 610 * and every transformation. 611 * 612 * @return A Templates object that can be used to create Transformers. 613 * @throws TransformerConfigurationException 614 */ 615 public Transformer newTransformer(Source source) throws 616 TransformerConfigurationException 617 { 618 final Templates templates = newTemplates(source); 619 final Transformer transformer = templates.newTransformer(); 620 if (_uriResolver != null) { 621 transformer.setURIResolver(_uriResolver); 622 } 623 return(transformer); 624 } 625 626 /** 627 * Pass warning messages from the compiler to the error listener 628 */ 629 private void passWarningsToListener(Vector messages) 630 throws TransformerException 631 { 632 if (_errorListener == null || messages == null) { 633 return; 634 } 635 // Pass messages to listener, one by one 636 final int count = messages.size(); 637 for (int pos = 0; pos < count; pos++) { 638 ErrorMsg msg = (ErrorMsg)messages.elementAt(pos); 639 // Workaround for the TCK failure ErrorListener.errorTests.error001. 640 if (msg.isWarningError()) 641 _errorListener.error( 642 new TransformerConfigurationException(msg.toString())); 643 else 644 _errorListener.warning( 645 new TransformerConfigurationException(msg.toString())); 646 } 647 } 648 649 /** 650 * Pass error messages from the compiler to the error listener 651 */ 652 private void passErrorsToListener(Vector messages) { 653 try { 654 if (_errorListener == null || messages == null) { 655 return; 656 } 657 // Pass messages to listener, one by one 658 final int count = messages.size(); 659 for (int pos = 0; pos < count; pos++) { 660 String message = messages.elementAt(pos).toString(); 661 _errorListener.error(new TransformerException(message)); 662 } 663 } 664 catch (TransformerException e) { 665 // nada 666 } 667 } 668 669 /** 670 * javax.xml.transform.sax.TransformerFactory implementation. 671 * Process the Source into a Templates object, which is a a compiled 672 * representation of the source. 673 * 674 * @param source The input stylesheet - DOMSource not supported!!! 675 * @return A Templates object that can be used to create Transformers. 676 * @throws TransformerConfigurationException 677 */ 678 public Templates newTemplates(Source source) 679 throws TransformerConfigurationException 680 { 681 // If the _useClasspath attribute is true, try to load the translet from 682 // the CLASSPATH and create a template object using the loaded 683 // translet. 684 if (_useClasspath) { 685 String transletName = getTransletBaseName(source); 686 687 if (_packageName != null) 688 transletName = _packageName + "." + transletName; 689 690 try { 691 final Class clazz = ObjectFactory.findProviderClass( 692 transletName, ObjectFactory.findClassLoader(), true); 693 resetTransientAttributes(); 694 695 return new TemplatesImpl(new Class[]{clazz}, transletName, null, _indentNumber, this); 696 } 697 catch (ClassNotFoundException cnfe) { 698 ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, transletName); 699 throw new TransformerConfigurationException(err.toString()); 700 } 701 catch (Exception e) { 702 ErrorMsg err = new ErrorMsg( 703 new ErrorMsg(ErrorMsg.RUNTIME_ERROR_KEY) 704 + e.getMessage()); 705 throw new TransformerConfigurationException(err.toString()); 706 } 707 } 708 709 // If _autoTranslet is true, we will try to load the bytecodes 710 // from the translet classes without compiling the stylesheet. 711 if (_autoTranslet) { 712 byte[][] bytecodes = null; 713 String transletClassName = getTransletBaseName(source); 714 715 if (_packageName != null) 716 transletClassName = _packageName + "." + transletClassName; 717 718 if (_jarFileName != null) 719 bytecodes = getBytecodesFromJar(source, transletClassName); 720 else 721 bytecodes = getBytecodesFromClasses(source, transletClassName); 722 723 if (bytecodes != null) { 724 if (_debug) { 725 if (_jarFileName != null) 726 System.err.println(new ErrorMsg( 727 ErrorMsg.TRANSFORM_WITH_JAR_STR, transletClassName, _jarFileName)); 728 else 729 System.err.println(new ErrorMsg( 730 ErrorMsg.TRANSFORM_WITH_TRANSLET_STR, transletClassName)); 731 } 732 733 // Reset the per-session attributes to their default values 734 // after each newTemplates() call. 735 resetTransientAttributes(); 736 737 return new TemplatesImpl(bytecodes, transletClassName, null, _indentNumber, this); 738 } 739 } 740 741 // Create and initialize a stylesheet compiler 742 final XSLTC xsltc = new XSLTC(); 743 if (_debug) xsltc.setDebug(true); 744 if (_enableInlining) 745 xsltc.setTemplateInlining(true); 746 else 747 xsltc.setTemplateInlining(false); 748 if (_isSecureProcessing) xsltc.setSecureProcessing(true); 749 xsltc.init(); 750 751 // Set a document loader (for xsl:include/import) if defined 752 if (_uriResolver != null) { 753 xsltc.setSourceLoader(this); 754 } 755 756 // Pass parameters to the Parser to make sure it locates the correct 757 // <?xml-stylesheet ...?> PI in an XML input document 758 if ((_piParams != null) && (_piParams.get(source) != null)) { 759 // Get the parameters for this Source object 760 PIParamWrapper p = (PIParamWrapper)_piParams.get(source); 761 // Pass them on to the compiler (which will pass then to the parser) 762 if (p != null) { 763 xsltc.setPIParameters(p._media, p._title, p._charset); 764 } 765 } 766 767 // Set the attributes for translet generation 768 int outputType = XSLTC.BYTEARRAY_OUTPUT; 769 if (_generateTranslet || _autoTranslet) { 770 // Set the translet name 771 xsltc.setClassName(getTransletBaseName(source)); 772 773 if (_destinationDirectory != null) 774 xsltc.setDestDirectory(_destinationDirectory); 775 else { 776 String xslName = getStylesheetFileName(source); 777 if (xslName != null) { 778 File xslFile = new File(xslName); 779 String xslDir = xslFile.getParent(); 780 781 if (xslDir != null) 782 xsltc.setDestDirectory(xslDir); 783 } 784 } 785 786 if (_packageName != null) 787 xsltc.setPackageName(_packageName); 788 789 if (_jarFileName != null) { 790 xsltc.setJarFileName(_jarFileName); 791 outputType = XSLTC.BYTEARRAY_AND_JAR_OUTPUT; 792 } 793 else 794 outputType = XSLTC.BYTEARRAY_AND_FILE_OUTPUT; 795 } 796 797 // Compile the stylesheet 798 final InputSource input = Util.getInputSource(xsltc, source); 799 byte[][] bytecodes = xsltc.compile(null, input, outputType); 800 final String transletName = xsltc.getClassName(); 801 802 // Output to the jar file if the jar file name is set. 803 if ((_generateTranslet || _autoTranslet) 804 && bytecodes != null && _jarFileName != null) { 805 try { 806 xsltc.outputToJar(); 807 } 808 catch (java.io.IOException e) { } 809 } 810 811 // Reset the per-session attributes to their default values 812 // after each newTemplates() call. 813 resetTransientAttributes(); 814 815 // Pass compiler warnings to the error listener 816 if (_errorListener != this) { 817 try { 818 passWarningsToListener(xsltc.getWarnings()); 819 } 820 catch (TransformerException e) { 821 throw new TransformerConfigurationException(e); 822 } 823 } 824 else { 825 xsltc.printWarnings(); 826 } 827 828 // Check that the transformation went well before returning 829 if (bytecodes == null) { 830 831 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR); 832 TransformerConfigurationException exc = new TransformerConfigurationException(err.toString()); 833 834 // Pass compiler errors to the error listener 835 if (_errorListener != null) { 836 passErrorsToListener(xsltc.getErrors()); 837 838 // As required by TCK 1.2, send a fatalError to the 839 // error listener because compilation of the stylesheet 840 // failed and no further processing will be possible. 841 try { 842 _errorListener.fatalError(exc); 843 } catch (TransformerException te) { 844 // well, we tried. 845 } 846 } 847 else { 848 xsltc.printErrors(); 849 } 850 throw exc; 851 } 852 853 return new TemplatesImpl(bytecodes, transletName, 854 xsltc.getOutputProperties(), _indentNumber, this); 855 } 856 857 /** 858 * javax.xml.transform.sax.SAXTransformerFactory implementation. 859 * Get a TemplatesHandler object that can process SAX ContentHandler 860 * events into a Templates object. 861 * 862 * @return A TemplatesHandler object that can handle SAX events 863 * @throws TransformerConfigurationException 864 */ 865 public TemplatesHandler newTemplatesHandler() 866 throws TransformerConfigurationException 867 { 868 final TemplatesHandlerImpl handler = 869 new TemplatesHandlerImpl(_indentNumber, this); 870 if (_uriResolver != null) { 871 handler.setURIResolver(_uriResolver); 872 } 873 return handler; 874 } 875 876 /** 877 * javax.xml.transform.sax.SAXTransformerFactory implementation. 878 * Get a TransformerHandler object that can process SAX ContentHandler 879 * events into a Result. This method will return a pure copy transformer. 880 * 881 * @return A TransformerHandler object that can handle SAX events 882 * @throws TransformerConfigurationException 883 */ 884 public TransformerHandler newTransformerHandler() 885 throws TransformerConfigurationException 886 { 887 final Transformer transformer = newTransformer(); 888 if (_uriResolver != null) { 889 transformer.setURIResolver(_uriResolver); 890 } 891 return new TransformerHandlerImpl((TransformerImpl) transformer); 892 } 893 894 /** 895 * javax.xml.transform.sax.SAXTransformerFactory implementation. 896 * Get a TransformerHandler object that can process SAX ContentHandler 897 * events into a Result, based on the transformation instructions 898 * specified by the argument. 899 * 900 * @param src The source of the transformation instructions. 901 * @return A TransformerHandler object that can handle SAX events 902 * @throws TransformerConfigurationException 903 */ 904 public TransformerHandler newTransformerHandler(Source src) 905 throws TransformerConfigurationException 906 { 907 final Transformer transformer = newTransformer(src); 908 if (_uriResolver != null) { 909 transformer.setURIResolver(_uriResolver); 910 } 911 return new TransformerHandlerImpl((TransformerImpl) transformer); 912 } 913 914 /** 915 * javax.xml.transform.sax.SAXTransformerFactory implementation. 916 * Get a TransformerHandler object that can process SAX ContentHandler 917 * events into a Result, based on the transformation instructions 918 * specified by the argument. 919 * 920 * @param templates Represents a pre-processed stylesheet 921 * @return A TransformerHandler object that can handle SAX events 922 * @throws TransformerConfigurationException 923 */ 924 public TransformerHandler newTransformerHandler(Templates templates) 925 throws TransformerConfigurationException 926 { 927 final Transformer transformer = templates.newTransformer(); 928 final TransformerImpl internal = (TransformerImpl)transformer; 929 return new TransformerHandlerImpl(internal); 930 } 931 932 /** 933 * javax.xml.transform.sax.SAXTransformerFactory implementation. 934 * Create an XMLFilter that uses the given source as the 935 * transformation instructions. 936 * 937 * @param src The source of the transformation instructions. 938 * @return An XMLFilter object, or null if this feature is not supported. 939 * @throws TransformerConfigurationException 940 */ 941 public XMLFilter newXMLFilter(Source src) 942 throws TransformerConfigurationException 943 { 944 Templates templates = newTemplates(src); 945 if (templates == null) return null; 946 return newXMLFilter(templates); 947 } 948 949 /** 950 * javax.xml.transform.sax.SAXTransformerFactory implementation. 951 * Create an XMLFilter that uses the given source as the 952 * transformation instructions. 953 * 954 * @param templates The source of the transformation instructions. 955 * @return An XMLFilter object, or null if this feature is not supported. 956 * @throws TransformerConfigurationException 957 */ 958 public XMLFilter newXMLFilter(Templates templates) 959 throws TransformerConfigurationException 960 { 961 try { 962 return new org.apache.xalan.xsltc.trax.TrAXFilter(templates); 963 } 964 catch (TransformerConfigurationException e1) { 965 if (_errorListener != null) { 966 try { 967 _errorListener.fatalError(e1); 968 return null; 969 } 970 catch (TransformerException e2) { 971 new TransformerConfigurationException(e2); 972 } 973 } 974 throw e1; 975 } 976 } 977 978 /** 979 * Receive notification of a recoverable error. 980 * The transformer must continue to provide normal parsing events after 981 * invoking this method. It should still be possible for the application 982 * to process the document through to the end. 983 * 984 * @param e The warning information encapsulated in a transformer 985 * exception. 986 * @throws TransformerException if the application chooses to discontinue 987 * the transformation (always does in our case). 988 */ 989 public void error(TransformerException e) 990 throws TransformerException 991 { 992 Throwable wrapped = e.getException(); 993 if (wrapped != null) { 994 System.err.println(new ErrorMsg(ErrorMsg.ERROR_PLUS_WRAPPED_MSG, 995 e.getMessageAndLocation(), 996 wrapped.getMessage())); 997 } else { 998 System.err.println(new ErrorMsg(ErrorMsg.ERROR_MSG, 999 e.getMessageAndLocation())); 1000 } 1001 throw e; 1002 } 1003 1004 /** 1005 * Receive notification of a non-recoverable error. 1006 * The application must assume that the transformation cannot continue 1007 * after the Transformer has invoked this method, and should continue 1008 * (if at all) only to collect addition error messages. In fact, 1009 * Transformers are free to stop reporting events once this method has 1010 * been invoked. 1011 * 1012 * @param e warning information encapsulated in a transformer 1013 * exception. 1014 * @throws TransformerException if the application chooses to discontinue 1015 * the transformation (always does in our case). 1016 */ 1017 public void fatalError(TransformerException e) 1018 throws TransformerException 1019 { 1020 Throwable wrapped = e.getException(); 1021 if (wrapped != null) { 1022 System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_PLUS_WRAPPED_MSG, 1023 e.getMessageAndLocation(), 1024 wrapped.getMessage())); 1025 } else { 1026 System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_MSG, 1027 e.getMessageAndLocation())); 1028 } 1029 throw e; 1030 } 1031 1032 /** 1033 * Receive notification of a warning. 1034 * Transformers can use this method to report conditions that are not 1035 * errors or fatal errors. The default behaviour is to take no action. 1036 * After invoking this method, the Transformer must continue with the 1037 * transformation. It should still be possible for the application to 1038 * process the document through to the end. 1039 * 1040 * @param e The warning information encapsulated in a transformer 1041 * exception. 1042 * @throws TransformerException if the application chooses to discontinue 1043 * the transformation (never does in our case). 1044 */ 1045 public void warning(TransformerException e) 1046 throws TransformerException 1047 { 1048 Throwable wrapped = e.getException(); 1049 if (wrapped != null) { 1050 System.err.println(new ErrorMsg(ErrorMsg.WARNING_PLUS_WRAPPED_MSG, 1051 e.getMessageAndLocation(), 1052 wrapped.getMessage())); 1053 } else { 1054 System.err.println(new ErrorMsg(ErrorMsg.WARNING_MSG, 1055 e.getMessageAndLocation())); 1056 } 1057 } 1058 1059 /** 1060 * This method implements XSLTC's SourceLoader interface. It is used to 1061 * glue a TrAX URIResolver to the XSLTC compiler's Input and Import classes. 1062 * 1063 * @param href The URI of the document to load 1064 * @param context The URI of the currently loaded document 1065 * @param xsltc The compiler that resuests the document 1066 * @return An InputSource with the loaded document 1067 */ 1068 public InputSource loadSource(String href, String context, XSLTC xsltc) { 1069 try { 1070 if (_uriResolver != null) { 1071 final Source source = _uriResolver.resolve(href, context); 1072 if (source != null) { 1073 return Util.getInputSource(xsltc, source); 1074 } 1075 } 1076 } 1077 catch (TransformerException e) { 1078 // Falls through 1079 } 1080 return null; 1081 } 1082 1083 /** 1084 * Reset the per-session attributes to their default values 1085 */ 1086 private void resetTransientAttributes() { 1087 _transletName = DEFAULT_TRANSLET_NAME; 1088 _destinationDirectory = null; 1089 _packageName = null; 1090 _jarFileName = null; 1091 } 1092 1093 /** 1094 * Load the translet classes from local .class files and return 1095 * the bytecode array. 1096 * 1097 * @param source The xsl source 1098 * @param fullClassName The full name of the translet 1099 * @return The bytecode array 1100 */ 1101 private byte[][] getBytecodesFromClasses(Source source, String fullClassName) 1102 { 1103 if (fullClassName == null) 1104 return null; 1105 1106 String xslFileName = getStylesheetFileName(source); 1107 File xslFile = null; 1108 if (xslFileName != null) 1109 xslFile = new File(xslFileName); 1110 1111 // Find the base name of the translet 1112 final String transletName; 1113 int lastDotIndex = fullClassName.lastIndexOf('.'); 1114 if (lastDotIndex > 0) 1115 transletName = fullClassName.substring(lastDotIndex+1); 1116 else 1117 transletName = fullClassName; 1118 1119 // Construct the path name for the translet class file 1120 String transletPath = fullClassName.replace('.', '/'); 1121 if (_destinationDirectory != null) { 1122 transletPath = _destinationDirectory + "/" + transletPath + ".class"; 1123 } 1124 else { 1125 if (xslFile != null && xslFile.getParent() != null) 1126 transletPath = xslFile.getParent() + "/" + transletPath + ".class"; 1127 else 1128 transletPath = transletPath + ".class"; 1129 } 1130 1131 // Return null if the translet class file does not exist. 1132 File transletFile = new File(transletPath); 1133 if (!transletFile.exists()) 1134 return null; 1135 1136 // Compare the timestamps of the translet and the xsl file. 1137 // If the translet is older than the xsl file, return null 1138 // so that the xsl file is used for the transformation and 1139 // the translet is regenerated. 1140 if (xslFile != null && xslFile.exists()) { 1141 long xslTimestamp = xslFile.lastModified(); 1142 long transletTimestamp = transletFile.lastModified(); 1143 if (transletTimestamp < xslTimestamp) 1144 return null; 1145 } 1146 1147 // Load the translet into a bytecode array. 1148 List bytecodes = new ArrayList(); 1149 int fileLength = (int)transletFile.length(); 1150 if (fileLength > 0) { 1151 FileInputStream input = null; 1152 try { 1153 input = new FileInputStream(transletFile); 1154 } 1155 catch (FileNotFoundException e) { 1156 return null; 1157 } 1158 1159 byte[] bytes = new byte[fileLength]; 1160 try { 1161 readFromInputStream(bytes, input, fileLength); 1162 input.close(); 1163 } 1164 catch (IOException e) { 1165 return null; 1166 } 1167 1168 bytecodes.add(bytes); 1169 } 1170 else 1171 return null; 1172 1173 // Find the parent directory of the translet. 1174 String transletParentDir = transletFile.getParent(); 1175 if (transletParentDir == null) 1176 transletParentDir = System.getProperty("user.dir"); 1177 1178 File transletParentFile = new File(transletParentDir); 1179 1180 // Find all the auxiliary files which have a name pattern of "transletClass$nnn.class". 1181 final String transletAuxPrefix = transletName + "$"; 1182 File[] auxfiles = transletParentFile.listFiles(new FilenameFilter() { 1183 public boolean accept(File dir, String name) 1184 { 1185 return (name.endsWith(".class") && name.startsWith(transletAuxPrefix)); 1186 } 1187 }); 1188 1189 // Load the auxiliary class files and add them to the bytecode array. 1190 for (int i = 0; i < auxfiles.length; i++) 1191 { 1192 File auxfile = auxfiles[i]; 1193 int auxlength = (int)auxfile.length(); 1194 if (auxlength > 0) { 1195 FileInputStream auxinput = null; 1196 try { 1197 auxinput = new FileInputStream(auxfile); 1198 } 1199 catch (FileNotFoundException e) { 1200 continue; 1201 } 1202 1203 byte[] bytes = new byte[auxlength]; 1204 1205 try { 1206 readFromInputStream(bytes, auxinput, auxlength); 1207 auxinput.close(); 1208 } 1209 catch (IOException e) { 1210 continue; 1211 } 1212 1213 bytecodes.add(bytes); 1214 } 1215 } 1216 1217 // Convert the Vector of byte[] to byte[][]. 1218 final int count = bytecodes.size(); 1219 if ( count > 0) { 1220 final byte[][] result = new byte[count][1]; 1221 for (int i = 0; i < count; i++) { 1222 result[i] = (byte[])bytecodes.get(i); 1223 } 1224 1225 return result; 1226 } 1227 else 1228 return null; 1229 } 1230 1231 /** 1232 * Load the translet classes from the jar file and return the bytecode. 1233 * 1234 * @param source The xsl source 1235 * @param fullClassName The full name of the translet 1236 * @return The bytecode array 1237 */ 1238 private byte[][] getBytecodesFromJar(Source source, String fullClassName) 1239 { 1240 String xslFileName = getStylesheetFileName(source); 1241 File xslFile = null; 1242 if (xslFileName != null) 1243 xslFile = new File(xslFileName); 1244 1245 // Construct the path for the jar file 1246 String jarPath = null; 1247 if (_destinationDirectory != null) 1248 jarPath = _destinationDirectory + "/" + _jarFileName; 1249 else { 1250 if (xslFile != null && xslFile.getParent() != null) 1251 jarPath = xslFile.getParent() + "/" + _jarFileName; 1252 else 1253 jarPath = _jarFileName; 1254 } 1255 1256 // Return null if the jar file does not exist. 1257 File file = new File(jarPath); 1258 if (!file.exists()) 1259 return null; 1260 1261 // Compare the timestamps of the jar file and the xsl file. Return null 1262 // if the xsl file is newer than the jar file. 1263 if (xslFile != null && xslFile.exists()) { 1264 long xslTimestamp = xslFile.lastModified(); 1265 long transletTimestamp = file.lastModified(); 1266 if (transletTimestamp < xslTimestamp) 1267 return null; 1268 } 1269 1270 // Create a ZipFile object for the jar file 1271 ZipFile jarFile = null; 1272 try { 1273 jarFile = new ZipFile(file); 1274 } 1275 catch (IOException e) { 1276 return null; 1277 } 1278 1279 String transletPath = fullClassName.replace('.', '/'); 1280 String transletAuxPrefix = transletPath + "$"; 1281 String transletFullName = transletPath + ".class"; 1282 1283 List bytecodes = new ArrayList(); 1284 1285 // Iterate through all entries in the jar file to find the 1286 // translet and auxiliary classes. 1287 Enumeration entries = jarFile.entries(); 1288 while (entries.hasMoreElements()) 1289 { 1290 ZipEntry entry = (ZipEntry)entries.nextElement(); 1291 String entryName = entry.getName(); 1292 if (entry.getSize() > 0 && 1293 (entryName.equals(transletFullName) || 1294 (entryName.endsWith(".class") && 1295 entryName.startsWith(transletAuxPrefix)))) 1296 { 1297 try { 1298 InputStream input = jarFile.getInputStream(entry); 1299 int size = (int)entry.getSize(); 1300 byte[] bytes = new byte[size]; 1301 readFromInputStream(bytes, input, size); 1302 input.close(); 1303 bytecodes.add(bytes); 1304 } 1305 catch (IOException e) { 1306 return null; 1307 } 1308 } 1309 } 1310 1311 // Convert the Vector of byte[] to byte[][]. 1312 final int count = bytecodes.size(); 1313 if (count > 0) { 1314 final byte[][] result = new byte[count][1]; 1315 for (int i = 0; i < count; i++) { 1316 result[i] = (byte[])bytecodes.get(i); 1317 } 1318 1319 return result; 1320 } 1321 else 1322 return null; 1323 } 1324 1325 /** 1326 * Read a given number of bytes from the InputStream into a byte array. 1327 * 1328 * @param bytes The byte array to store the input content. 1329 * @param input The input stream. 1330 * @param size The number of bytes to read. 1331 */ 1332 private void readFromInputStream(byte[] bytes, InputStream input, int size) 1333 throws IOException 1334 { 1335 int n = 0; 1336 int offset = 0; 1337 int length = size; 1338 while (length > 0 && (n = input.read(bytes, offset, length)) > 0) { 1339 offset = offset + n; 1340 length = length - n; 1341 } 1342 } 1343 1344 /** 1345 * Return the base class name of the translet. 1346 * The translet name is resolved using the following rules: 1347 * 1. if the _transletName attribute is set and its value is not "GregorSamsa", 1348 * then _transletName is returned. 1349 * 2. otherwise get the translet name from the base name of the system ID 1350 * 3. return "GregorSamsa" if the result from step 2 is null. 1351 * 1352 * @param source The input Source 1353 * @return The name of the translet class 1354 */ 1355 private String getTransletBaseName(Source source) 1356 { 1357 String transletBaseName = null; 1358 if (!_transletName.equals(DEFAULT_TRANSLET_NAME)) 1359 return _transletName; 1360 else { 1361 String systemId = source.getSystemId(); 1362 if (systemId != null) { 1363 String baseName = Util.baseName(systemId); 1364 if (baseName != null) { 1365 baseName = Util.noExtName(baseName); 1366 transletBaseName = Util.toJavaName(baseName); 1367 } 1368 } 1369 } 1370 1371 return (transletBaseName != null) ? transletBaseName : DEFAULT_TRANSLET_NAME; 1372 } 1373 1374 /** 1375 * Return the local file name from the systemId of the Source object 1376 * 1377 * @param source The Source 1378 * @return The file name in the local filesystem, or null if the 1379 * systemId does not represent a local file. 1380 */ 1381 private String getStylesheetFileName(Source source) 1382 { 1383 String systemId = source.getSystemId(); 1384 if (systemId != null) { 1385 File file = new File(systemId); 1386 if (file.exists()) 1387 return systemId; 1388 else { 1389 URL url = null; 1390 try { 1391 url = new URL(systemId); 1392 } 1393 catch (MalformedURLException e) { 1394 return null; 1395 } 1396 1397 if ("file".equals(url.getProtocol())) 1398 return url.getFile(); 1399 else 1400 return null; 1401 } 1402 } 1403 else 1404 return null; 1405 } 1406 1407 /** 1408 * Returns the Class object the provides the XSLTC DTM Manager service. 1409 */ 1410 protected Class getDTMManagerClass() { 1411 return m_DTMManagerClass; 1412 } 1413 }