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: ProcessorInclude.java 469349 2006-10-31 03:06:50Z minchau $
020 */
021 package org.apache.xalan.processor;
022
023 import java.io.IOException;
024
025 import javax.xml.XMLConstants;
026 import javax.xml.transform.Source;
027 import javax.xml.transform.TransformerException;
028 import javax.xml.transform.URIResolver;
029 import javax.xml.transform.dom.DOMSource;
030 import javax.xml.transform.sax.SAXSource;
031 import javax.xml.transform.stream.StreamSource;
032
033 import org.apache.xalan.res.XSLMessages;
034 import org.apache.xalan.res.XSLTErrorResources;
035 import org.apache.xml.utils.SystemIDResolver;
036 import org.apache.xml.utils.TreeWalker;
037
038 import org.w3c.dom.Node;
039
040 import org.xml.sax.Attributes;
041 import org.xml.sax.InputSource;
042 import org.xml.sax.XMLReader;
043 import org.xml.sax.helpers.XMLReaderFactory;
044
045 /**
046 * TransformerFactory class for xsl:include markup.
047 * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
048 * @see <a href="http://www.w3.org/TR/xslt#include">include in XSLT Specification</a>
049 *
050 * @xsl.usage internal
051 */
052 public class ProcessorInclude extends XSLTElementProcessor
053 {
054 static final long serialVersionUID = -4570078731972673481L;
055
056 /**
057 * The base URL of the XSL document.
058 * @serial
059 */
060 private String m_href = null;
061
062 /**
063 * Get the base identifier with which this stylesheet is associated.
064 *
065 * @return non-null reference to the href attribute string, or
066 * null if setHref has not been called.
067 */
068 public String getHref()
069 {
070 return m_href;
071 }
072
073 /**
074 * Get the base identifier with which this stylesheet is associated.
075 *
076 * @param baseIdent Should be a non-null reference to a valid URL string.
077 */
078 public void setHref(String baseIdent)
079 {
080 // Validate?
081 m_href = baseIdent;
082 }
083
084 /**
085 * Get the stylesheet type associated with an included stylesheet
086 *
087 * @return the type of the stylesheet
088 */
089 protected int getStylesheetType()
090 {
091 return StylesheetHandler.STYPE_INCLUDE;
092 }
093
094 /**
095 * Get the error number associated with this type of stylesheet including itself
096 *
097 * @return the appropriate error number
098 */
099 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 */
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 */
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 */
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 */
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 */
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 }