Move yang-data-jaxen to attic top-level directory
[yangtools.git] / attic / yang-data-jaxen / src / main / java / org / opendaylight / yangtools / yang / data / jaxen / NormalizedNodeContext.java
diff --git a/attic/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java b/attic/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java
new file mode 100644 (file)
index 0000000..6293ce2
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.jaxen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+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.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+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.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 {
+    private static final long serialVersionUID = 1L;
+    private final @Nullable NormalizedNodeContext parent;
+    private final DataSchemaContextNode<?> schema;
+    private final NormalizedNode<?, ?> node;
+
+    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));
+    }
+
+    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;
+    }
+
+    @Nullable NormalizedNodeContext getParent() {
+        return parent;
+    }
+
+    YangInstanceIdentifier getPath() {
+        return (parent == null ? YangInstanceIdentifier.empty() : parent.getPath()).node(node.getIdentifier());
+    }
+
+    DataSchemaContextNode<?> getSchema() {
+        return schema;
+    }
+
+    NormalizedNodeContext createChild(final NormalizedNode<?, ?> input) {
+        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);
+    }
+
+    Optional<NormalizedNodeContext> findChild(final PathArgument arg) {
+        return node instanceof DataContainerNode ? ((DataContainerNode<?>)node).getChild(arg).map(this::createChild)
+                : Optional.empty();
+    }
+
+    Optional<NormalizedNodeContext> findDescendant(final YangInstanceIdentifier path) {
+        if (path.isEmpty()) {
+            return Optional.of(this);
+        }
+
+        NormalizedNodeContext ctxWalk = this;
+        NormalizedNode<?, ?> dataWalk = node;
+        for (PathArgument arg : path.getPathArguments()) {
+            checkArgument(dataWalk instanceof DataContainerNode, "Path %s refers beyond node %s", path, dataWalk);
+
+            final Optional<DataContainerChild<? extends @Nullable PathArgument, ?>> optChild =
+                    ((DataContainerNode)dataWalk).getChild(arg);
+            if (!optChild.isPresent()) {
+                return Optional.empty();
+            }
+
+            dataWalk = optChild.get();
+            ctxWalk = createChild(dataWalk);
+        }
+
+        return Optional.of(ctxWalk.createChild(dataWalk));
+    }
+
+    Iterator<NormalizedNodeContext> iterateChildren(final DataContainerNode<?> data) {
+        return Iterators.transform(((DataContainerNode<?>) node).getValue().iterator(), this::createChild);
+    }
+
+    @Nullable Iterator<NormalizedNodeContext> iterateChildrenNamed(final DataContainerNode<?> data, final QName qname) {
+        final NodeIdentifier arg = new NodeIdentifier(qname);
+        final Optional<DataContainerChild<? extends @Nullable PathArgument, ?>> maybeChild = data.getChild(arg);
+        if (!maybeChild.isPresent()) {
+            return null;
+        }
+
+        final NormalizedNode<?, ?> child = maybeChild.get();
+        final Collection<? extends NormalizedNode<?, ?>> collection;
+
+        // The child may be a structural node
+        if (child instanceof MapNode) {
+            collection = ((MapNode)child).getValue();
+        } else if (child instanceof LeafSetNode) {
+            collection = ((LeafSetNode<?>)child).getValue();
+        } else {
+            return Iterators.singletonIterator(createChild(child));
+        }
+
+        return Iterators.transform(collection.iterator(), this::createChild);
+    }
+
+    static NormalizedNodeContext cast(@Nullable final Context context) {
+        verify(context instanceof NormalizedNodeContext);
+        return (@NonNull NormalizedNodeContext) context;
+    }
+}