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