import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
abstract class AbstractLeafContextNode<T extends PathArgument, S extends DataSchemaNode>
extends DataSchemaContextNode<T> {
- AbstractLeafContextNode(T identifier, S schema) {
+ AbstractLeafContextNode(final T identifier, final S schema) {
super(identifier, schema);
}
public final DataSchemaContextNode<?> getChild(final QName child) {
return null;
}
+
+ @Override
+ protected final DataSchemaContextNode<?> enterChild(final QName child, final SchemaInferenceStack stack) {
+ return null;
+ }
+
+ @Override
+ protected final DataSchemaContextNode<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) {
+ return null;
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+
+/**
+ * Abstract superclass for individual list items -- be it {@link ListItemContextNode} or
+ * {@link UnkeyedListItemContextNode}.
+ */
+abstract class AbstractListItemContextNode<T extends PathArgument> extends DataContainerContextNode<T> {
+ AbstractListItemContextNode(final T identifier, final DataNodeContainer container, final DataSchemaNode schema) {
+ super(identifier, container, schema);
+ }
+
+ @Override
+ protected void pushToStack(final SchemaInferenceStack stack) {
+ // No-op
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.util;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+
+/**
+ * An {@link AbstractMixinContextNode} which corresponding to a {@code list} or {@code leaf-list} node. NormalizedNode
+ * representation of these nodes is similar to JSON encoding and therefore we have two {@link DataSchemaContextNode}
+ * levels backed by a single {@link DataSchemaNode}.
+ */
+abstract class AbstractListLikeContextNode<T extends PathArgument> extends AbstractMixinContextNode<T> {
+ AbstractListLikeContextNode(final T identifier, final DataSchemaNode schema) {
+ super(identifier, schema);
+ }
+
+ @Override
+ protected final DataSchemaContextNode<?> enterChild(final QName child, final SchemaInferenceStack stack) {
+ // Stack is already pointing to the corresponding statement, now we are just working with the child
+ return getChild(child);
+ }
+
+ @Override
+ protected final DataSchemaContextNode<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) {
+ return getChild(child);
+ }
+}
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
final class AugmentationContextNode extends DataContainerContextNode<AugmentationIdentifier> {
AugmentationContextNode(final AugmentationSchemaNode augmentation, final DataNodeContainer target) {
protected Set<QName> getQNameIdentifiers() {
return getIdentifier().getPossibleChildNames();
}
+
+ @Override
+ protected void pushToStack(final SchemaInferenceStack stack) {
+ // No-op
+ }
}
*/
package org.opendaylight.yangtools.yang.data.util;
+import static com.google.common.base.Verify.verifyNotNull;
+
import com.google.common.collect.ImmutableMap;
import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
final class ChoiceNodeContextNode extends AbstractMixinContextNode<NodeIdentifier> {
- private final ImmutableMap<QName, DataSchemaContextNode<?>> byQName;
private final ImmutableMap<PathArgument, DataSchemaContextNode<?>> byArg;
+ private final ImmutableMap<QName, DataSchemaContextNode<?>> byQName;
+ private final ImmutableMap<DataSchemaContextNode<?>, QName> childToCase;
ChoiceNodeContextNode(final ChoiceSchemaNode schema) {
super(NodeIdentifier.create(schema.getQName()), schema);
+ ImmutableMap.Builder<DataSchemaContextNode<?>, QName> childToCaseBuilder = ImmutableMap.builder();
ImmutableMap.Builder<QName, DataSchemaContextNode<?>> byQNameBuilder = ImmutableMap.builder();
ImmutableMap.Builder<PathArgument, DataSchemaContextNode<?>> byArgBuilder = ImmutableMap.builder();
for (DataSchemaNode cazeChild : caze.getChildNodes()) {
DataSchemaContextNode<?> childOp = DataSchemaContextNode.of(cazeChild);
byArgBuilder.put(childOp.getIdentifier(), childOp);
+ childToCaseBuilder.put(childOp, caze.getQName());
for (QName qname : childOp.getQNameIdentifiers()) {
byQNameBuilder.put(qname, childOp);
}
}
}
+
+ childToCase = childToCaseBuilder.build();
byQName = byQNameBuilder.build();
byArg = byArgBuilder.build();
}
protected Set<QName> getQNameIdentifiers() {
return byQName.keySet();
}
-}
+
+ @Override
+ protected DataSchemaContextNode<?> enterChild(final QName child, final SchemaInferenceStack stack) {
+ return pushToStack(getChild(child), stack);
+ }
+
+ @Override
+ protected DataSchemaContextNode<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) {
+ return pushToStack(getChild(child), stack);
+ }
+
+ @Override
+ protected void pushToStack(final @NonNull SchemaInferenceStack stack) {
+ stack.enterChoice(getIdentifier().getNodeType());
+ }
+
+ private @Nullable DataSchemaContextNode<?> pushToStack(final @Nullable DataSchemaContextNode<?> child,
+ final @NonNull SchemaInferenceStack stack) {
+ if (child != null) {
+ final var caseName = verifyNotNull(childToCase.get(child), "No case statement for %s in %s", child, this);
+ stack.enterSchemaTree(caseName);
+ child.pushToStack(stack);
+ }
+ return child;
+ }
+}
\ No newline at end of file
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
abstract class DataContainerContextNode<T extends PathArgument> extends AbstractInteriorContextNode<T> {
private final ConcurrentMap<PathArgument, DataSchemaContextNode<?>> byArg = new ConcurrentHashMap<>();
return register(potential);
}
+ @Override
+ protected final DataSchemaContextNode<?> enterChild(final QName child, final SchemaInferenceStack stack) {
+ return pushToStack(getChild(child), stack);
+ }
+
+
+ @Override
+ protected final DataSchemaContextNode<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) {
+ return pushToStack(getChild(child), stack);
+ }
+
+ private static @Nullable DataSchemaContextNode<?> pushToStack(final @Nullable DataSchemaContextNode<?> child,
+ final @NonNull SchemaInferenceStack stack) {
+ if (child != null) {
+ child.pushToStack(stack);
+ }
+ return child;
+ }
+
private DataSchemaContextNode<?> fromLocalSchema(final PathArgument child) {
if (child instanceof AugmentationIdentifier) {
return fromSchemaAndQNameChecked(container, ((AugmentationIdentifier) child).getPossibleChildNames()
return fromSchemaAndQNameChecked(container, child.getNodeType());
}
- protected DataSchemaContextNode<?> fromLocalSchemaAndQName(final DataNodeContainer schema2, final QName child) {
- return fromSchemaAndQNameChecked(schema2, child);
+ protected DataSchemaContextNode<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child) {
+ return fromSchemaAndQNameChecked(schema, child);
}
private DataSchemaContextNode<?> register(final DataSchemaContextNode<?> potential) {
}
return potential;
}
-
}
*/
package org.opendaylight.yangtools.yang.data.util;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.util.Optional;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
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.ChoiceNode;
+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.UnkeyedListNode;
import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
/**
* Schema derived data providing necessary information for mapping between
*
* @param <T> Path Argument type
*/
+// FIXME: YANGTOOLS-1413: this really should be an interface, as there is a ton of non-trivial composition going on:
+// - getDataSchemaNode() cannot return AugmentationSchemaNode, which is guarded by isMixinNode() and users should
+// not be touching mixin details anyway
+// - the idea of getIdentifier() is wrong -- if does the wrong thing for items of leaf-list and keyed list
+// because those identifiers need a value. We also do not expect users to store the results in a Map, which
+// defeats the idea of Identifiable
+// - the generic argument is really an implementation detail and we really would like to also make dataSchemaNode
+// (or rather: underlying SchemaNode) an argument. Both of these are not something users can influence and
+// therefore we should not burden them with <?> on each reference to this class
public abstract class DataSchemaContextNode<T extends PathArgument> extends AbstractSimpleIdentifiable<T> {
// FIXME: this can be null only for AugmentationContextNode and in that case the interior part is handled by a
// separate field in DataContainerContextNode. We need to re-examine our base interface class hierarchy
this.dataSchemaNode = schema;
}
+ // FIXME: remove this constructor. Once we do, adjust 'enterChild' visibility to package-private
@Deprecated(forRemoval = true, since = "8.0.2")
protected DataSchemaContextNode(final T identifier, final SchemaNode schema) {
this(identifier, schema instanceof DataSchemaNode ? (DataSchemaNode) schema : null);
}
- // FIXME: document this method
+ /**
+ * This node is a {@link NormalizedNode} intermediate, not represented in RFC7950 XML encoding. This is typically
+ * one of
+ * <ul>
+ * <li>{@link AugmentationNode} backed by an {@link AugmentationSchemaNode}, or</li>
+ * <li>{@link ChoiceNode} backed by a {@link ChoiceSchemaNode}, or</li>
+ * <li>{@link LeafSetNode} backed by a {@link LeafListSchemaNode}, or</li>
+ * <li>{@link MapNode} backed by a {@link ListSchemaNode} with a non-empty
+ * {@link ListSchemaNode#getKeyDefinition()}, or</li>
+ * <li>{@link UnkeyedListNode} backed by a {@link ListSchemaNode} with an empty
+ * {@link ListSchemaNode#getKeyDefinition()}</li>
+ * </ul>
+ *
+ * @return {@code} false if this node corresponds to an XML element, or {@code true} if it is an encapsulation node.
+ */
public boolean isMixin() {
return false;
}
// FIXME: document PathArgument type mismatch
public abstract @Nullable DataSchemaContextNode<?> getChild(PathArgument child);
+ /**
+ * Find a child node identifier by its {code data tree} {@link QName}. This method returns intermediate nodes
+ * significant from {@link YangInstanceIdentifier} hierarchy of {@link PathArgument}s. If the returned node
+ * indicates {@code true} via {@link #isMixin()}, it represents a {@link NormalizedNode} encapsulation which is
+ * not visible in RFC7950 XML encoding, and a further call to this method with the same {@code child} argument will
+ * provide the next step.
+ *
+ * @param child Child data tree QName
+ * @return A child node, or null if not found
+ */
// FIXME: document child == null
public abstract @Nullable DataSchemaContextNode<?> getChild(QName child);
+ /**
+ * Attempt to enter a child {@link DataSchemaContextNode} towards the {@link DataSchemaNode} child identified by
+ * specified {@code data tree} {@link QName}, adjusting provided {@code stack} with inference steps corresponding to
+ * the transition to the returned node. The stack is expected to be correctly pointing at this node's schema,
+ * otherwise the results of this method are undefined.
+ *
+ * @param stack {@link SchemaInferenceStack} to update
+ * @param child Child QName
+ * @return A DataSchemaContextNode on the path towards the specified child
+ * @throws NullPointerException if any argument is {@code null}
+ */
+ public final @Nullable DataSchemaContextNode<?> enterChild(final SchemaInferenceStack stack, final QName child) {
+ return enterChild(requireNonNull(child), requireNonNull(stack));
+ }
+
+ // FIXME: make this method package-private once the protected constructor is gone
+ protected abstract @Nullable DataSchemaContextNode<?> enterChild(@NonNull QName child,
+ @NonNull SchemaInferenceStack stack);
+
+ /**
+ * Attempt to enter a child {@link DataSchemaContextNode} towards the {@link DataSchemaNode} child identified by
+ * specified {@link PathArgument}, adjusting provided {@code stack} with inference steps corresponding to
+ * the transition to the returned node. The stack is expected to be correctly pointing at this node's schema,
+ * otherwise the results of this method are undefined.
+ *
+ * @param stack {@link SchemaInferenceStack} to update
+ * @param child Child path argument
+ * @return A DataSchemaContextNode for the specified child
+ * @throws NullPointerException if any argument is {@code null}
+ */
+ public final @Nullable DataSchemaContextNode<?> enterChild(final SchemaInferenceStack stack,
+ final PathArgument child) {
+ return enterChild(requireNonNull(child), requireNonNull(stack));
+ }
+
+ // FIXME: make this method package-private once the protected constructor is gone
+ protected abstract @Nullable DataSchemaContextNode<?> enterChild(@NonNull PathArgument child,
+ @NonNull SchemaInferenceStack stack);
+
+ /**
+ * Push this node into specified {@link SchemaInferenceStack}.
+ *
+ * @param stack {@link SchemaInferenceStack}
+ */
+ // FIXME: make this method package-private once the protected constructor is gone
+ protected void pushToStack(final @NonNull SchemaInferenceStack stack) {
+ // Accurate for most subclasses
+ stack.enterSchemaTree(getIdentifier().getNodeType());
+ }
+
// FIXME: final
public @Nullable DataSchemaNode getDataSchemaNode() {
return dataSchemaNode;
*/
package org.opendaylight.yangtools.yang.data.util;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.CheckedValue;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.spi.AbstractEffectiveModelContextProvider;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
/**
* Semantic tree binding a {@link EffectiveModelContext} to a {@link NormalizedNode} tree. Since the layout of the
* @author Robert Varga
*/
public final class DataSchemaContextTree extends AbstractEffectiveModelContextProvider {
+ // FIXME: record once we have JDK17+
+ public static final class NodeAndStack {
+ private final @NonNull DataSchemaContextNode<?> node;
+ private final @NonNull SchemaInferenceStack stack;
+
+ NodeAndStack(final DataSchemaContextNode<?> node, final @NonNull SchemaInferenceStack stack) {
+ this.node = requireNonNull(node);
+ this.stack = requireNonNull(stack);
+ }
+
+ public @NonNull DataSchemaContextNode<?> node() {
+ return node;
+ }
+
+ public @NonNull SchemaInferenceStack stack() {
+ return stack;
+ }
+ }
+
private static final LoadingCache<EffectiveModelContext, @NonNull DataSchemaContextTree> TREES =
CacheBuilder.newBuilder().weakKeys().weakValues().build(new CacheLoader<>() {
@Override
return root.findChild(path);
}
+ /**
+ * Find a child node as identified by an absolute {@link YangInstanceIdentifier} and return it along with a suitably
+ * initialized {@link SchemaInferenceStack}.
+ *
+ * @param path Path towards the child node
+ * @return A {@link NodeAndStack}, or empty when corresponding child is not found.
+ * @throws NullPointerException if {@code path} is null
+ */
+ public @NonNull CheckedValue<@NonNull NodeAndStack, @NonNull IllegalArgumentException> enterPath(
+ final YangInstanceIdentifier path) {
+ final var stack = SchemaInferenceStack.of((EffectiveModelContext) root.getDataSchemaNode());
+ DataSchemaContextNode<?> node = root;
+ for (var arg : path.getPathArguments()) {
+ final var child = node.enterChild(arg, stack);
+ if (child == null) {
+ return CheckedValue.ofException(new IllegalArgumentException("Failed to find " + arg + " in " + node));
+ }
+ node = child;
+ }
+
+ return CheckedValue.ofValue(new NodeAndStack(node, stack));
+ }
+
public @NonNull DataSchemaContextNode<?> getRoot() {
return root;
}
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
final class LeafListEntryContextNode extends AbstractLeafNodeContext<NodeWithValue<?>, LeafListSchemaNode> {
LeafListEntryContextNode(final LeafListSchemaNode schema) {
public boolean isKeyedEntry() {
return true;
}
+
+ @Override
+ protected void pushToStack(final SchemaInferenceStack stack) {
+ // No-op
+ }
}
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-final class ListItemContextNode extends DataContainerContextNode<NodeIdentifierWithPredicates> {
- ListItemContextNode(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
- super(identifier, schema, schema);
+final class ListItemContextNode extends AbstractListItemContextNode<NodeIdentifierWithPredicates> {
+ ListItemContextNode(final ListSchemaNode schema) {
+ // FIXME: this is wrong: we have no predicates at all!
+ super(NodeIdentifierWithPredicates.of(schema.getQName()), schema, schema);
}
@Override
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-final class UnkeyedListItemContextNode extends DataContainerContextNode<NodeIdentifier> {
+final class UnkeyedListItemContextNode extends AbstractListItemContextNode<NodeIdentifier> {
UnkeyedListItemContextNode(final ListSchemaNode schema) {
super(NodeIdentifier.create(schema.getQName()), schema, schema);
}
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-final class UnkeyedListMixinContextNode extends AbstractMixinContextNode<NodeIdentifier> {
+final class UnkeyedListMixinContextNode extends AbstractListLikeContextNode<NodeIdentifier> {
private final UnkeyedListItemContextNode innerNode;
UnkeyedListMixinContextNode(final ListSchemaNode list) {
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-class UnorderedLeafListMixinContextNode extends AbstractMixinContextNode<NodeIdentifier> {
+class UnorderedLeafListMixinContextNode extends AbstractListLikeContextNode<NodeIdentifier> {
private final LeafListEntryContextNode innerOp;
UnorderedLeafListMixinContextNode(final LeafListSchemaNode schema) {
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-class UnorderedMapMixinContextNode extends AbstractMixinContextNode<NodeIdentifier> {
+class UnorderedMapMixinContextNode extends AbstractListLikeContextNode<NodeIdentifier> {
private final ListItemContextNode innerNode;
UnorderedMapMixinContextNode(final ListSchemaNode list) {
super(NodeIdentifier.create(list.getQName()), list);
- innerNode = new ListItemContextNode(NodeIdentifierWithPredicates.of(list.getQName()), list);
+ innerNode = new ListItemContextNode(list);
}
@Override
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.util;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.XMLNamespace;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class YT1412Test {
+ private static final QNameModule MODULE = QNameModule.create(XMLNamespace.of("foo"));
+ private static final QName ONE = QName.create(MODULE, "one");
+ private static final QName TWO = QName.create(MODULE, "two");
+ private static final QName THREE = QName.create(MODULE, "three");
+ private static final QName FOUR = QName.create(MODULE, "four");
+ private static final QName FIVE = QName.create(MODULE, "five");
+ private static final QName SIX = QName.create(MODULE, "six");
+
+ private static DataSchemaContextTree CONTEXT;
+
+ private final SchemaInferenceStack stack = SchemaInferenceStack.of(CONTEXT.getEffectiveModelContext());
+
+ @BeforeClass
+ public static void init() {
+ CONTEXT = DataSchemaContextTree.from(YangParserTestUtils.parseYangResource("/yt1412.yang"));
+ }
+
+ @AfterClass
+ public static void cleanup() {
+ CONTEXT = null;
+ }
+
+ @Test
+ public void testEnterThroughChoice() {
+ final var one = CONTEXT.getRoot().enterChild(stack, ONE);
+ assertThat(one, instanceOf(ContainerContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ContainerEffectiveStatement.class));
+
+ final var two = one.enterChild(FOUR, stack);
+ assertThat(two, instanceOf(ChoiceNodeContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ChoiceEffectiveStatement.class));
+
+ final var three = two.enterChild(FOUR, stack);
+ assertThat(three, instanceOf(ChoiceNodeContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ChoiceEffectiveStatement.class));
+
+ final var four = three.enterChild(FOUR, stack);
+ assertThat(four, instanceOf(LeafContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(LeafEffectiveStatement.class));
+
+ assertEquals(Absolute.of(ONE, TWO, THREE, THREE, FOUR, FOUR), stack.toSchemaNodeIdentifier());
+ }
+
+ @Test
+ public void testEnterThroughAugment() {
+ final var one = CONTEXT.getRoot().enterChild(stack, ONE);
+ assertThat(one, instanceOf(ContainerContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ContainerEffectiveStatement.class));
+
+ final var augment = one.enterChild(FIVE, stack);
+ assertThat(augment, instanceOf(AugmentationContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ContainerEffectiveStatement.class));
+
+ final var five = augment.enterChild(FIVE, stack);
+ assertThat(five, instanceOf(UnkeyedListMixinContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ListEffectiveStatement.class));
+
+ final var fiveItem = five.enterChild(FIVE, stack);
+ assertThat(fiveItem, instanceOf(UnkeyedListItemContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ListEffectiveStatement.class));
+
+ assertEquals(Absolute.of(ONE, FIVE), stack.toSchemaNodeIdentifier());
+ }
+
+ @Test
+ public void testEnterThroughAugmentChoiceAugment() {
+ final var one = CONTEXT.getRoot().enterChild(stack, ONE);
+ assertThat(one, instanceOf(ContainerContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ContainerEffectiveStatement.class));
+
+ final var two = one.enterChild(SIX, stack);
+ assertThat(two, instanceOf(ChoiceNodeContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ChoiceEffectiveStatement.class));
+
+ final var three = two.enterChild(SIX, stack);
+ assertThat(three, instanceOf(ChoiceNodeContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(ChoiceEffectiveStatement.class));
+
+ final var six = three.enterChild(SIX, stack);
+ assertThat(six, instanceOf(LeafContextNode.class));
+ assertThat(stack.currentStatement(), instanceOf(LeafEffectiveStatement.class));
+
+ assertEquals(Absolute.of(ONE, TWO, THREE, THREE, SIX, SIX), stack.toSchemaNodeIdentifier());
+ }
+
+ @Test
+ public void testEnterChoicePath() {
+ final var result = CONTEXT.enterPath(YangInstanceIdentifier.create(
+ new NodeIdentifier(ONE),
+ new NodeIdentifier(TWO),
+ new NodeIdentifier(THREE),
+ new NodeIdentifier(FOUR)))
+ .orElseThrow();
+
+ assertThat(result.node(), instanceOf(LeafContextNode.class));
+ assertEquals(Absolute.of(ONE, TWO, THREE, THREE, FOUR, FOUR), result.stack().toSchemaNodeIdentifier());
+ }
+
+ @Test
+ public void testEnterAugmentPath() {
+ final var result = CONTEXT.enterPath(YangInstanceIdentifier.create(
+ new NodeIdentifier(ONE),
+ new AugmentationIdentifier(Set.of(FIVE)),
+ new NodeIdentifier(FIVE),
+ new NodeIdentifier(FIVE)))
+ .orElseThrow();
+
+ assertThat(result.node(), instanceOf(UnkeyedListItemContextNode.class));
+ assertEquals(Absolute.of(ONE, FIVE), result.stack().toSchemaNodeIdentifier());
+ }
+
+ @Test
+ public void testEnterAugmentChoicePath() {
+ final var result = CONTEXT.enterPath(YangInstanceIdentifier.create(
+ new NodeIdentifier(ONE),
+ new NodeIdentifier(TWO),
+ new NodeIdentifier(THREE),
+ new NodeIdentifier(SIX)))
+ .orElseThrow();
+
+ assertThat(result.node(), instanceOf(LeafContextNode.class));
+ assertEquals(Absolute.of(ONE, TWO, THREE, THREE, SIX, SIX), result.stack().toSchemaNodeIdentifier());
+ }
+}
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+ yang-version 1.1;
+
+ container one {
+ choice two {
+ choice three {
+ leaf four {
+ type string;
+ }
+ }
+ }
+ }
+
+ augment /one {
+ list five;
+ }
+
+ augment /one/two/three/three {
+ leaf six {
+ type string;
+ }
+ }
+}