1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: ProcessorInclude.java 469349 2006-10-31 03:06:50Z minchau $
20  */
21 package org.apache.xalan.processor;
22 
23 import java.io.IOException;
24 
25 import javax.xml.XMLConstants;
26 import javax.xml.transform.Source;
27 import javax.xml.transform.TransformerException;
28 import javax.xml.transform.URIResolver;
29 import javax.xml.transform.dom.DOMSource;
30 import javax.xml.transform.sax.SAXSource;
31 import javax.xml.transform.stream.StreamSource;
32 
33 import org.apache.xalan.res.XSLMessages;
34 import org.apache.xalan.res.XSLTErrorResources;
35 import org.apache.xml.utils.SystemIDResolver;
36 import org.apache.xml.utils.TreeWalker;
37 
38 import org.w3c.dom.Node;
39 
40 import org.xml.sax.Attributes;
41 import org.xml.sax.InputSource;
42 import org.xml.sax.XMLReader;
43 import org.xml.sax.helpers.XMLReaderFactory;
44 
45 /**
46  * TransformerFactory class for xsl:include markup.
47  * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
48  * @see <a href="http://www.w3.org/TR/xslt#include">include in XSLT Specification</a>
49  *
50  * @xsl.usage internal
51  */
52 public class ProcessorInclude extends XSLTElementProcessor
53 {
54     static final long serialVersionUID = -4570078731972673481L;
55 
56   /**
57    * The base URL of the XSL document.
58    * @serial
59    */
60   private String m_href = null;
61 
62   /**
63    * Get the base identifier with which this stylesheet is associated.
64    *
65    * @return non-null reference to the href attribute string, or
66    *         null if setHref has not been called.
67    */
getHref()68   public String getHref()
69   {
70     return m_href;
71   }
72 
73   /**
74    * Get the base identifier with which this stylesheet is associated.
75    *
76    * @param baseIdent Should be a non-null reference to a valid URL string.
77    */
setHref(String baseIdent)78   public void setHref(String baseIdent)
79   {
80     // Validate?
81     m_href = baseIdent;
82   }
83 
84   /**
85    * Get the stylesheet type associated with an included stylesheet
86    *
87    * @return the type of the stylesheet
88    */
getStylesheetType()89   protected int getStylesheetType()
90   {
91     return StylesheetHandler.STYPE_INCLUDE;
92   }
93 
94   /**
95    * Get the error number associated with this type of stylesheet including itself
96    *
97    * @return the appropriate error number
98    */
getStylesheetInclErr()99   protected String getStylesheetInclErr()
100   {
101     return XSLTErrorResources.ER_STYLESHEET_INCLUDES_ITSELF;
102   }
103 
104   /**
105    * Receive notification of the start of an xsl:include element.
106    *
107    * @param handler The calling StylesheetHandler/TemplatesBuilder.
108    * @param uri The Namespace URI, or the empty string if the
109    *        element has no Namespace URI or if Namespace
110    *        processing is not being performed.
111    * @param localName The local name (without prefix), or the
112    *        empty string if Namespace processing is not being
113    *        performed.
114    * @param rawName The raw XML 1.0 name (with prefix), or the
115    *        empty string if raw names are not available.
116    * @param attributes The attributes attached to the element.  If
117    *        there are no attributes, it shall be an empty
118    *        Attributes object.
119    *
120    * @throws org.xml.sax.SAXException Any SAX exception, possibly
121    *            wrapping another exception.
122    */
startElement( StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)123   public void startElement(
124           StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
125             throws org.xml.sax.SAXException
126   {
127 
128 
129     setPropertiesFromAttributes(handler, rawName, attributes, this);
130 
131     try
132     {
133 
134       // Get the Source from the user's URIResolver (if any).
135       Source sourceFromURIResolver = getSourceFromUriResolver(handler);
136       // Get the system ID of the included/imported stylesheet module
137       String hrefUrl = getBaseURIOfIncludedStylesheet(handler, sourceFromURIResolver);
138 
139       if (handler.importStackContains(hrefUrl))
140       {
141         throw new org.xml.sax.SAXException(
142           XSLMessages.createMessage(
143           getStylesheetInclErr(), new Object[]{ hrefUrl }));  //"(StylesheetHandler) "+hrefUrl+" is directly or indirectly importing itself!");
144       }
145 
146       // Push the system ID and corresponding Source
147       // on some stacks for later retrieval during parse() time.
148       handler.pushImportURL(hrefUrl);
149       handler.pushImportSource(sourceFromURIResolver);
150 
151       int savedStylesheetType = handler.getStylesheetType();
152 
153       handler.setStylesheetType(this.getStylesheetType());
154       handler.pushNewNamespaceSupport();
155 
156       try
157       {
158         parse(handler, uri, localName, rawName, attributes);
159       }
160       finally
161       {
162         handler.setStylesheetType(savedStylesheetType);
163         handler.popImportURL();
164         handler.popImportSource();
165         handler.popNamespaceSupport();
166       }
167     }
168     catch(TransformerException te)
169     {
170       handler.error(te.getMessage(), te);
171     }
172   }
173 
174   /**
175    * Set off a new parse for an included or imported stylesheet.  This will
176    * set the {@link StylesheetHandler} to a new state, and recurse in with
177    * a new set of parse events.  Once this function returns, the state of
178    * the StylesheetHandler should be restored.
179    *
180    * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
181    * @param uri The Namespace URI, which should be the XSLT namespace.
182    * @param localName The local name (without prefix), which should be "include" or "import".
183    * @param rawName The qualified name (with prefix).
184    * @param attributes The list of attributes on the xsl:include or xsl:import element.
185    *
186    * @throws org.xml.sax.SAXException Any SAX exception, possibly
187    *            wrapping another exception.
188    */
parse( StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)189   protected void parse(
190           StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
191             throws org.xml.sax.SAXException
192   {
193     TransformerFactoryImpl processor = handler.getStylesheetProcessor();
194     URIResolver uriresolver = processor.getURIResolver();
195 
196     try
197     {
198       Source source = null;
199 
200       // The base identifier, an aboslute URI
201       // that is associated with the included/imported
202       // stylesheet module is known in this method,
203       // so this method does the pushing of the
204       // base ID onto the stack.
205 
206       if (null != uriresolver)
207       {
208         // There is a user provided URI resolver.
209         // At the startElement() call we would
210         // have tried to obtain a Source from it
211         // which we now retrieve
212         source = handler.peekSourceFromURIResolver();
213 
214         if (null != source && source instanceof DOMSource)
215         {
216           Node node = ((DOMSource)source).getNode();
217 
218           // There is a user provided URI resolver.
219           // At the startElement() call we would
220           // have already pushed the system ID, obtained
221           // from either the source.getSystemId(), if non-null
222           // or from SystemIDResolver.getAbsoluteURI() as a backup
223           // which we now retrieve.
224           String systemId = handler.peekImportURL();
225 
226           // Push the absolute URI of the included/imported
227           // stylesheet module onto the stack.
228           if (systemId != null)
229               handler.pushBaseIndentifier(systemId);
230 
231           TreeWalker walker = new TreeWalker(handler, new org.apache.xml.utils.DOM2Helper(), systemId);
232 
233           try
234           {
235             walker.traverse(node);
236           }
237           catch(org.xml.sax.SAXException se)
238           {
239             throw new TransformerException(se);
240           }
241           if (systemId != null)
242             handler.popBaseIndentifier();
243           return;
244         }
245       }
246 
247       if(null == source)
248       {
249         String absURL = SystemIDResolver.getAbsoluteURI(getHref(),
250                           handler.getBaseIdentifier());
251 
252         source = new StreamSource(absURL);
253       }
254 
255       // possible callback to a class that over-rides this method.
256       source = processSource(handler, source);
257 
258       XMLReader reader = null;
259 
260       if(source instanceof SAXSource)
261       {
262         SAXSource saxSource = (SAXSource)source;
263         reader = saxSource.getXMLReader(); // may be null
264       }
265 
266       InputSource inputSource = SAXSource.sourceToInputSource(source);
267 
268       if (null == reader)
269       {
270         // Use JAXP1.1 ( if possible )
271         try {
272           javax.xml.parsers.SAXParserFactory factory=
273                                                      javax.xml.parsers.SAXParserFactory.newInstance();
274           factory.setNamespaceAware( true );
275 
276           if (handler.getStylesheetProcessor().isSecureProcessing())
277           {
278             try
279             {
280               factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
281             }
282             catch (org.xml.sax.SAXException se) {}
283           }
284 
285           javax.xml.parsers.SAXParser jaxpParser=
286                                                  factory.newSAXParser();
287           reader=jaxpParser.getXMLReader();
288 
289         } catch( javax.xml.parsers.ParserConfigurationException ex ) {
290           throw new org.xml.sax.SAXException( ex );
291         } catch( javax.xml.parsers.FactoryConfigurationError ex1 ) {
292             throw new org.xml.sax.SAXException( ex1.toString() );
293         }
294         catch( NoSuchMethodError ex2 )
295         {
296         }
297         catch (AbstractMethodError ame){}
298       }
299       if (null == reader)
300         reader = XMLReaderFactory.createXMLReader();
301 
302       if (null != reader)
303       {
304         reader.setContentHandler(handler);
305 
306         // Push the absolute URI of the included/imported
307         // stylesheet module onto the stack.
308         handler.pushBaseIndentifier(inputSource.getSystemId());
309 
310         try
311         {
312           reader.parse(inputSource);
313         }
314         finally
315         {
316           handler.popBaseIndentifier();
317         }
318       }
319     }
320     catch (IOException ioe)
321     {
322       handler.error(XSLTErrorResources.ER_IOEXCEPTION,
323                     new Object[]{ getHref() }, ioe);
324     }
325     catch(TransformerException te)
326     {
327       handler.error(te.getMessage(), te);
328     }
329   }
330 
331   /**
332    * This method does nothing, but a class that extends this class could
333    * over-ride it and do some processing of the source.
334    * @param handler The calling StylesheetHandler/TemplatesBuilder.
335    * @param source The source of the included stylesheet.
336    * @return the same or an equivalent source to what was passed in.
337    */
processSource(StylesheetHandler handler, Source source)338   protected Source processSource(StylesheetHandler handler, Source source)
339   {
340       return source;
341   }
342 
343   /**
344    * Get the Source object for the included or imported stylesheet module
345    * obtained from the user's URIResolver, if there is no user provided
346    * URIResolver null is returned.
347    */
getSourceFromUriResolver(StylesheetHandler handler)348   private Source getSourceFromUriResolver(StylesheetHandler handler)
349             throws TransformerException {
350         Source s = null;
351             TransformerFactoryImpl processor = handler.getStylesheetProcessor();
352             URIResolver uriresolver = processor.getURIResolver();
353             if (uriresolver != null) {
354                 String href = getHref();
355                 String base = handler.getBaseIdentifier();
356                 s = uriresolver.resolve(href,base);
357             }
358 
359         return s;
360     }
361 
362     /**
363      * Get the base URI of the included or imported stylesheet,
364      * if the user provided a URIResolver, then get the Source
365      * object for the stylsheet from it, and get the systemId
366      * from that Source object, otherwise try to recover by
367      * using the SysteIDResolver to figure out the base URI.
368      * @param handler The handler that processes the stylesheet as SAX events,
369      * and maintains state
370      * @param s The Source object from a URIResolver, for the included stylesheet module,
371      * so this will be null if there is no URIResolver set.
372      */
getBaseURIOfIncludedStylesheet(StylesheetHandler handler, Source s)373     private String getBaseURIOfIncludedStylesheet(StylesheetHandler handler, Source s)
374             throws TransformerException {
375 
376 
377 
378         String baseURI;
379         String idFromUriResolverSource;
380         if (s != null && (idFromUriResolverSource = s.getSystemId()) != null) {
381             // We have a Source obtained from a users's URIResolver,
382             // and the system ID is set on it, so return that as the base URI
383             baseURI = idFromUriResolverSource;
384         } else {
385             // The user did not provide a URIResolver, or it did not
386             // return a Source for the included stylesheet module, or
387             // the Source has no system ID set, so we fall back to using
388             // the system ID Resolver to take the href and base
389             // to generate the baseURI of the included stylesheet.
390             baseURI = SystemIDResolver.getAbsoluteURI(getHref(), handler
391                     .getBaseIdentifier());
392         }
393 
394         return baseURI;
395     }
396 }
397