From 26e5a7d6b7872dac5143aa796a8e1f8b550ce929 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 27 Apr 2018 22:46:32 +0200 Subject: [PATCH] Jaxen binding should use DataSchemaContextTree Context tree is a simple structure which allows traversal of nodes in the order of NormalizedNodes in a strictly-forward way. This allows us to not muck with SchemaContext when we need to look up our SchemaNode, as it is readily available. Change-Id: Ic6c67c1e064ffe74433debd4f1a63e24a8571791 Signed-off-by: Robert Varga --- .../yang/data/jaxen/JaxenDocument.java | 18 ++++-- .../yang/data/jaxen/JaxenSchemaContext.java | 11 ++-- .../data/jaxen/NormalizedNodeContext.java | 50 ++++++++++++--- .../jaxen/NormalizedNodeContextSupport.java | 13 ++-- .../data/jaxen/NormalizedNodeNavigator.java | 27 ++++---- .../yang/data/jaxen/YangFunctionContext.java | 62 ++++--------------- 6 files changed, 90 insertions(+), 91 deletions(-) diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java index eb7cdd5357..ead6ae4049 100644 --- a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java +++ b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java @@ -9,28 +9,36 @@ package org.opendaylight.yangtools.yang.data.jaxen; import static java.util.Objects.requireNonNull; -import javax.annotation.Nonnull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +@NonNullByDefault final class JaxenDocument implements XPathDocument { + private final DataSchemaContextNode schema; private final NormalizedNode root; private final SchemaContext context; - JaxenDocument(final JaxenSchemaContext context, final NormalizedNode root) { + JaxenDocument(final SchemaContext context, final DataSchemaContextTree tree, + final NormalizedNode root) { this.root = requireNonNull(root); - this.context = context.getSchemaContext(); + this.context = requireNonNull(context); + this.schema = requireNonNull(tree.getRoot().getChild(root.getIdentifier())); } - @Nonnull @Override public NormalizedNode getRootNode() { return root; } - @Nonnull SchemaContext getSchemaContext() { return context; } + + DataSchemaContextNode getSchema() { + return schema; + } } diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java index c6081f0ca9..ef8aed5566 100644 --- a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java +++ b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java @@ -18,15 +18,17 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument; import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathExpression; import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaPath; final class JaxenSchemaContext implements XPathSchemaContext { - // Will be needed for compileExpression() + private final DataSchemaContextTree tree; private final SchemaContext context; JaxenSchemaContext(final SchemaContext context) { this.context = requireNonNull(context); + this.tree = DataSchemaContextTree.from(context); } @Nonnull @@ -44,11 +46,6 @@ final class JaxenSchemaContext implements XPathSchemaContext { @Nonnull @Override public XPathDocument createDocument(@Nonnull final NormalizedNode documentRoot) { - return new JaxenDocument(this, documentRoot); - } - - @Nonnull - SchemaContext getSchemaContext() { - return context; + return new JaxenDocument(context, tree, documentRoot); } } diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java index 362d6f7e27..8b04b4355a 100644 --- a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java +++ b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java @@ -7,36 +7,49 @@ */ package org.opendaylight.yangtools.yang.data.jaxen; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.jaxen.Context; import org.jaxen.ContextSupport; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; /** * Context wrapper around a {@link NormalizedNode} for use with Jaxen. It tracks the parent node for purposes of * traversing upwards the NormalizedNode tree. */ +@NonNullByDefault final class NormalizedNodeContext extends Context implements Function, NormalizedNodeContext> { private static final long serialVersionUID = 1L; - private final NormalizedNodeContext parent; + private final @Nullable NormalizedNodeContext parent; + private final DataSchemaContextNode schema; private final NormalizedNode node; - NormalizedNodeContext(@Nonnull final ContextSupport contextSupport, @Nonnull final NormalizedNode node, - @Nullable final NormalizedNodeContext parent) { + private NormalizedNodeContext(final ContextSupport contextSupport, final DataSchemaContextNode schema, + final NormalizedNode node, final @Nullable NormalizedNodeContext parent) { super(contextSupport); + this.schema = requireNonNull(schema); this.node = requireNonNull(node); this.parent = parent; setNodeSet(ImmutableList.of(this)); } - @Nonnull NormalizedNode getNode() { + static NormalizedNodeContext forRoot(final NormalizedNodeContextSupport contextSupport) { + final JaxenDocument document = contextSupport.getNavigator().getDocument(); + return new NormalizedNodeContext(contextSupport, document.getSchema(), document.getRootNode(), null); + } + + NormalizedNode getNode() { return node; } @@ -44,12 +57,33 @@ final class NormalizedNodeContext extends Context implements Function getSchema() { + return schema; + } + @Override public NormalizedNodeContext apply(final NormalizedNode input) { - return new NormalizedNodeContext(getContextSupport(), input, this); + DataSchemaContextNode childSchema = schema.getChild(input.getIdentifier()); + if (childSchema == null) { + /* This feels very much like a hack: but solves lookup of child nodes with predicates. + * + * What is happening is that a Map context gets queried for its children, which results in contexts being + * backed by UnorderedMapMixinContextNode, which requires us to unmask it. + * + * When the predicate is being evaluated, each child is queried for its child -- but since it is protected, + * we cannot find it. + */ + final DataSchemaNode mySchema = schema.getDataSchemaNode(); + if (mySchema instanceof ListSchemaNode) { + childSchema = verifyNotNull(schema.getChild(mySchema.getQName())).getChild(input.getIdentifier()); + } + } + + checkArgument(childSchema != null, "Failed to find schema for child %s", input); + return new NormalizedNodeContext(getContextSupport(), childSchema, input, this); } } diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java index 441e7d672d..67c7376b54 100644 --- a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java +++ b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java @@ -27,7 +27,7 @@ final class NormalizedNodeContextSupport extends ContextSupport { private NormalizedNodeContextSupport(final ConverterNamespaceContext context, final NormalizedNodeNavigator navigator) { super(context, YangFunctionContext.getInstance(), new SimpleVariableContext(), navigator); - this.root = new NormalizedNodeContext(this, navigator.getRootNode(), null); + this.root = NormalizedNodeContext.forRoot(this); } static NormalizedNodeContextSupport create(final JaxenDocument document, @@ -43,18 +43,17 @@ final class NormalizedNodeContextSupport extends ContextSupport { for (PathArgument arg : path.getPathArguments()) { final Optional> node = NormalizedNodes.getDirectChild(result.getNode(), arg); checkArgument(node.isPresent(), "Node %s has no child %s", result.getNode(), arg); - result = new NormalizedNodeContext(this, node.get(), result); + result = result.apply(node.get()); } - return result; } - SchemaContext getSchemaContext() { - return getNavigator().getSchemaContext(); - } - @Override public NormalizedNodeNavigator getNavigator() { return (NormalizedNodeNavigator) super.getNavigator(); } + + SchemaContext getSchemaContext() { + return getNavigator().getDocument().getSchemaContext(); + } } diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java index 8c735e943f..2781edbdf9 100644 --- a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java +++ b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java @@ -15,13 +15,13 @@ import com.google.common.collect.Iterators; import com.google.common.collect.UnmodifiableIterator; import com.google.common.io.BaseEncoding; import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; import org.jaxen.DefaultNavigator; import org.jaxen.NamedAccessNavigator; import org.jaxen.Navigator; @@ -38,7 +38,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; /** * A {@link Navigator} implementation for YANG XPaths instantiated on a particular root {@link NormalizedNode}. @@ -240,16 +239,19 @@ final class NormalizedNodeNavigator extends DefaultNavigator implements NamedAcc return null; } - // The child may be a structural node final NormalizedNode child = maybeChild.get(); + final Collection> collection; + + // The child may be a structural node if (child instanceof MapNode) { - return Iterators.transform(((MapNode)child).getValue().iterator(), ctx); - } - if (child instanceof LeafSetNode) { - return Iterators.transform(((LeafSetNode)child).getValue().iterator(), ctx); + collection = ((MapNode)child).getValue(); + } else if (child instanceof LeafSetNode) { + collection = ((LeafSetNode)child).getValue(); + } else { + return Iterators.singletonIterator(ctx.apply(child)); } - return Iterators.singletonIterator(ctx.apply(child)); + return Iterators.transform(collection.iterator(), ctx); } @Override @@ -315,13 +317,8 @@ final class NormalizedNodeNavigator extends DefaultNavigator implements NamedAcc return cast(contextNode).getParent(); } - NormalizedNode getRootNode() { - return document.getRootNode(); - } - - @Nonnull - SchemaContext getSchemaContext() { - return document.getSchemaContext(); + JaxenDocument getDocument() { + return document; } private static final class NormalizedNodeContextIterator extends UnmodifiableIterator { diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java index 1c7d2fec61..3b3e51d210 100644 --- a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java +++ b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java @@ -11,9 +11,6 @@ import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.base.Verify; import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Optional; @@ -24,26 +21,22 @@ import org.jaxen.ContextSupport; import org.jaxen.Function; import org.jaxen.FunctionCallException; import org.jaxen.FunctionContext; -import org.jaxen.JaxenRuntimeException; import org.jaxen.UnresolvableException; -import org.jaxen.UnsupportedAxisException; import org.jaxen.XPathFunctionContext; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.ModuleImport; import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; @@ -52,7 +45,6 @@ import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.util.RegexUtils; -import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; /** * A {@link FunctionContext} which contains also YANG-specific functions current(), re-match(), deref(), @@ -118,9 +110,7 @@ final class YangFunctionContext implements FunctionContext { Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass()); final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context; - final SchemaContext schemaContext = getSchemaContext(currentNodeContext); - final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, - currentNodeContext); + final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext); final TypeDefinition nodeType = correspondingSchemaNode.getType(); if (!(nodeType instanceof BitsTypeDefinition)) { @@ -157,9 +147,7 @@ final class YangFunctionContext implements FunctionContext { Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass()); final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context; - final SchemaContext schemaContext = getSchemaContext(currentNodeContext); - final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, - currentNodeContext); + final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext); final Object nodeValue = currentNodeContext.getNode().getValue(); final TypeDefinition type = correspondingSchemaNode.getType(); @@ -170,15 +158,15 @@ final class YangFunctionContext implements FunctionContext { } if (type instanceof LeafrefTypeDefinition) { final RevisionAwareXPath xpath = ((LeafrefTypeDefinition) type).getPathStatement(); - return getNodeReferencedByLeafref(xpath, currentNodeContext, schemaContext, correspondingSchemaNode, - nodeValue); + return getNodeReferencedByLeafref(xpath, currentNodeContext, getSchemaContext(currentNodeContext), + correspondingSchemaNode, nodeValue); } return null; } // derived-from(node-set nodes, string identity) function as per https://tools.ietf.org/html/rfc7950#section-10.4.1 private static boolean derivedFrom(final Context context, final List args) throws FunctionCallException { - final Entry ids = commonDerivedFrom("derived-from", context, args); + final Entry ids = commonDerivedFrom("derived-from", context, args); return ids != null && isAncestorOf(ids.getKey(), ids.getValue()); } @@ -202,10 +190,9 @@ final class YangFunctionContext implements FunctionContext { Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass()); final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context; - final SchemaContext schemaContext = getSchemaContext(currentNodeContext); - final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, - currentNodeContext); + final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext); + final SchemaContext schemaContext = getSchemaContext(currentNodeContext); return correspondingSchemaNode.getType() instanceof IdentityrefTypeDefinition && currentNodeContext.getNode().getValue() instanceof QName ? new SimpleImmutableEntry<>( getIdentitySchemaNodeFromString((String) args.get(0), schemaContext, correspondingSchemaNode), @@ -222,9 +209,7 @@ final class YangFunctionContext implements FunctionContext { Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass()); final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context; - final SchemaContext schemaContext = getSchemaContext(currentNodeContext); - final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, - currentNodeContext); + final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext); final TypeDefinition nodeType = correspondingSchemaNode.getType(); if (!(nodeType instanceof EnumTypeDefinition)) { @@ -323,7 +308,7 @@ final class YangFunctionContext implements FunctionContext { private static NormalizedNode getNodeReferencedByInstanceIdentifier(final YangInstanceIdentifier path, final NormalizedNodeContext currentNodeContext) { final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator(); - final NormalizedNode rootNode = navigator.getRootNode(); + final NormalizedNode rootNode = navigator.getDocument().getRootNode(); final List pathArguments = path.getPathArguments(); if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) { final List relPath = pathArguments.subList(1, pathArguments.size()); @@ -361,7 +346,7 @@ final class YangFunctionContext implements FunctionContext { xpath.toString(), schemaContext, correspondingSchemaNode, currentNodeContext); final List pathArguments = builder.build(); final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator(); - final NormalizedNode rootNode = navigator.getRootNode(); + final NormalizedNode rootNode = navigator.getDocument().getRootNode(); if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) { final List relPath = pathArguments.subList(1, pathArguments.size()); final Optional> possibleNode = NormalizedNodes.findNode(rootNode, relPath); @@ -438,29 +423,8 @@ final class YangFunctionContext implements FunctionContext { return ((NormalizedNodeContextSupport) contextSupport).getSchemaContext(); } - private static TypedDataSchemaNode getCorrespondingTypedSchemaNode(final SchemaContext schemaContext, - final NormalizedNodeContext currentNodeContext) { - Iterator ancestorOrSelfAxisIterator; - try { - ancestorOrSelfAxisIterator = currentNodeContext.getContextSupport().getNavigator() - .getAncestorOrSelfAxisIterator(currentNodeContext); - } catch (UnsupportedAxisException ex) { - throw new JaxenRuntimeException(ex); - } - - final Deque schemaPathToCurrentNode = new ArrayDeque<>(); - while (ancestorOrSelfAxisIterator.hasNext()) { - final NormalizedNode nextNode = ancestorOrSelfAxisIterator.next().getNode(); - if (!(nextNode instanceof MapNode) && !(nextNode instanceof LeafSetNode) - && !(nextNode instanceof AugmentationNode)) { - schemaPathToCurrentNode.addFirst(nextNode.getNodeType()); - } - } - - final SchemaNode schemaNode = SchemaContextUtil.findNodeInSchemaContext(schemaContext, schemaPathToCurrentNode); - - Preconditions.checkNotNull(schemaNode, "Node %s does not have a corresponding SchemaNode in the SchemaContext.", - currentNodeContext.getNode()); + private static TypedDataSchemaNode getCorrespondingTypedSchemaNode(final NormalizedNodeContext currentNodeContext) { + final DataSchemaNode schemaNode = currentNodeContext.getSchema().getDataSchemaNode(); Preconditions.checkState(schemaNode instanceof TypedDataSchemaNode, "Node %s must be a leaf or a leaf-list.", currentNodeContext.getNode()); return (TypedDataSchemaNode) schemaNode; -- 2.36.6