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: SourceTreeManager.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath;
022    
023    import java.io.IOException;
024    import java.util.Vector;
025    
026    import javax.xml.transform.Source;
027    import javax.xml.transform.SourceLocator;
028    import javax.xml.transform.TransformerException;
029    import javax.xml.transform.URIResolver;
030    import javax.xml.transform.sax.SAXSource;
031    import javax.xml.transform.stream.StreamSource;
032    
033    import org.apache.xml.dtm.DTM;
034    import org.apache.xml.utils.SystemIDResolver;
035    
036    import org.xml.sax.XMLReader;
037    import org.xml.sax.helpers.XMLReaderFactory;
038    
039    /**
040     * This class bottlenecks all management of source trees.  The methods
041     * in this class should allow easy garbage collection of source
042     * trees (not yet!), and should centralize parsing for those source trees.
043     */
044    public class SourceTreeManager
045    {
046    
047      /** Vector of SourceTree objects that this manager manages. */
048      private Vector m_sourceTree = new Vector();
049    
050      /**
051       * Reset the list of SourceTree objects that this manager manages.
052       *
053       */
054      public void reset()
055      {
056        m_sourceTree = new Vector();
057      }
058    
059      /** The TrAX URI resolver used to obtain source trees. */
060      URIResolver m_uriResolver;
061    
062      /**
063       * Set an object that will be used to resolve URIs used in
064       * document(), etc.
065       * @param resolver An object that implements the URIResolver interface,
066       * or null.
067       */
068      public void setURIResolver(URIResolver resolver)
069      {
070        m_uriResolver = resolver;
071      }
072    
073      /**
074       * Get the object that will be used to resolve URIs used in
075       * document(), etc.
076       * @return An object that implements the URIResolver interface,
077       * or null.
078       */
079      public URIResolver getURIResolver()
080      {
081        return m_uriResolver;
082      }
083    
084      /**
085       * Given a document, find the URL associated with that document.
086       * @param owner Document that was previously processed by this liaison.
087       *
088       * @return The base URI of the owner argument.
089       */
090      public String findURIFromDoc(int owner)
091      {
092        int n = m_sourceTree.size();
093    
094        for (int i = 0; i < n; i++)
095        {
096          SourceTree sTree = (SourceTree) m_sourceTree.elementAt(i);
097    
098          if (owner == sTree.m_root)
099            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    }