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 24 27 28 public static KeyFn internalKeyCall(NamePool pool, int fingerprint, String 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 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 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 nsContext = env.getNamespaceResolver(); 82 } 83 } 84 85 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 105 106 public Expression preEvaluate(StaticContext env) { 107 return this; 108 } 109 110 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 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 152 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 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 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 205 206 private static class SubtreeFilter implements MappingFunction { 207 208 public NodeInfo origin; 209 210 212 public Object 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 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 | Popular Tags |