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 */ reset()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 */ setURIResolver(URIResolver resolver)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 */ getURIResolver()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 */ findURIFromDoc(int owner)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 */ resolveURI( String base, String urlString, SourceLocator locator)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 */ removeDocumentFromCache(int n)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 */ putDocumentInCache(int n, Source source)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 */ getNode(Source source)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 */ getSourceTree( String base, String urlString, SourceLocator locator, XPathContext xctxt)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 */ getSourceTree(Source source, SourceLocator locator, XPathContext xctxt)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 */ parseToNode(Source source, SourceLocator locator, XPathContext xctxt)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 */ getXMLReader(Source inputSource, SourceLocator locator)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