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 */
21package org.apache.xalan.processor;
22
23import java.io.IOException;
24
25import javax.xml.XMLConstants;
26import javax.xml.transform.Source;
27import javax.xml.transform.TransformerException;
28import javax.xml.transform.URIResolver;
29import javax.xml.transform.dom.DOMSource;
30import javax.xml.transform.sax.SAXSource;
31import javax.xml.transform.stream.StreamSource;
32
33import org.apache.xalan.res.XSLMessages;
34import org.apache.xalan.res.XSLTErrorResources;
35import org.apache.xml.utils.SystemIDResolver;
36import org.apache.xml.utils.TreeWalker;
37
38import org.w3c.dom.Node;
39
40import org.xml.sax.Attributes;
41import org.xml.sax.InputSource;
42import org.xml.sax.XMLReader;
43import 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 */
52public 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   */
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   */
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   */
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   */
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   */
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}
397