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    }