Home | History | Annotate | Download | only in processor
      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    */
     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