Home | History | Annotate | Download | only in xpath
      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: SourceTreeManager.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xpath;
     22 
     23 import java.io.IOException;
     24 import java.util.Vector;
     25 
     26 import javax.xml.transform.Source;
     27 import javax.xml.transform.SourceLocator;
     28 import javax.xml.transform.TransformerException;
     29 import javax.xml.transform.URIResolver;
     30 import javax.xml.transform.sax.SAXSource;
     31 import javax.xml.transform.stream.StreamSource;
     32 
     33 import org.apache.xml.dtm.DTM;
     34 import org.apache.xml.utils.SystemIDResolver;
     35 
     36 import org.xml.sax.XMLReader;
     37 import org.xml.sax.helpers.XMLReaderFactory;
     38 
     39 /**
     40  * This class bottlenecks all management of source trees.  The methods
     41  * in this class should allow easy garbage collection of source
     42  * trees (not yet!), and should centralize parsing for those source trees.
     43  */
     44 public class SourceTreeManager
     45 {
     46 
     47   /** Vector of SourceTree objects that this manager manages. */
     48   private Vector m_sourceTree = new Vector();
     49 
     50   /**
     51    * Reset the list of SourceTree objects that this manager manages.
     52    *
     53    */
     54   public void reset()
     55   {
     56     m_sourceTree = new Vector();
     57   }
     58 
     59   /** The TrAX URI resolver used to obtain source trees. */
     60   URIResolver m_uriResolver;
     61 
     62   /**
     63    * Set an object that will be used to resolve URIs used in
     64    * document(), etc.
     65    * @param resolver An object that implements the URIResolver interface,
     66    * or null.
     67    */
     68   public void setURIResolver(URIResolver resolver)
     69   {
     70     m_uriResolver = resolver;
     71   }
     72 
     73   /**
     74    * Get the object that will be used to resolve URIs used in
     75    * document(), etc.
     76    * @return An object that implements the URIResolver interface,
     77    * or null.
     78    */
     79   public URIResolver getURIResolver()
     80   {
     81     return m_uriResolver;
     82   }
     83 
     84   /**
     85    * Given a document, find the URL associated with that document.
     86    * @param owner Document that was previously processed by this liaison.
     87    *
     88    * @return The base URI of the owner argument.
     89    */
     90   public String findURIFromDoc(int owner)
     91   {
     92     int n = m_sourceTree.size();
     93 
     94     for (int i = 0; i < n; i++)
     95     {
     96       SourceTree sTree = (SourceTree) m_sourceTree.elementAt(i);
     97 
     98       if (owner == sTree.m_root)
     99         return sTree.m_url;
    100     }
    101 
    102     return null;
    103   }
    104 
    105   /**
    106    * This will be called by the processor when it encounters
    107    * an xsl:include, xsl:import, or document() function.
    108    *
    109    * @param base The base URI that should be used.
    110    * @param urlString Value from an xsl:import or xsl:include's href attribute,
    111    * or a URI specified in the document() function.
    112    *
    113    * @return a Source that can be used to process the resource.
    114    *
    115    * @throws IOException
    116    * @throws TransformerException
    117    */
    118   public Source resolveURI(
    119           String base, String urlString, SourceLocator locator)
    120             throws TransformerException, IOException
    121   {
    122 
    123     Source source = null;
    124 
    125     if (null != m_uriResolver)
    126     {
    127       source = m_uriResolver.resolve(urlString, base);
    128     }
    129 
    130     if (null == source)
    131     {
    132       String uri = SystemIDResolver.getAbsoluteURI(urlString, base);
    133 
    134       source = new StreamSource(uri);
    135     }
    136 
    137     return source;
    138   }
    139 
    140   /** JJK: Support  <?xalan:doc_cache_off?> kluge in ElemForEach.
    141    * TODO: This function is highly dangerous. Cache management must be improved.
    142    *
    143    * @param n The node to remove.
    144    */
    145   public void removeDocumentFromCache(int n)
    146   {
    147     if(DTM.NULL ==n)
    148       return;
    149     for(int i=m_sourceTree.size()-1;i>=0;--i)
    150     {
    151       SourceTree st=(SourceTree)m_sourceTree.elementAt(i);
    152       if(st!=null && st.m_root==n)
    153       {
    154 	m_sourceTree.removeElementAt(i);
    155 	return;
    156       }
    157     }
    158   }
    159 
    160 
    161 
    162   /**
    163    * Put the source tree root node in the document cache.
    164    * TODO: This function needs to be a LOT more sophisticated.
    165    *
    166    * @param n The node to cache.
    167    * @param source The Source object to cache.
    168    */
    169   public void putDocumentInCache(int n, Source source)
    170   {
    171 
    172     int cachedNode = getNode(source);
    173 
    174     if (DTM.NULL != cachedNode)
    175     {
    176       if (!(cachedNode == n))
    177         throw new RuntimeException(
    178           "Programmer's Error!  "
    179           + "putDocumentInCache found reparse of doc: "
    180           + source.getSystemId());
    181       return;
    182     }
    183     if (null != source.getSystemId())
    184     {
    185       m_sourceTree.addElement(new SourceTree(n, source.getSystemId()));
    186     }
    187   }
    188 
    189   /**
    190    * Given a Source object, find the node associated with it.
    191    *
    192    * @param source The Source object to act as the key.
    193    *
    194    * @return The node that is associated with the Source, or null if not found.
    195    */
    196   public int getNode(Source source)
    197   {
    198 
    199 //    if (source instanceof DOMSource)
    200 //      return ((DOMSource) source).getNode();
    201 
    202     // TODO: Not sure if the BaseID is really the same thing as the ID.
    203     String url = source.getSystemId();
    204 
    205     if (null == url)
    206       return DTM.NULL;
    207 
    208     int n = m_sourceTree.size();
    209 
    210     // System.out.println("getNode: "+n);
    211     for (int i = 0; i < n; i++)
    212     {
    213       SourceTree sTree = (SourceTree) m_sourceTree.elementAt(i);
    214 
    215       // System.out.println("getNode -         url: "+url);
    216       // System.out.println("getNode - sTree.m_url: "+sTree.m_url);
    217       if (url.equals(sTree.m_url))
    218         return sTree.m_root;
    219     }
    220 
    221     // System.out.println("getNode - returning: "+node);
    222     return DTM.NULL;
    223   }
    224 
    225   /**
    226    * Get the source tree from the a base URL and a URL string.
    227    *
    228    * @param base The base URI to use if the urlString is relative.
    229    * @param urlString An absolute or relative URL string.
    230    * @param locator The location of the caller, for diagnostic purposes.
    231    *
    232    * @return should be a non-null reference to the node identified by the
    233    * base and urlString.
    234    *
    235    * @throws TransformerException If the URL can not resolve to a node.
    236    */
    237   public int getSourceTree(
    238           String base, String urlString, SourceLocator locator, XPathContext xctxt)
    239             throws TransformerException
    240   {
    241 
    242     // System.out.println("getSourceTree");
    243     try
    244     {
    245       Source source = this.resolveURI(base, urlString, locator);
    246 
    247       // System.out.println("getSourceTree - base: "+base+", urlString: "+urlString+", source: "+source.getSystemId());
    248       return getSourceTree(source, locator, xctxt);
    249     }
    250     catch (IOException ioe)
    251     {
    252       throw new TransformerException(ioe.getMessage(), locator, ioe);
    253     }
    254 
    255     /* catch (TransformerException te)
    256      {
    257        throw new TransformerException(te.getMessage(), locator, te);
    258      }*/
    259   }
    260 
    261   /**
    262    * Get the source tree from the input source.
    263    *
    264    * @param source The Source object that should identify the desired node.
    265    * @param locator The location of the caller, for diagnostic purposes.
    266    *
    267    * @return non-null reference to a node.
    268    *
    269    * @throws TransformerException if the Source argument can't be resolved to
    270    *         a node.
    271    */
    272   public int getSourceTree(Source source, SourceLocator locator, XPathContext xctxt)
    273           throws TransformerException
    274   {
    275 
    276     int n = getNode(source);
    277 
    278     if (DTM.NULL != n)
    279       return n;
    280 
    281     n = parseToNode(source, locator, xctxt);
    282 
    283     if (DTM.NULL != n)
    284       putDocumentInCache(n, source);
    285 
    286     return n;
    287   }
    288 
    289   /**
    290    * Try to create a DOM source tree from the input source.
    291    *
    292    * @param source The Source object that identifies the source node.
    293    * @param locator The location of the caller, for diagnostic purposes.
    294    *
    295    * @return non-null reference to node identified by the source argument.
    296    *
    297    * @throws TransformerException if the source argument can not be resolved
    298    *         to a source node.
    299    */
    300   public int parseToNode(Source source, SourceLocator locator, XPathContext xctxt)
    301           throws TransformerException
    302   {
    303 
    304     try
    305     {
    306       Object xowner = xctxt.getOwnerObject();
    307       DTM dtm;
    308       if(null != xowner && xowner instanceof org.apache.xml.dtm.DTMWSFilter)
    309       {
    310         dtm = xctxt.getDTM(source, false,
    311                           (org.apache.xml.dtm.DTMWSFilter)xowner, false, true);
    312       }
    313       else
    314       {
    315         dtm = xctxt.getDTM(source, false, null, false, true);
    316       }
    317       return dtm.getDocument();
    318     }
    319     catch (Exception e)
    320     {
    321       //e.printStackTrace();
    322       throw new TransformerException(e.getMessage(), locator, e);
    323     }
    324 
    325   }
    326 
    327   /**
    328    * This method returns the SAX2 parser to use with the InputSource
    329    * obtained from this URI.
    330    * It may return null if any SAX2-conformant XML parser can be used,
    331    * or if getInputSource() will also return null. The parser must
    332    * be free for use (i.e.
    333    * not currently in use for another parse().
    334    *
    335    * @param inputSource The value returned from the URIResolver.
    336    * @return a SAX2 XMLReader to use to resolve the inputSource argument.
    337    * @param locator The location of the original caller, for diagnostic purposes.
    338    *
    339    * @throws TransformerException if the reader can not be created.
    340    */
    341   public static XMLReader getXMLReader(Source inputSource, SourceLocator locator)
    342           throws TransformerException
    343   {
    344 
    345     try
    346     {
    347       XMLReader reader = (inputSource instanceof SAXSource)
    348                          ? ((SAXSource) inputSource).getXMLReader() : null;
    349 
    350       if (null == reader)
    351       {
    352         try {
    353           javax.xml.parsers.SAXParserFactory factory=
    354               javax.xml.parsers.SAXParserFactory.newInstance();
    355           factory.setNamespaceAware( true );
    356           javax.xml.parsers.SAXParser jaxpParser=
    357               factory.newSAXParser();
    358           reader=jaxpParser.getXMLReader();
    359 
    360         } catch( javax.xml.parsers.ParserConfigurationException ex ) {
    361           throw new org.xml.sax.SAXException( ex );
    362         } catch( javax.xml.parsers.FactoryConfigurationError ex1 ) {
    363             throw new org.xml.sax.SAXException( ex1.toString() );
    364         } catch( NoSuchMethodError ex2 ) {
    365         }
    366         catch (AbstractMethodError ame){}
    367         if(null == reader)
    368           reader = XMLReaderFactory.createXMLReader();
    369       }
    370 
    371       try
    372       {
    373         reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
    374                           true);
    375       }
    376       catch (org.xml.sax.SAXException se)
    377       {
    378 
    379         // What can we do?
    380         // TODO: User diagnostics.
    381       }
    382 
    383       return reader;
    384     }
    385     catch (org.xml.sax.SAXException se)
    386     {
    387       throw new TransformerException(se.getMessage(), locator, se);
    388     }
    389   }
    390 }
    391