001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: SourceTreeManager.java,v 1.35 2005/01/23 01:02:10 mcnamara Exp $
018: */
019: package org.apache.xpath;
020:
021: import java.io.IOException;
022: import java.util.Vector;
023:
024: import javax.xml.transform.Source;
025: import javax.xml.transform.SourceLocator;
026: import javax.xml.transform.TransformerException;
027: import javax.xml.transform.URIResolver;
028: import javax.xml.transform.sax.SAXSource;
029: import javax.xml.transform.stream.StreamSource;
030:
031: import org.apache.xml.dtm.DTM;
032: import org.apache.xml.utils.SystemIDResolver;
033:
034: import org.xml.sax.XMLReader;
035: import org.xml.sax.helpers.XMLReaderFactory;
036:
037: /**
038: * This class bottlenecks all management of source trees. The methods
039: * in this class should allow easy garbage collection of source
040: * trees (not yet!), and should centralize parsing for those source trees.
041: */
042: public class SourceTreeManager {
043:
044: /** Vector of SourceTree objects that this manager manages. */
045: private Vector m_sourceTree = new Vector();
046:
047: /**
048: * Reset the list of SourceTree objects that this manager manages.
049: *
050: */
051: public void reset() {
052: m_sourceTree = new Vector();
053: }
054:
055: /** The TrAX URI resolver used to obtain source trees. */
056: URIResolver m_uriResolver;
057:
058: /**
059: * Set an object that will be used to resolve URIs used in
060: * document(), etc.
061: * @param resolver An object that implements the URIResolver interface,
062: * or null.
063: */
064: public void setURIResolver(URIResolver resolver) {
065: m_uriResolver = resolver;
066: }
067:
068: /**
069: * Get the object that will be used to resolve URIs used in
070: * document(), etc.
071: * @return An object that implements the URIResolver interface,
072: * or null.
073: */
074: public URIResolver getURIResolver() {
075: return m_uriResolver;
076: }
077:
078: /**
079: * Given a document, find the URL associated with that document.
080: * @param owner Document that was previously processed by this liaison.
081: *
082: * @return The base URI of the owner argument.
083: */
084: public String findURIFromDoc(int owner) {
085: int n = m_sourceTree.size();
086:
087: for (int i = 0; i < n; i++) {
088: SourceTree sTree = (SourceTree) m_sourceTree.elementAt(i);
089:
090: if (owner == sTree.m_root)
091: return sTree.m_url;
092: }
093:
094: return null;
095: }
096:
097: /**
098: * This will be called by the processor when it encounters
099: * an xsl:include, xsl:import, or document() function.
100: *
101: * @param base The base URI that should be used.
102: * @param urlString Value from an xsl:import or xsl:include's href attribute,
103: * or a URI specified in the document() function.
104: *
105: * @return a Source that can be used to process the resource.
106: *
107: * @throws IOException
108: * @throws TransformerException
109: */
110: public Source resolveURI(String base, String urlString,
111: SourceLocator locator) throws TransformerException,
112: IOException {
113:
114: Source source = null;
115:
116: if (null != m_uriResolver) {
117: source = m_uriResolver.resolve(urlString, base);
118: }
119:
120: if (null == source) {
121: String uri = SystemIDResolver.getAbsoluteURI(urlString,
122: base);
123:
124: source = new StreamSource(uri);
125: }
126:
127: return source;
128: }
129:
130: /** JJK: Support <?xalan:doc_cache_off?> kluge in ElemForEach.
131: * TODO: This function is highly dangerous. Cache management must be improved.
132: *
133: * @param n The node to remove.
134: */
135: public void removeDocumentFromCache(int n) {
136: if (DTM.NULL == n)
137: return;
138: for (int i = m_sourceTree.size() - 1; i >= 0; --i) {
139: SourceTree st = (SourceTree) m_sourceTree.elementAt(i);
140: if (st != null && st.m_root == n) {
141: m_sourceTree.removeElementAt(i);
142: return;
143: }
144: }
145: }
146:
147: /**
148: * Put the source tree root node in the document cache.
149: * TODO: This function needs to be a LOT more sophisticated.
150: *
151: * @param n The node to cache.
152: * @param source The Source object to cache.
153: */
154: public void putDocumentInCache(int n, Source source) {
155:
156: int cachedNode = getNode(source);
157:
158: if (DTM.NULL != cachedNode) {
159: if (!(cachedNode == n))
160: throw new RuntimeException("Programmer's Error! "
161: + "putDocumentInCache found reparse of doc: "
162: + source.getSystemId());
163: return;
164: }
165: if (null != source.getSystemId()) {
166: m_sourceTree.addElement(new SourceTree(n, source
167: .getSystemId()));
168: }
169: }
170:
171: /**
172: * Given a Source object, find the node associated with it.
173: *
174: * @param source The Source object to act as the key.
175: *
176: * @return The node that is associated with the Source, or null if not found.
177: */
178: public int getNode(Source source) {
179:
180: // if (source instanceof DOMSource)
181: // return ((DOMSource) source).getNode();
182:
183: // TODO: Not sure if the BaseID is really the same thing as the ID.
184: String url = source.getSystemId();
185:
186: if (null == url)
187: return DTM.NULL;
188:
189: int n = m_sourceTree.size();
190:
191: // System.out.println("getNode: "+n);
192: for (int i = 0; i < n; i++) {
193: SourceTree sTree = (SourceTree) m_sourceTree.elementAt(i);
194:
195: // System.out.println("getNode - url: "+url);
196: // System.out.println("getNode - sTree.m_url: "+sTree.m_url);
197: if (url.equals(sTree.m_url))
198: return sTree.m_root;
199: }
200:
201: // System.out.println("getNode - returning: "+node);
202: return DTM.NULL;
203: }
204:
205: /**
206: * Get the source tree from the a base URL and a URL string.
207: *
208: * @param base The base URI to use if the urlString is relative.
209: * @param urlString An absolute or relative URL string.
210: * @param locator The location of the caller, for diagnostic purposes.
211: *
212: * @return should be a non-null reference to the node identified by the
213: * base and urlString.
214: *
215: * @throws TransformerException If the URL can not resolve to a node.
216: */
217: public int getSourceTree(String base, String urlString,
218: SourceLocator locator, XPathContext xctxt)
219: throws TransformerException {
220:
221: // System.out.println("getSourceTree");
222: try {
223: Source source = this .resolveURI(base, urlString, locator);
224:
225: // System.out.println("getSourceTree - base: "+base+", urlString: "+urlString+", source: "+source.getSystemId());
226: return getSourceTree(source, locator, xctxt);
227: } catch (IOException ioe) {
228: throw new TransformerException(ioe.getMessage(), locator,
229: ioe);
230: }
231:
232: /* catch (TransformerException te)
233: {
234: throw new TransformerException(te.getMessage(), locator, te);
235: }*/
236: }
237:
238: /**
239: * Get the source tree from the input source.
240: *
241: * @param source The Source object that should identify the desired node.
242: * @param locator The location of the caller, for diagnostic purposes.
243: *
244: * @return non-null reference to a node.
245: *
246: * @throws TransformerException if the Source argument can't be resolved to
247: * a node.
248: */
249: public int getSourceTree(Source source, SourceLocator locator,
250: XPathContext xctxt) throws TransformerException {
251:
252: int n = getNode(source);
253:
254: if (DTM.NULL != n)
255: return n;
256:
257: n = parseToNode(source, locator, xctxt);
258:
259: if (DTM.NULL != n)
260: putDocumentInCache(n, source);
261:
262: return n;
263: }
264:
265: /**
266: * Try to create a DOM source tree from the input source.
267: *
268: * @param source The Source object that identifies the source node.
269: * @param locator The location of the caller, for diagnostic purposes.
270: *
271: * @return non-null reference to node identified by the source argument.
272: *
273: * @throws TransformerException if the source argument can not be resolved
274: * to a source node.
275: */
276: public int parseToNode(Source source, SourceLocator locator,
277: XPathContext xctxt) throws TransformerException {
278:
279: try {
280: Object xowner = xctxt.getOwnerObject();
281: DTM dtm;
282: if (null != xowner
283: && xowner instanceof org.apache.xml.dtm.DTMWSFilter) {
284: dtm = xctxt.getDTM(source, false,
285: (org.apache.xml.dtm.DTMWSFilter) xowner, false,
286: true);
287: } else {
288: dtm = xctxt.getDTM(source, false, null, false, true);
289: }
290: return dtm.getDocument();
291: } catch (Exception e) {
292: //e.printStackTrace();
293: throw new TransformerException(e.getMessage(), locator, e);
294: }
295:
296: }
297:
298: /**
299: * This method returns the SAX2 parser to use with the InputSource
300: * obtained from this URI.
301: * It may return null if any SAX2-conformant XML parser can be used,
302: * or if getInputSource() will also return null. The parser must
303: * be free for use (i.e.
304: * not currently in use for another parse().
305: *
306: * @param inputSource The value returned from the URIResolver.
307: * @return a SAX2 XMLReader to use to resolve the inputSource argument.
308: * @param locator The location of the original caller, for diagnostic purposes.
309: *
310: * @throws TransformerException if the reader can not be created.
311: */
312: public static XMLReader getXMLReader(Source inputSource,
313: SourceLocator locator) throws TransformerException {
314:
315: try {
316: XMLReader reader = (inputSource instanceof SAXSource) ? ((SAXSource) inputSource)
317: .getXMLReader()
318: : null;
319:
320: if (null == reader) {
321: try {
322: javax.xml.parsers.SAXParserFactory factory = javax.xml.parsers.SAXParserFactory
323: .newInstance();
324: factory.setNamespaceAware(true);
325: javax.xml.parsers.SAXParser jaxpParser = factory
326: .newSAXParser();
327: reader = jaxpParser.getXMLReader();
328:
329: } catch (javax.xml.parsers.ParserConfigurationException ex) {
330: throw new org.xml.sax.SAXException(ex);
331: } catch (javax.xml.parsers.FactoryConfigurationError ex1) {
332: throw new org.xml.sax.SAXException(ex1.toString());
333: } catch (NoSuchMethodError ex2) {
334: } catch (AbstractMethodError ame) {
335: }
336: if (null == reader)
337: reader = XMLReaderFactory.createXMLReader();
338: }
339:
340: try {
341: reader
342: .setFeature(
343: "http://xml.org/sax/features/namespace-prefixes",
344: true);
345: } catch (org.xml.sax.SAXException se) {
346:
347: // What can we do?
348: // TODO: User diagnostics.
349: }
350:
351: return reader;
352: } catch (org.xml.sax.SAXException se) {
353: throw new TransformerException(se.getMessage(), locator, se);
354: }
355: }
356: }
|