Jaxen binding should use DataSchemaContextTree 28/71528/12
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 27 Apr 2018 20:46:32 +0000 (22:46 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 1 Aug 2018 20:35:43 +0000 (22:35 +0200)
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 <robert.varga@pantheon.tech>
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java

index eb7cdd535755004ff7bcf901486b0b2f5de791cb..ead6ae4049c515e0188f6dcb5b82e023bc2e45aa 100644 (file)
@@ -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;
+    }
 }
index c6081f0ca904e3c984ed4726c5820afa766e2f4a..ef8aed55665df198ff8d09a56f586a27a7aa4a35 100644 (file)
@@ -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);
     }
 }
index 362d6f7e27ec877bca8de3701b442d438b9969e1..8b04b4355a6b4c4c0b86818509510161d4a04053 100644 (file)
@@ -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<NormalizedNode<?, ?>, 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<Normalized
         return parent;
     }
 
-    @Nonnull YangInstanceIdentifier getPath() {
+    YangInstanceIdentifier getPath() {
         return (parent == null ? YangInstanceIdentifier.EMPTY : parent.getPath()).node(node.getIdentifier());
     }
 
+    DataSchemaContextNode<?> 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);
     }
 }
index 441e7d672dbb28ed0c08f1edae89443d0147e87a..67c7376b54cc1ebcf044ff54be0520f6a6fa88f1 100644 (file)
@@ -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<NormalizedNode<?, ?>> 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();
+    }
 }
index 8c735e943f8ca0294d893c2d82cf80d60128c64c..2781edbdf9f17d867f188e0b28fa6f5290a9c583 100644 (file)
@@ -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<? extends NormalizedNode<?, ?>> 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<NormalizedNodeContext> {
index 1c7d2fec613b261c7976f390534bce8ee9f4eeba..3b3e51d210033d77f8e48fde95f71f66e43380cb 100644 (file)
@@ -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<IdentitySchemaNode, IdentitySchemaNode> ids =  commonDerivedFrom("derived-from", context, args);
+        final Entry<IdentitySchemaNode, IdentitySchemaNode> 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<PathArgument> pathArguments = path.getPathArguments();
         if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) {
             final List<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
@@ -361,7 +346,7 @@ final class YangFunctionContext implements FunctionContext {
                 xpath.toString(), schemaContext, correspondingSchemaNode, currentNodeContext);
         final List<PathArgument> 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<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
             final Optional<NormalizedNode<?, ?>> 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<NormalizedNodeContext> ancestorOrSelfAxisIterator;
-        try {
-            ancestorOrSelfAxisIterator = currentNodeContext.getContextSupport().getNavigator()
-                    .getAncestorOrSelfAxisIterator(currentNodeContext);
-        } catch (UnsupportedAxisException ex) {
-            throw new JaxenRuntimeException(ex);
-        }
-
-        final Deque<QName> 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;