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    }