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