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 }