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