KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.functions;
2 import net.sf.saxon.Controller;
3 import net.sf.saxon.expr.*;
4 import net.sf.saxon.om.*;
5 import net.sf.saxon.sort.DocumentOrderIterator;
6 import net.sf.saxon.sort.LocalOrderComparer;
7 import net.sf.saxon.style.ExpressionContext;
8 import net.sf.saxon.trans.KeyManager;
9 import net.sf.saxon.trans.StaticError;
10 import net.sf.saxon.trans.XPathException;
11 import net.sf.saxon.value.AtomicValue;
12 import net.sf.saxon.value.Cardinality;
13 import net.sf.saxon.value.StringValue;
14
15
16 public class KeyFn extends SystemFunction implements XSLTFunction {
17
18     private NamespaceResolver nsContext = null;
19     private int keyFingerprint = -1;
20     private transient boolean checked = false;
21     private transient boolean internal = false;
22         // the second time checkArguments is called, it's a global check so the static context is inaccurate
23

24     /**
25      * Non-standard constructor to create an internal call on key() with a known key fingerprint
26      */

27
28     public static KeyFn internalKeyCall(NamePool pool, int fingerprint, String JavaDoc name, Expression value, Expression doc) {
29         KeyFn k = new KeyFn();
30         Expression[] arguments = {new StringValue(name), value, doc};
31         k.argument = arguments;
32         k.keyFingerprint= fingerprint;
33         k.checked = true;
34         k.internal = true;
35         k.setDetails(StandardFunction.getFunction("key", 3));
36         k.setFunctionNameCode(pool.allocate("fn", NamespaceConstant.FN, "key"));
37         return k;
38     }
39
40     /**
41     * Simplify: add a third implicit argument, the context document
42     */

43
44      public Expression simplify(StaticContext env) throws XPathException {
45         if (!internal && !(env instanceof ExpressionContext)) {
46             throw new StaticError("The key() function is available only in XPath expressions within an XSLT stylesheet");
47         }
48         KeyFn f = (KeyFn)super.simplify(env);
49         if (argument.length == 2) {
50             f.addContextDocumentArgument(2, "key");
51         }
52         return f;
53     }
54
55     public void checkArguments(StaticContext env) throws XPathException {
56         if (checked) return;
57         checked = true;
58         super.checkArguments(env);
59         Optimizer opt = env.getConfiguration().getOptimizer();
60         argument[1] = ExpressionTool.unsorted(opt, argument[1], false);
61         if (argument[0] instanceof StringValue) {
62             // common case, key name is supplied as a constant
63
try {
64                 keyFingerprint = ((ExpressionContext)env).getFingerprint(((StringValue)argument[0]).getStringValue(), false);
65             } catch (XPathException e) {
66                 StaticError err = new StaticError("Error in key name " +
67                         ((StringValue)argument[0]).getStringValue() + ": " + e.getMessage());
68                 err.setLocator(this);
69                 err.setErrorCode("XTDE1260");
70                 throw err;
71             }
72             if (keyFingerprint==-1) {
73                 StaticError err = new StaticError("Key " +
74                         ((StringValue)argument[0]).getStringValue() + " has not been defined");
75                 err.setLocator(this);
76                 err.setErrorCode("XTDE1260");
77                 throw err;
78             }
79         } else {
80             // we need to save the namespace context
81
nsContext = env.getNamespaceResolver();
82         }
83     }
84
85     /**
86     * Get the static properties of this expression (other than its type). The result is
87     * bit-signficant. These properties are used for optimizations. In general, if
88     * a property bit is set, it is true, but if it is unset, the value is unknown.
89     */

90
91     public int computeSpecialProperties() {
92         int prop = StaticProperty.ORDERED_NODESET |
93                 StaticProperty.SINGLE_DOCUMENT_NODESET |
94                 StaticProperty.NON_CREATIVE;
95         if ((getNumberOfArguments() == 2) ||
96                 (argument[2].getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
97             prop |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
98         }
99         return prop;
100     }
101
102     /**
103     * preEvaluate: this method suppresses compile-time evaluation by doing nothing
104     */

105
106     public Expression preEvaluate(StaticContext env) {
107         return this;
108     }
109
110     /**
111     * Enumerate the results of the expression
112     */

113
114     public SequenceIterator iterate(XPathContext context) throws XPathException {
115
116         Controller controller = context.getController();
117
118         Item arg2 = argument[2].evaluateItem(context);
119         if (!(arg2 instanceof NodeInfo)) {
120             dynamicError("When calling the key() function, the context item must be a node", "XTDE1270", context);
121             return null;
122         }
123         NodeInfo origin = (NodeInfo)arg2;
124         NodeInfo root = origin.getRoot();
125         if (!(root instanceof DocumentInfo)) {
126             dynamicError("In the key() function," +
127                             " the node supplied in the third argument (or the context node if absent)" +
128                             " must be in a tree whose root is a document node", "XTDE1270", context);
129             return null;
130         }
131         DocumentInfo doc = (DocumentInfo)root;
132
133         int fprint = keyFingerprint;
134         if (fprint == -1) {
135             String JavaDoc givenkeyname = argument[0].evaluateItem(context).getStringValue();
136             try {
137                 fprint = context.getController().getNamePool().allocateLexicalQName(
138                         givenkeyname, false, nsContext) & NamePool.FP_MASK;
139             } catch (XPathException err) {
140                 dynamicError("Invalid key name: " + err.getMessage(), "XTDE1260", context);
141             }
142             if (fprint==-1) {
143                 dynamicError("Key '" + givenkeyname + "' has not been defined", "XTDE1260", context);
144                 return null;
145             }
146         }
147
148 // if (internal) {
149
// System.err.println("Using key " + fprint + " on doc " + doc);
150
// }
151

152         // If the second argument is a singleton, we evaluate the function
153
// directly; otherwise we recurse to evaluate it once for each Item
154
// in the sequence.
155

156         Expression expression = argument[1];
157         SequenceIterator allResults;
158         if (Cardinality.allowsMany(expression.getCardinality())) {
159             KeyMappingFunction map = new KeyMappingFunction();
160             map.document = doc;
161             map.keyContext = context;
162             map.keyFingerprint = fprint;
163
164             SequenceIterator keys = argument[1].iterate(context);
165             SequenceIterator allValues = new MappingIterator(keys, map, null);
166             allResults = new DocumentOrderIterator(allValues, LocalOrderComparer.getInstance());
167         } else {
168             AtomicValue keyValue = (AtomicValue)argument[1].evaluateItem(context);
169             if (keyValue == null) {
170                 return EmptyIterator.getInstance();
171             }
172             KeyManager keyManager = controller.getKeyManager();
173             allResults = keyManager.selectByKey(fprint, doc, keyValue, context);
174         }
175         if (origin == doc) {
176             return allResults;
177         }
178         SubtreeFilter filter = new SubtreeFilter();
179         filter.origin = origin;
180         return new MappingIterator(allResults, filter, null);
181     }
182
183
184
185     /**
186     * Implement the MappingFunction interface
187     */

188
189     private static class KeyMappingFunction implements MappingFunction {
190
191         public XPathContext keyContext;
192         public int keyFingerprint;
193         public DocumentInfo document;
194
195         public Object JavaDoc map(Item item, XPathContext context) throws XPathException {
196             KeyManager keyManager = keyContext.getController().getKeyManager();
197             return keyManager.selectByKey(
198                     keyFingerprint, document, (AtomicValue)item, keyContext);
199         }
200     }
201
202     /**
203      * Mapping class to filter nodes that have the origin node as an ancestor-or-self
204      */

205
206     private static class SubtreeFilter implements MappingFunction {
207
208         public NodeInfo origin;
209
210         // TODO: much more efficient implementations are possible, especially with the TinyTree
211

212         public Object JavaDoc map(Item item, XPathContext context) throws XPathException {
213             if (isAncestorOrSelf(origin, (NodeInfo)item)) {
214                 return item;
215             } else {
216                 return null;
217             }
218         }
219
220         /**
221          * Test if one node is an ancestor-or-self of another
222          * @param a the putative ancestor-or-self node
223          * @param d the putative descendant node
224          * @return true if a is an ancestor-or-self of d
225          */

226
227         private static boolean isAncestorOrSelf(NodeInfo a, NodeInfo d) {
228             NodeInfo p = d;
229             while (p != null) {
230                 if (a.isSameNodeInfo(p)) {
231                     return true;
232                 }
233                 p = p.getParent();
234             }
235             return false;
236         }
237     }
238
239 }
240
241
242
243
244
245 //
246
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
247
// you may not use this file except in compliance with the License. You may obtain a copy of the
248
// License at http://www.mozilla.org/MPL/
249
//
250
// Software distributed under the License is distributed on an "AS IS" basis,
251
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
252
// See the License for the specific language governing rights and limitations under the License.
253
//
254
// The Original Code is: all this file.
255
//
256
// The Initial Developer of the Original Code is Michael H. Kay.
257
//
258
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
259
//
260
// Contributor(s): none.
261
//
262
Popular Tags