KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > functions > Document


1 package net.sf.saxon.functions;
2 import net.sf.saxon.Controller;
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.event.Builder;
5 import net.sf.saxon.event.Receiver;
6 import net.sf.saxon.event.Sender;
7 import net.sf.saxon.event.PipelineConfiguration;
8 import net.sf.saxon.expr.*;
9 import net.sf.saxon.om.*;
10 import net.sf.saxon.sort.DocumentOrderIterator;
11 import net.sf.saxon.sort.GlobalOrderComparer;
12 import net.sf.saxon.trans.DynamicError;
13 import net.sf.saxon.trans.XPathException;
14 import net.sf.saxon.value.Cardinality;
15
16 import javax.xml.transform.Source JavaDoc;
17 import javax.xml.transform.TransformerException JavaDoc;
18 import javax.xml.transform.URIResolver JavaDoc;
19 import javax.xml.transform.SourceLocator JavaDoc;
20 import javax.xml.transform.dom.DOMSource JavaDoc;
21 import java.net.URI JavaDoc;
22 import java.net.URISyntaxException JavaDoc;
23
24
25 /**
26  * Implements the XSLT document() function
27  */

28
29 public class Document extends SystemFunction implements XSLTFunction {
30
31     private String JavaDoc expressionBaseURI = null;
32
33     public void checkArguments(StaticContext env) throws XPathException {
34         if (expressionBaseURI == null) {
35             // only do this once. The second call supplies an env pointing to the containing
36
// xsl:template, which has a different base URI (and in a simplified stylesheet, has no base URI)
37
super.checkArguments(env);
38             expressionBaseURI = env.getBaseURI();
39             Optimizer opt = env.getConfiguration().getOptimizer();
40             argument[0] = ExpressionTool.unsorted(opt, argument[0], false);
41         }
42     }
43
44     /**
45     * Determine the static cardinality
46     */

47
48     public int computeCardinality() {
49         Expression expression = argument[0];
50         if (Cardinality.allowsMany(expression.getCardinality())) {
51             return StaticProperty.ALLOWS_ZERO_OR_MORE;
52         } else {
53             return StaticProperty.ALLOWS_ZERO_OR_ONE;
54         }
55         // may have to revise this if the argument can be a list-valued element or attribute
56
}
57
58     /**
59     * Get the static properties of this expression (other than its type). The result is
60     * bit-signficant. These properties are used for optimizations. In general, if
61     * property bit is set, it is true, but if it is unset, the value is unknown.
62     */

63
64     public int computeSpecialProperties() {
65         return StaticProperty.ORDERED_NODESET |
66                 StaticProperty.PEER_NODESET |
67                 StaticProperty.NON_CREATIVE;
68         // Declaring it as a peer node-set expression avoids sorting of expressions such as
69
// document(XXX)/a/b/c
70
// The document() function might appear to be creative: but it isn't, because multiple calls
71
// with the same arguments will produce identical results.
72
}
73
74     /**
75     * preEvaluate: this method suppresses compile-time evaluation by doing nothing
76     */

77
78     public Expression preEvaluate(StaticContext env) {
79         return this;
80     }
81
82
83     /**
84     * iterate() handles evaluation of the function:
85     * it returns a sequence of Document nodes
86     */

87
88     public SequenceIterator iterate(XPathContext context) throws XPathException {
89         int numArgs = argument.length;
90
91         SequenceIterator hrefSequence = argument[0].iterate(context);
92         String JavaDoc baseURI = null;
93         if (numArgs==2) {
94             // we can trust the type checking: it must be a node
95
NodeInfo base = (NodeInfo)argument[1].evaluateItem(context);
96             baseURI = base.getBaseURI();
97         }
98
99         DocumentMappingFunction map = new DocumentMappingFunction();
100         map.baseURI = baseURI;
101         map.stylesheetURI = expressionBaseURI;
102         map.locator = this;
103
104         MappingIterator iter = new MappingIterator(hrefSequence, map, context);
105
106         Expression expression = argument[0];
107         if (Cardinality.allowsMany(expression.getCardinality())) {
108             return new DocumentOrderIterator(iter, GlobalOrderComparer.getInstance());
109             // this is to make sure we eliminate duplicates: two href's might be the same
110
} else {
111             return iter;
112         }
113     }
114
115     private static class DocumentMappingFunction implements MappingFunction {
116
117         public String JavaDoc baseURI;
118         public String JavaDoc stylesheetURI;
119         public SourceLocator JavaDoc locator;
120
121         /**
122         * Implement the MappingFunction interface: called from the MappingIterator
123         */

124
125         public Object JavaDoc map(Item item, XPathContext context) throws XPathException {
126
127             if (baseURI==null) {
128                 if (item instanceof NodeInfo) {
129                     baseURI = ((NodeInfo)item).getBaseURI();
130                 } else {
131                     baseURI = stylesheetURI;
132                 }
133             }
134             NodeInfo doc = makeDoc(item.getStringValue(), baseURI, context, locator);
135             return doc;
136         }
137     }
138
139
140     /**
141     * Supporting routine to load one external document given a URI (href) and a baseURI
142     */

143
144     public static NodeInfo makeDoc(String JavaDoc href, String JavaDoc baseURL, XPathContext c, SourceLocator JavaDoc locator)
145             throws XPathException {
146
147         // If the href contains a fragment identifier, strip it out now
148

149         int hash = href.indexOf('#');
150
151         String JavaDoc fragmentId = null;
152         if (hash>=0) {
153             if (hash==href.length()-1) {
154                 // # sign at end - just ignore it
155
href = href.substring(0, hash);
156             } else {
157                 fragmentId = href.substring(hash+1);
158                 href = href.substring(0, hash);
159             }
160         }
161
162
163         // Resolve relative URI
164

165         String JavaDoc documentKey;
166         if (baseURL==null) { // no base URI available
167
try {
168                 // the href might be an absolute URL
169
documentKey = (new URI JavaDoc(href)).toString();
170             } catch (URISyntaxException JavaDoc err) {
171                 // it isn't; but the URI resolver might know how to cope
172
documentKey = '/' + href;
173                 baseURL = "";
174             }
175         } else if (href.equals("")) {
176             // common case in XSLT, which java.net.URI#resolve() does not handle correctly
177
documentKey = baseURL;
178         } else {
179             try {
180                 URI JavaDoc url = new URI JavaDoc(baseURL).resolve(href);
181                 documentKey = url.toString();
182             } catch (URISyntaxException JavaDoc err) {
183                 documentKey = baseURL + "/../" + href;
184             } catch (IllegalArgumentException JavaDoc err) {
185                 documentKey = baseURL + "/../" + href;
186             }
187         }
188
189         Controller controller = c.getController();
190
191         // see if the document is already loaded
192

193
194         DocumentInfo doc = controller.getDocumentPool().find(documentKey);
195         if (doc != null) {
196             return getFragment(doc, fragmentId, c);
197         }
198
199         try {
200             // Get a Source from the URIResolver
201

202             URIResolver JavaDoc r = controller.getURIResolver();
203             Source JavaDoc source = null;
204             if (r != null) {
205                 source = r.resolve(href, baseURL);
206             }
207
208             // if a user URI resolver returns null, try the standard one
209
// (Note, the standard URI resolver never returns null)
210
if (source==null) {
211                 r = controller.getStandardURIResolver();
212                 source = r.resolve(href, baseURL);
213             }
214
215             Configuration config = controller.getConfiguration();
216             source = config.getSourceResolver().resolveSource(source, config);
217
218             DocumentInfo newdoc;
219             if (source instanceof NodeInfo || source instanceof DOMSource JavaDoc) {
220                 NodeInfo startNode = controller.prepareInputTree(source);
221                 newdoc = startNode.getDocumentRoot();
222             } else {
223                 Builder b = controller.makeBuilder();
224                 Receiver s = controller.makeStripper(b);
225                 if (controller.getExecutable().stripsInputTypeAnnotations()) {
226                     s = controller.getConfiguration().getAnnotationStripper(s);
227                 }
228                 new Sender(controller.makePipelineConfiguration()).send(source, s);
229                 newdoc = (DocumentInfo)b.getCurrentRoot();
230             }
231             controller.registerDocument(newdoc, documentKey);
232             return getFragment(newdoc, fragmentId, c);
233
234         } catch (TransformerException JavaDoc err) {
235             DynamicError xerr = DynamicError.makeDynamicError(err);
236             xerr.setLocator(locator);
237             xerr.setErrorCode("FODC0005");
238             try {
239                 controller.recoverableError(xerr);
240             } catch (XPathException err2) {
241                 throw new DynamicError(err);
242             }
243             return null;
244         }
245     }
246
247     /**
248      * Copy the documents identified by this expression to a given Receiver. This method is used only when it is
249      * known that the documents are being copied, because there is then no problem about node identity.
250      */

251
252     public void sendDocuments(XPathContext context, Receiver out) throws XPathException {
253         SequenceIterator hrefSequence = argument[0].iterate(context);
254         String JavaDoc explicitBaseURI = null;
255         if (argument.length==2) {
256             // we can trust the type checking: it must be a node
257
NodeInfo base = (NodeInfo)argument[1].evaluateItem(context);
258             explicitBaseURI = base.getBaseURI();
259         }
260         while (true) {
261             Item href = hrefSequence.next();
262             if (href == null) {
263                 break;
264             }
265             String JavaDoc base;
266             if (explicitBaseURI == null) {
267                 if (href instanceof NodeInfo) {
268                     base = ((NodeInfo)href).getBaseURI();
269                 } else {
270                     base = expressionBaseURI;
271                 }
272             } else {
273                 base = explicitBaseURI;
274             }
275             sendDoc(href.getStringValue(), base, context, this, out);
276         }
277     }
278
279     /**
280      * Supporting routine to push one external document given a URI (href) and a baseURI to a given Receiver.
281      * This method cannot handle fragment identifiers
282     */

283
284     public static void sendDoc(String JavaDoc href, String JavaDoc baseURL, XPathContext c, SourceLocator JavaDoc locator, Receiver out) throws XPathException {
285
286         PipelineConfiguration pipe = out.getPipelineConfiguration();
287         if (pipe == null) {
288             pipe = c.getController().makePipelineConfiguration();
289         }
290
291         // Resolve relative URI
292

293         String JavaDoc documentKey;
294         if (baseURL==null) { // no base URI available
295
try {
296                 // the href might be an absolute URL
297
documentKey = (new URI JavaDoc(href)).toString();
298             } catch (URISyntaxException JavaDoc err) {
299                 // it isn't; but the URI resolver might know how to cope
300
documentKey = '/' + href;
301                 baseURL = "";
302             }
303         } else if (href.equals("")) {
304             // common case in XSLT, which java.net.URI#resolve() does not handle correctly
305
documentKey = baseURL;
306         } else {
307             try {
308                 URI JavaDoc url = new URI JavaDoc(baseURL).resolve(href);
309                 documentKey = url.toString();
310             } catch (URISyntaxException JavaDoc err) {
311                 documentKey = baseURL + "/../" + href;
312             } catch (IllegalArgumentException JavaDoc err) {
313                 documentKey = baseURL + "/../" + href;
314             }
315         }
316
317         Controller controller = c.getController();
318
319         // see if the document is already loaded
320

321         DocumentInfo doc = controller.getDocumentPool().find(documentKey);
322         Source JavaDoc source = null;
323         if (doc != null) {
324             source = doc;
325         } else {
326
327             try {
328                 // Get a Source from the URIResolver
329

330                 URIResolver JavaDoc r = controller.getURIResolver();
331                 if (r != null) {
332                     source = r.resolve(href, baseURL);
333                 }
334
335                 // if a user URI resolver returns null, try the standard one
336
// (Note, the standard URI resolver never returns null)
337
if (source==null) {
338                     r = controller.getStandardURIResolver();
339                     source = r.resolve(href, baseURL);
340                 }
341                 if (source instanceof NodeInfo || source instanceof DOMSource JavaDoc) {
342                     NodeInfo startNode = controller.prepareInputTree(source);
343                     source = startNode.getDocumentRoot();
344                 }
345             } catch (TransformerException JavaDoc err) {
346                 DynamicError xerr = DynamicError.makeDynamicError(err);
347                 xerr.setLocator(locator);
348                 xerr.setErrorCode("FODC0005");
349                 throw xerr;
350             }
351         }
352         out = controller.makeStripper(out);
353         out.setPipelineConfiguration(pipe);
354         if (controller.getExecutable().stripsInputTypeAnnotations()) {
355             out = controller.getConfiguration().getAnnotationStripper(out);
356             out.setPipelineConfiguration(pipe);
357         }
358         new Sender(pipe).send(source, out);
359     }
360
361
362     /**
363     * Resolve the fragment identifier within a URI Reference.
364     * Only "bare names" XPointers are recognized, that is, a fragment identifier
365     * that matches an ID attribute value within the target document.
366     * @return the element within the supplied document that matches the
367     * given id value; or null if no such element is found.
368     */

369
370     private static NodeInfo getFragment(DocumentInfo doc, String JavaDoc fragmentId, XPathContext context)
371     throws XPathException {
372         // TODO: we only support one kind of fragment identifier. The rules say
373
// that the interpretation of the fragment identifier depends on media type,
374
// but we aren't getting the media type from the URIResolver.
375
if (fragmentId==null) {
376             return doc;
377         }
378         if (!XMLChar.isValidNCName(fragmentId)) {
379             DynamicError err = new DynamicError("Invalid fragment identifier in URI");
380             err.setXPathContext(context);
381             err.setErrorCode("XTRE1160");
382             try {
383                 context.getController().recoverableError(err);
384             } catch (DynamicError dynamicError) {
385                 throw err;
386             }
387             return doc;
388         }
389         return doc.selectID(fragmentId);
390     }
391
392
393 }
394
395
396
397
398
399 //
400
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
401
// you may not use this file except in compliance with the License. You may obtain a copy of the
402
// License at http://www.mozilla.org/MPL/
403
//
404
// Software distributed under the License is distributed on an "AS IS" basis,
405
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
406
// See the License for the specific language governing rights and limitations under the License.
407
//
408
// The Original Code is: all this file.
409
//
410
// The Initial Developer of the Original Code is Michael H. Kay.
411
//
412
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
413
//
414
// Contributor(s): none.
415
//
416
Popular Tags