Cleanup use of Guava library
[yangtools.git] / yang / yang-data-jaxen / src / main / java / org / opendaylight / yangtools / yang / data / jaxen / YangFunctionContext.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.jaxen;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Splitter;
12 import com.google.common.base.Verify;
13 import java.util.ArrayDeque;
14 import java.util.Deque;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.Set;
20 import org.jaxen.ContextSupport;
21 import org.jaxen.Function;
22 import org.jaxen.FunctionCallException;
23 import org.jaxen.FunctionContext;
24 import org.jaxen.JaxenRuntimeException;
25 import org.jaxen.UnresolvableException;
26 import org.jaxen.UnsupportedAxisException;
27 import org.jaxen.XPathFunctionContext;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
38 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.Module;
40 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
41 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
47 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
48 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
51 import org.opendaylight.yangtools.yang.model.util.RegexUtils;
52 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
53
54 /**
55  * A {@link FunctionContext} which contains also YANG-specific functions current(), re-match(), deref(),
56  * derived-from(), derived-from-or-self(), enum-value() and bit-is-set().
57  */
58 final class YangFunctionContext implements FunctionContext {
59     private static final Splitter COLON_SPLITTER = Splitter.on(':');
60     private static final Double DOUBLE_NAN = Double.NaN;
61
62     // Core XPath functions, as per http://tools.ietf.org/html/rfc6020#section-6.4.1
63     private static final FunctionContext XPATH_FUNCTION_CONTEXT = new XPathFunctionContext(false);
64     // current() function, as per http://tools.ietf.org/html/rfc6020#section-6.4.1
65     private static final Function CURRENT_FUNCTION = (context, args) -> {
66         if (!args.isEmpty()) {
67             throw new FunctionCallException("current() takes no arguments.");
68         }
69
70         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
71         return (NormalizedNodeContext) context;
72     };
73
74     // re-match(string subject, string pattern) function as per https://tools.ietf.org/html/rfc7950#section-10.2.1
75     private static final Function REMATCH_FUNCTION = (context, args) -> {
76         if (args == null || args.size() != 2) {
77             throw new FunctionCallException("re-match() takes two arguments: string subject, string pattern.");
78         }
79
80         if (!(args.get(0) instanceof String)) {
81             throw new FunctionCallException("First argument of re-match() should be a String.");
82         }
83
84         if (!(args.get(1) instanceof String)) {
85             throw new FunctionCallException("Second argument of re-match() should be a String.");
86         }
87
88         final String subject = (String) args.get(0);
89         final String rawPattern = (String) args.get(1);
90
91         final String pattern = RegexUtils.getJavaRegexFromXSD(rawPattern);
92
93         return (Boolean) subject.matches(pattern);
94     };
95
96     // deref(node-set nodes) function as per https://tools.ietf.org/html/rfc7950#section-10.3.1
97     private static final Function DEREF_FUNCTION = (context, args) -> {
98         if (!args.isEmpty()) {
99             throw new FunctionCallException("deref() takes only one argument: node-set nodes.");
100         }
101
102         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
103
104         final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
105         final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
106         final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext,
107                 currentNodeContext);
108
109         final Object nodeValue = currentNodeContext.getNode().getValue();
110
111         if (correspondingSchemaNode.getType() instanceof InstanceIdentifierTypeDefinition
112                 && nodeValue instanceof YangInstanceIdentifier) {
113             return getNodeReferencedByInstanceIdentifier((YangInstanceIdentifier) nodeValue, currentNodeContext);
114         }
115
116         if (correspondingSchemaNode.getType() instanceof LeafrefTypeDefinition) {
117             final LeafrefTypeDefinition leafrefType = (LeafrefTypeDefinition) correspondingSchemaNode.getType();
118             final RevisionAwareXPath xpath = leafrefType.getPathStatement();
119             return getNodeReferencedByLeafref(xpath, currentNodeContext, schemaContext, correspondingSchemaNode,
120                     nodeValue);
121         }
122
123         return null;
124     };
125
126     private static NormalizedNode<?, ?> getNodeReferencedByInstanceIdentifier(final YangInstanceIdentifier path,
127             final NormalizedNodeContext currentNodeContext) {
128         final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator();
129         final NormalizedNode<?, ?> rootNode = navigator.getRootNode();
130         final List<PathArgument> pathArguments = path.getPathArguments();
131         if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) {
132             final List<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
133             final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(rootNode, relPath);
134             if (possibleNode.isPresent()) {
135                 return possibleNode.get();
136             }
137         }
138
139         return null;
140     }
141
142     private static NormalizedNode<?, ?> getNodeReferencedByLeafref(final RevisionAwareXPath xpath,
143             final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
144             final TypedSchemaNode correspondingSchemaNode, final Object nodeValue) {
145         final NormalizedNode<?, ?> referencedNode = xpath.isAbsolute() ? getNodeReferencedByAbsoluteLeafref(xpath,
146                 currentNodeContext, schemaContext, correspondingSchemaNode) : getNodeReferencedByRelativeLeafref(xpath,
147                 currentNodeContext, schemaContext, correspondingSchemaNode);
148
149         if (referencedNode instanceof LeafSetNode) {
150             return getReferencedLeafSetEntryNode((LeafSetNode<?>) referencedNode, nodeValue);
151         }
152
153         if (referencedNode instanceof LeafNode && referencedNode.getValue().equals(nodeValue)) {
154             return referencedNode;
155         }
156
157         return null;
158     }
159
160     private static NormalizedNode<?, ?> getNodeReferencedByAbsoluteLeafref(final RevisionAwareXPath xpath,
161             final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
162             final TypedSchemaNode correspondingSchemaNode) {
163         final LeafrefXPathStringParsingPathArgumentBuilder builder = new LeafrefXPathStringParsingPathArgumentBuilder(
164                 xpath.toString(), schemaContext, correspondingSchemaNode, currentNodeContext);
165         final List<PathArgument> pathArguments = builder.build();
166         final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator();
167         final NormalizedNode<?, ?> rootNode = navigator.getRootNode();
168         if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) {
169             final List<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
170             final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(rootNode, relPath);
171             if (possibleNode.isPresent()) {
172                 return possibleNode.get();
173             }
174         }
175
176         return null;
177     }
178
179     private static NormalizedNode<?, ?> getNodeReferencedByRelativeLeafref(final RevisionAwareXPath xpath,
180             final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
181             final TypedSchemaNode correspondingSchemaNode) {
182         NormalizedNodeContext relativeNodeContext = currentNodeContext;
183         final StringBuilder xPathStringBuilder = new StringBuilder(xpath.toString());
184         // strip the relative path of all ../ at the beginning
185         while (xPathStringBuilder.indexOf("../") == 0) {
186             xPathStringBuilder.delete(0, 3);
187             relativeNodeContext = relativeNodeContext.getParent();
188         }
189
190         // add / to the beginning of the path so that it can be processed the same way as an absolute path
191         xPathStringBuilder.insert(0, '/');
192         final LeafrefXPathStringParsingPathArgumentBuilder builder = new LeafrefXPathStringParsingPathArgumentBuilder(
193                 xPathStringBuilder.toString(), schemaContext, correspondingSchemaNode, currentNodeContext);
194         final List<PathArgument> pathArguments = builder.build();
195         final NormalizedNode<?, ?> relativeNode = relativeNodeContext.getNode();
196         final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(relativeNode, pathArguments);
197         if (possibleNode.isPresent()) {
198             return possibleNode.get();
199         }
200
201         return null;
202     }
203
204     private static LeafSetEntryNode<?> getReferencedLeafSetEntryNode(final LeafSetNode<?> referencedNode,
205             final Object currentNodeValue) {
206         for (final LeafSetEntryNode<?> entryNode : referencedNode.getValue()) {
207             if (currentNodeValue.equals(entryNode.getValue())) {
208                 return entryNode;
209             }
210         }
211
212         return null;
213     }
214
215     // derived-from(node-set nodes, string identity) function as per https://tools.ietf.org/html/rfc7950#section-10.4.1
216     private static final Function DERIVED_FROM_FUNCTION = (context, args) -> {
217         if (args == null || args.size() != 1) {
218             throw new FunctionCallException("derived-from() takes two arguments: node-set nodes, string identity.");
219         }
220
221         if (!(args.get(0) instanceof String)) {
222             throw new FunctionCallException("Argument 'identity' of derived-from() function should be a String.");
223         }
224
225         final String identityArg = (String) args.get(0);
226
227         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
228
229         final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
230         final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
231         final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext,
232             currentNodeContext);
233
234         if (!(correspondingSchemaNode.getType() instanceof IdentityrefTypeDefinition)) {
235             return Boolean.FALSE;
236         }
237
238         if (!(currentNodeContext.getNode().getValue() instanceof QName)) {
239             return Boolean.FALSE;
240         }
241
242         final QName currentNodeValue = (QName) currentNodeContext.getNode().getValue();
243
244         final IdentitySchemaNode identityArgSchemaNode = getIdentitySchemaNodeFromString(identityArg, schemaContext,
245                 correspondingSchemaNode);
246         final IdentitySchemaNode currentNodeIdentitySchemaNode = getIdentitySchemaNodeFromQName(currentNodeValue,
247                 schemaContext);
248
249         final Set<IdentitySchemaNode> ancestorIdentities = new HashSet<>();
250         collectAncestorIdentities(currentNodeIdentitySchemaNode, ancestorIdentities);
251
252         return Boolean.valueOf(ancestorIdentities.contains(identityArgSchemaNode));
253     };
254
255     // derived-from-or-self(node-set nodes, string identity) function as per
256     // https://tools.ietf.org/html/rfc7950#section-10.4.2
257     private static final Function DERIVED_FROM_OR_SELF_FUNCTION = (context, args) -> {
258         if (args == null || args.size() != 1) {
259             throw new FunctionCallException(
260                 "derived-from-or-self() takes two arguments: node-set nodes, string identity");
261         }
262
263         if (!(args.get(0) instanceof String)) {
264             throw new FunctionCallException(
265                 "Argument 'identity' of derived-from-or-self() function should be a String.");
266         }
267
268         final String identityArg = (String) args.get(0);
269
270         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
271
272         final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
273         final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
274         final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext,
275             currentNodeContext);
276
277         if (!(correspondingSchemaNode.getType() instanceof IdentityrefTypeDefinition)) {
278             return Boolean.FALSE;
279         }
280
281         if (!(currentNodeContext.getNode().getValue() instanceof QName)) {
282             return Boolean.FALSE;
283         }
284
285         final QName currentNodeValue = (QName) currentNodeContext.getNode().getValue();
286
287         final IdentitySchemaNode identityArgSchemaNode = getIdentitySchemaNodeFromString(identityArg, schemaContext,
288                 correspondingSchemaNode);
289         final IdentitySchemaNode currentNodeIdentitySchemaNode = getIdentitySchemaNodeFromQName(currentNodeValue,
290                 schemaContext);
291         if (currentNodeIdentitySchemaNode.equals(identityArgSchemaNode)) {
292             return Boolean.TRUE;
293         }
294
295         final Set<IdentitySchemaNode> ancestorIdentities = new HashSet<>();
296         collectAncestorIdentities(currentNodeIdentitySchemaNode, ancestorIdentities);
297
298         return Boolean.valueOf(ancestorIdentities.contains(identityArgSchemaNode));
299     };
300
301     private static void collectAncestorIdentities(final IdentitySchemaNode identity,
302             final Set<IdentitySchemaNode> ancestorIdentities) {
303         for (final IdentitySchemaNode id : identity.getBaseIdentities()) {
304             collectAncestorIdentities(id, ancestorIdentities);
305             ancestorIdentities.add(id);
306         }
307     }
308
309     private static IdentitySchemaNode getIdentitySchemaNodeFromQName(final QName identityQName,
310             final SchemaContext schemaContext) {
311         final Module module = schemaContext.findModuleByNamespaceAndRevision(identityQName.getNamespace(),
312                 identityQName.getRevision());
313         return findIdentitySchemaNodeInModule(module, identityQName);
314     }
315
316     private static IdentitySchemaNode getIdentitySchemaNodeFromString(final String identity,
317             final SchemaContext schemaContext, final TypedSchemaNode correspondingSchemaNode) {
318         final List<String> identityPrefixAndName = COLON_SPLITTER.splitToList(identity);
319         final Module module = schemaContext.findModuleByNamespaceAndRevision(
320                 correspondingSchemaNode.getQName().getNamespace(), correspondingSchemaNode.getQName().getRevision());
321         if (identityPrefixAndName.size() == 2) {
322             // prefix of local module
323             if (identityPrefixAndName.get(0).equals(module.getPrefix())) {
324                 return findIdentitySchemaNodeInModule(module, QName.create(module.getQNameModule(),
325                         identityPrefixAndName.get(1)));
326             }
327
328             // prefix of imported module
329             for (final ModuleImport moduleImport : module.getImports()) {
330                 if (identityPrefixAndName.get(0).equals(moduleImport.getPrefix())) {
331                     final Module importedModule = schemaContext.findModuleByName(moduleImport.getModuleName(),
332                         moduleImport.getRevision());
333                     return findIdentitySchemaNodeInModule(importedModule, QName.create(
334                         importedModule.getQNameModule(), identityPrefixAndName.get(1)));
335                 }
336             }
337
338             throw new IllegalArgumentException(String.format("Cannot resolve prefix '%s' from identity '%s'.",
339                     identityPrefixAndName.get(0), identity));
340         }
341
342         if (identityPrefixAndName.size() == 1) { // without prefix
343             return findIdentitySchemaNodeInModule(module, QName.create(module.getQNameModule(),
344                     identityPrefixAndName.get(0)));
345         }
346
347         throw new IllegalArgumentException(String.format("Malformed identity argument: %s.", identity));
348     }
349
350     private static IdentitySchemaNode findIdentitySchemaNodeInModule(final Module module, final QName identityQName) {
351         for (final IdentitySchemaNode id : module.getIdentities()) {
352             if (identityQName.equals(id.getQName())) {
353                 return id;
354             }
355         }
356
357         throw new IllegalArgumentException(String.format("Identity %s does not have a corresponding"
358                     + " identity schema node in the module %s.", identityQName, module));
359     }
360
361     // enum-value(node-set nodes) function as per https://tools.ietf.org/html/rfc7950#section-10.5.1
362     private static final Function ENUM_VALUE_FUNCTION = (context, args) -> {
363         if (!args.isEmpty()) {
364             throw new FunctionCallException("enum-value() takes one argument: node-set nodes.");
365         }
366
367         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
368
369         final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
370         final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
371         final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext,
372             currentNodeContext);
373
374         final TypeDefinition<?> nodeType = correspondingSchemaNode.getType();
375         if (!(nodeType instanceof EnumTypeDefinition)) {
376             return DOUBLE_NAN;
377         }
378
379         final Object nodeValue = currentNodeContext.getNode().getValue();
380         if (!(nodeValue instanceof String)) {
381             return DOUBLE_NAN;
382         }
383
384         final EnumTypeDefinition enumerationType = (EnumTypeDefinition) nodeType;
385         final String enumName = (String) nodeValue;
386
387         return getEnumValue(enumerationType, enumName);
388     };
389
390     private static int getEnumValue(final EnumTypeDefinition enumerationType, final String enumName) {
391         for (final EnumTypeDefinition.EnumPair enumPair : enumerationType.getValues()) {
392             if (enumName.equals(enumPair.getName())) {
393                 return enumPair.getValue();
394             }
395         }
396
397         throw new IllegalStateException(String.format("Enum %s does not belong to enumeration %s.",
398                 enumName, enumerationType));
399     }
400
401     // bit-is-set(node-set nodes, string bit-name) function as per
402     // https://tools.ietf.org/html/rfc7950#section-10.6.1
403     private static final Function BIT_IS_SET_FUNCTION = (context, args) -> {
404         if (args == null || args.size() != 1) {
405             throw new FunctionCallException("bit-is-set() takes two arguments: node-set nodes, string bit-name");
406         }
407
408         if (!(args.get(0) instanceof String)) {
409             throw new FunctionCallException("Argument bit-name of bit-is-set() function should be a String");
410         }
411
412         final String bitName = (String) args.get(0);
413
414         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
415
416         final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
417         final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
418         final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext,
419             currentNodeContext);
420
421         final TypeDefinition<?> nodeType = correspondingSchemaNode.getType();
422         if (!(nodeType instanceof BitsTypeDefinition)) {
423             return Boolean.FALSE;
424         }
425
426         final Object nodeValue = currentNodeContext.getNode().getValue();
427         if (!(nodeValue instanceof Set)) {
428             return Boolean.FALSE;
429         }
430
431         final BitsTypeDefinition bitsType = (BitsTypeDefinition) nodeType;
432         Preconditions.checkState(containsBit(bitsType, bitName), "Bit %s does not belong to bits %s.", bitName,
433             bitsType);
434         return Boolean.valueOf(((Set<?>)nodeValue).contains(bitName));
435     };
436
437     private static boolean containsBit(final BitsTypeDefinition bitsType, final String bitName) {
438         for (BitsTypeDefinition.Bit bit : bitsType.getBits()) {
439             if (bitName.equals(bit.getName())) {
440                 return true;
441             }
442         }
443
444         return false;
445     }
446
447     private static SchemaContext getSchemaContext(final NormalizedNodeContext normalizedNodeContext) {
448         final ContextSupport contextSupport = normalizedNodeContext.getContextSupport();
449         Verify.verify(contextSupport instanceof NormalizedNodeContextSupport, "Unhandled context support %s",
450                 contextSupport.getClass());
451         return ((NormalizedNodeContextSupport) contextSupport).getSchemaContext();
452     }
453
454     private static TypedSchemaNode getCorrespondingTypedSchemaNode(final SchemaContext schemaContext,
455             final NormalizedNodeContext currentNodeContext) {
456         Iterator<NormalizedNodeContext> ancestorOrSelfAxisIterator;
457         try {
458             ancestorOrSelfAxisIterator = currentNodeContext.getContextSupport().getNavigator()
459                     .getAncestorOrSelfAxisIterator(currentNodeContext);
460         } catch (UnsupportedAxisException ex) {
461             throw new JaxenRuntimeException(ex);
462         }
463
464         final Deque<QName> schemaPathToCurrentNode = new ArrayDeque<>();
465         while (ancestorOrSelfAxisIterator.hasNext()) {
466             final NormalizedNode<?, ?> nextNode = ancestorOrSelfAxisIterator.next().getNode();
467             if (!(nextNode instanceof MapNode) && !(nextNode instanceof LeafSetNode)
468                     && !(nextNode instanceof AugmentationNode)) {
469                 schemaPathToCurrentNode.addFirst(nextNode.getNodeType());
470             }
471         }
472
473         final SchemaNode schemaNode = SchemaContextUtil.findNodeInSchemaContext(schemaContext, schemaPathToCurrentNode);
474
475         Preconditions.checkNotNull(schemaNode, "Node %s does not have a corresponding SchemaNode in the SchemaContext.",
476                 currentNodeContext.getNode());
477         Preconditions.checkState(schemaNode instanceof TypedSchemaNode, "Node %s must be a leaf or a leaf-list.",
478                 currentNodeContext.getNode());
479         return (TypedSchemaNode) schemaNode;
480     }
481
482     // Singleton instance of reuse
483     private static final YangFunctionContext INSTANCE = new YangFunctionContext();
484
485     private YangFunctionContext() {
486     }
487
488     static YangFunctionContext getInstance() {
489         return INSTANCE;
490     }
491
492     @Override
493     public Function getFunction(final String namespaceURI, final String prefix, final String localName)
494             throws UnresolvableException {
495         if (prefix == null) {
496             switch (localName) {
497                 case "bit-is-set":
498                     return BIT_IS_SET_FUNCTION;
499                 case "current":
500                     return CURRENT_FUNCTION;
501                 case "deref":
502                     return DEREF_FUNCTION;
503                 case "derived-from":
504                     return DERIVED_FROM_FUNCTION;
505                 case "derived-from-or-self":
506                     return DERIVED_FROM_OR_SELF_FUNCTION;
507                 case "enum-value":
508                     return ENUM_VALUE_FUNCTION;
509                 case "re-match":
510                     return REMATCH_FUNCTION;
511                 default:
512                     break;
513             }
514         }
515
516         return XPATH_FUNCTION_CONTEXT.getFunction(namespaceURI, prefix, localName);
517     }
518 }