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: TemplatesHandlerImpl.java 577935 2007-09-20 21:35:20Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.trax;
023    
024    import javax.xml.XMLConstants;
025    import javax.xml.transform.Source;
026    import javax.xml.transform.Templates;
027    import javax.xml.transform.TransformerException;
028    import javax.xml.transform.URIResolver;
029    import javax.xml.transform.sax.TemplatesHandler;
030    
031    import org.apache.xalan.xsltc.compiler.CompilerException;
032    import org.apache.xalan.xsltc.compiler.Parser;
033    import org.apache.xalan.xsltc.compiler.SourceLoader;
034    import org.apache.xalan.xsltc.compiler.Stylesheet;
035    import org.apache.xalan.xsltc.compiler.SyntaxTreeNode;
036    import org.apache.xalan.xsltc.compiler.XSLTC;
037    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
038    
039    import org.xml.sax.ContentHandler;
040    import org.xml.sax.InputSource;
041    import org.xml.sax.Locator;
042    import org.xml.sax.SAXException;
043    import org.xml.sax.Attributes;
044    
045    import java.util.Vector;
046    
047    /**
048     * Implementation of a JAXP1.1 TemplatesHandler
049     * @author Morten Jorgensen
050     * @author Santiago Pericas-Geertsen
051     */
052    public class TemplatesHandlerImpl
053        implements ContentHandler, TemplatesHandler, SourceLoader
054    {
055        /**
056         * System ID for this stylesheet.
057         */
058        private String _systemId;
059    
060        /**
061         * Number of spaces to add for output indentation.
062         */
063        private int _indentNumber;
064    
065        /**
066         * This URIResolver is passed to all Transformers.
067         */
068        private URIResolver _uriResolver = null;
069    
070        /**
071         * A reference to the transformer factory that this templates
072         * object belongs to.
073         */
074        private TransformerFactoryImpl _tfactory = null;
075    
076        /**
077         * A reference to XSLTC's parser object.
078         */
079        private Parser _parser = null;
080    
081        /**
082         * The created Templates object.
083         */
084        private TemplatesImpl _templates = null;
085    
086        /**
087         * Default constructor
088         */
089        protected TemplatesHandlerImpl(int indentNumber,
090            TransformerFactoryImpl tfactory)
091        {
092            _indentNumber = indentNumber;
093            _tfactory = tfactory;
094    
095            // Instantiate XSLTC and get reference to parser object
096            XSLTC xsltc = new XSLTC();
097            if (tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING))
098                xsltc.setSecureProcessing(true);
099    
100            if ("true".equals(tfactory.getAttribute(TransformerFactoryImpl.ENABLE_INLINING)))
101                xsltc.setTemplateInlining(true);
102            else
103                xsltc.setTemplateInlining(false);
104    
105            _parser = xsltc.getParser();
106        }
107    
108        /**
109         * Implements javax.xml.transform.sax.TemplatesHandler.getSystemId()
110         * Get the base ID (URI or system ID) from where relative URLs will be
111         * resolved.
112         * @return The systemID that was set with setSystemId(String id)
113         */
114        public String getSystemId() {
115            return _systemId;
116        }
117    
118        /**
119         * Implements javax.xml.transform.sax.TemplatesHandler.setSystemId()
120         * Get the base ID (URI or system ID) from where relative URLs will be
121         * resolved.
122         * @param id Base URI for this stylesheet
123         */
124        public void setSystemId(String id) {
125            _systemId = id;
126        }
127    
128        /**
129         * Store URIResolver needed for Transformers.
130         */
131        public void setURIResolver(URIResolver resolver) {
132            _uriResolver = resolver;
133        }
134    
135        /**
136         * Implements javax.xml.transform.sax.TemplatesHandler.getTemplates()
137         * When a TemplatesHandler object is used as a ContentHandler or
138         * DocumentHandler for the parsing of transformation instructions, it
139         * creates a Templates object, which the caller can get once the SAX
140         * events have been completed.
141         * @return The Templates object that was created during the SAX event
142         *         process, or null if no Templates object has been created.
143         */
144        public Templates getTemplates() {
145            return _templates;
146        }
147    
148        /**
149         * This method implements XSLTC's SourceLoader interface. It is used to
150         * glue a TrAX URIResolver to the XSLTC compiler's Input and Import classes.
151         *
152         * @param href The URI of the document to load
153         * @param context The URI of the currently loaded document
154         * @param xsltc The compiler that resuests the document
155         * @return An InputSource with the loaded document
156         */
157        public InputSource loadSource(String href, String context, XSLTC xsltc) {
158            try {
159                // A _uriResolver must be set if this method is called
160                final Source source = _uriResolver.resolve(href, context);
161                if (source != null) {
162                    return Util.getInputSource(xsltc, source);
163                }
164            }
165            catch (TransformerException e) {
166                // Falls through
167            }
168            return null;
169        }
170    
171        // -- ContentHandler --------------------------------------------------
172    
173        /**
174         * Re-initialize parser and forward SAX2 event.
175         */
176        public void startDocument() {
177            XSLTC xsltc = _parser.getXSLTC();
178            xsltc.init();   // calls _parser.init()
179            xsltc.setOutputType(XSLTC.BYTEARRAY_OUTPUT);
180            _parser.startDocument();
181        }
182    
183        /**
184         * Just forward SAX2 event to parser object.
185         */
186        public void endDocument() throws SAXException {
187            _parser.endDocument();
188    
189            // create the templates
190            try {
191                XSLTC xsltc = _parser.getXSLTC();
192    
193                // Set the translet class name if not already set
194                String transletName;
195                if (_systemId != null) {
196                    transletName = Util.baseName(_systemId);
197                }
198                else {
199                    transletName = (String)_tfactory.getAttribute("translet-name");
200                }
201                xsltc.setClassName(transletName);
202    
203                // Get java-legal class name from XSLTC module
204                transletName = xsltc.getClassName();
205    
206                Stylesheet stylesheet = null;
207                SyntaxTreeNode root = _parser.getDocumentRoot();
208    
209                // Compile the translet - this is where the work is done!
210                if (!_parser.errorsFound() && root != null) {
211                    // Create a Stylesheet element from the root node
212                    stylesheet = _parser.makeStylesheet(root);
213                    stylesheet.setSystemId(_systemId);
214                    stylesheet.setParentStylesheet(null);
215    
216                    if (xsltc.getTemplateInlining())
217                       stylesheet.setTemplateInlining(true);
218                    else
219                       stylesheet.setTemplateInlining(false);
220    
221                    // Set a document loader (for xsl:include/import) if defined
222                    if (_uriResolver != null) {
223                        stylesheet.setSourceLoader(this);
224                    }
225    
226                    _parser.setCurrentStylesheet(stylesheet);
227    
228                    // Set it as top-level in the XSLTC object
229                    xsltc.setStylesheet(stylesheet);
230    
231                    // Create AST under the Stylesheet element
232                    _parser.createAST(stylesheet);
233                }
234    
235                // Generate the bytecodes and output the translet class(es)
236                if (!_parser.errorsFound() && stylesheet != null) {
237                    stylesheet.setMultiDocument(xsltc.isMultiDocument());
238                    stylesheet.setHasIdCall(xsltc.hasIdCall());
239    
240                    // Class synchronization is needed for BCEL
241                    synchronized (xsltc.getClass()) {
242                        stylesheet.translate();
243                    }
244                }
245    
246                if (!_parser.errorsFound()) {
247                    // Check that the transformation went well before returning
248                    final byte[][] bytecodes = xsltc.getBytecodes();
249                    if (bytecodes != null) {
250                        _templates =
251                        new TemplatesImpl(xsltc.getBytecodes(), transletName,
252                            _parser.getOutputProperties(), _indentNumber, _tfactory);
253    
254                        // Set URIResolver on templates object
255                        if (_uriResolver != null) {
256                            _templates.setURIResolver(_uriResolver);
257                        }
258                    }
259                }
260                else {
261                    StringBuffer errorMessage = new StringBuffer();
262                    Vector errors = _parser.getErrors();
263                    final int count = errors.size();
264                    for (int i = 0; i < count; i++) {
265                        if (errorMessage.length() > 0)
266                            errorMessage.append('\n');
267                        errorMessage.append(errors.elementAt(i).toString());
268                    }
269                    throw new SAXException(ErrorMsg.JAXP_COMPILE_ERR, new TransformerException(errorMessage.toString()));
270                }
271            }
272            catch (CompilerException e) {
273                throw new SAXException(ErrorMsg.JAXP_COMPILE_ERR, e);
274            }
275        }
276    
277        /**
278         * Just forward SAX2 event to parser object.
279         */
280        public void startPrefixMapping(String prefix, String uri) {
281            _parser.startPrefixMapping(prefix, uri);
282        }
283    
284        /**
285         * Just forward SAX2 event to parser object.
286         */
287        public void endPrefixMapping(String prefix) {
288            _parser.endPrefixMapping(prefix);
289        }
290    
291        /**
292         * Just forward SAX2 event to parser object.
293         */
294        public void startElement(String uri, String localname, String qname,
295            Attributes attributes) throws SAXException
296        {
297            _parser.startElement(uri, localname, qname, attributes);
298        }
299    
300        /**
301         * Just forward SAX2 event to parser object.
302         */
303        public void endElement(String uri, String localname, String qname) {
304            _parser.endElement(uri, localname, qname);
305        }
306    
307        /**
308         * Just forward SAX2 event to parser object.
309         */
310        public void characters(char[] ch, int start, int length) {
311            _parser.characters(ch, start, length);
312        }
313    
314        /**
315         * Just forward SAX2 event to parser object.
316         */
317        public void processingInstruction(String name, String value) {
318            _parser.processingInstruction(name, value);
319        }
320    
321        /**
322         * Just forward SAX2 event to parser object.
323         */
324        public void ignorableWhitespace(char[] ch, int start, int length) {
325            _parser.ignorableWhitespace(ch, start, length);
326        }
327    
328        /**
329         * Just forward SAX2 event to parser object.
330         */
331        public void skippedEntity(String name) {
332            _parser.skippedEntity(name);
333        }
334    
335        /**
336         * Set internal system Id and forward SAX2 event to parser object.
337         */
338        public void setDocumentLocator(Locator locator) {
339            setSystemId(locator.getSystemId());
340            _parser.setDocumentLocator(locator);
341        }
342    }
343    
344