import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
-import com.google.common.collect.Iterables;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
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;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-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.AugmentationTarget;
-import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerLike;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnydataEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
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.DataTreeAwareEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
public final class NormalizedNodeStreamWriterStack implements LeafrefResolver {
private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeStreamWriterStack.class);
- private final Deque<WithStatus> schemaStack = new ArrayDeque<>();
+ private final Deque<EffectiveStatement<?, ?>> schemaStack = new ArrayDeque<>();
private final SchemaInferenceStack dataTree;
- private final DataNodeContainer root;
+ private final Object root;
private NormalizedNodeStreamWriterStack(final EffectiveModelContext context) {
dataTree = SchemaInferenceStack.of(context);
private NormalizedNodeStreamWriterStack(final SchemaInferenceStack dataTree) {
this.dataTree = requireNonNull(dataTree);
if (!dataTree.isEmpty()) {
- final EffectiveStatement<?, ?> current = dataTree.currentStatement();
- checkArgument(current instanceof DataNodeContainer, "Cannot instantiate on %s", current);
-
- root = (DataNodeContainer) current;
+ final var current = dataTree.currentStatement();
+ if (current instanceof DataTreeAwareEffectiveStatement container) {
+ root = container;
+ } else {
+ throw new IllegalArgumentException("Cannot instantiate on " + current);
+ }
} else {
- root = dataTree.getEffectiveModelContext();
+ root = dataTree.modelContext();
}
}
.stack());
}
- /**
- * Create a new writer with the specified context and rooted in the specified schema path.
- *
- * @param context Associated {@link EffectiveModelContext}
- * @param path schema path
- * @return A new {@link NormalizedNodeStreamWriterStack}
- * @throws NullPointerException if any argument is null
- * @throws IllegalArgumentException if {@code path} does not point to a valid root
- */
- @Deprecated
- public static @NonNull NormalizedNodeStreamWriterStack of(final EffectiveModelContext context,
- final SchemaPath path) {
- return new NormalizedNodeStreamWriterStack(SchemaInferenceStack.ofSchemaPath(context, path));
- }
-
/**
* Create a new writer with the specified context and rooted in the specified schema path.
*
return dataTree.resolveLeafref(type);
}
- public Object getParent() {
- final WithStatus schema = schemaStack.peek();
- return schema == null ? root : schema;
+ /**
+ * Return the current {@link EffectiveStatement}, or {@code null}.
+ *
+ * @return the current {@link EffectiveStatement}, or {@code null}
+ */
+ public @Nullable EffectiveStatement<?, ?> currentStatement() {
+ return schemaStack.peek();
}
- private SchemaNode enterDataTree(final PathArgument name) {
- final QName qname = name.getNodeType();
- final DataTreeEffectiveStatement<?> stmt = dataTree.enterDataTree(qname);
- verify(stmt instanceof SchemaNode, "Unexpected result %s", stmt);
- final SchemaNode ret = (SchemaNode) stmt;
- final Object parent = getParent();
- if (parent instanceof ChoiceSchemaNode) {
- final DataSchemaNode check = ((ChoiceSchemaNode) parent).findDataSchemaChild(qname).orElse(null);
- verify(check == ret, "Data tree result %s does not match choice result %s", ret, check);
+ private @NonNull DataTreeEffectiveStatement<?> enterDataTree(final PathArgument name) {
+ final var qname = name.getNodeType();
+ final var stmt = dataTree.enterDataTree(qname);
+ if (currentStatement() instanceof ChoiceEffectiveStatement choice) {
+ final var check = choice.findDataTreeNode(qname).orElse(null);
+ verify(check == stmt, "Data tree result %s does not match choice result %s", stmt, check);
}
- return ret;
+ return stmt;
+ }
+
+ private <T extends DataTreeEffectiveStatement<?>> @NonNull T enterDataTree(final PathArgument name,
+ final @NonNull Class<T> expectedClass, final @NonNull String humanString) {
+ final var schema = enterDataTree(name);
+ final @NonNull T casted;
+ try {
+ casted = expectedClass.cast(schema);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Node " + schema + " is not " + humanString, e);
+ }
+ schemaStack.push(casted);
+ return casted;
}
public void startList(final PathArgument name) {
- final SchemaNode schema = enterDataTree(name);
- checkArgument(schema instanceof ListSchemaNode, "Node %s is not a list", schema);
- schemaStack.push(schema);
+ enterDataTree(name, ListEffectiveStatement.class, "a list");
}
public void startListItem(final PathArgument name) throws IOException {
- final Object schema = getParent();
- checkArgument(schema instanceof ListSchemaNode, "List item is not appropriate");
- schemaStack.push((ListSchemaNode) schema);
+ if (!(currentStatement() instanceof ListEffectiveStatement parentList)) {
+ throw new IllegalArgumentException("List item is not appropriate");
+ }
+ schemaStack.push(parentList);
}
public void startLeafNode(final NodeIdentifier name) throws IOException {
- final SchemaNode schema = enterDataTree(name);
- checkArgument(schema instanceof LeafSchemaNode, "Node %s is not a leaf", schema);
- schemaStack.push(schema);
- }
-
- public LeafListSchemaNode startLeafSet(final NodeIdentifier name) {
- final SchemaNode schema = enterDataTree(name);
- checkArgument(schema instanceof LeafListSchemaNode, "Node %s is not a leaf-list", schema);
- schemaStack.push(schema);
- return (LeafListSchemaNode) schema;
+ enterDataTree(name, LeafEffectiveStatement.class, "a leaf");
}
- public LeafListSchemaNode leafSetEntryNode(final QName qname) {
- final Object parent = getParent();
- if (parent instanceof LeafListSchemaNode) {
- return (LeafListSchemaNode) parent;
- }
- checkArgument(parent instanceof DataNodeContainer, "Cannot lookup %s in parent %s", qname, parent);
- final DataSchemaNode child = ((DataNodeContainer) parent).dataChildByName(qname);
- checkArgument(child instanceof LeafListSchemaNode,
- "Node %s is neither a leaf-list nor currently in a leaf-list", child);
- return (LeafListSchemaNode) child;
+ public void startLeafSet(final NodeIdentifier name) {
+ enterDataTree(name, LeafListEffectiveStatement.class, "a leaf-list");
}
public void startLeafSetEntryNode(final NodeWithValue<?> name) {
schemaStack.push(leafSetEntryNode(name.getNodeType()));
}
- public ChoiceSchemaNode startChoiceNode(final NodeIdentifier name) {
+ private @NonNull LeafListEffectiveStatement leafSetEntryNode(final QName qname) {
+ final var parent = currentStatement();
+ if (parent instanceof LeafListEffectiveStatement leafList) {
+ return leafList;
+ }
+ if (parent instanceof DataTreeAwareEffectiveStatement parentContainer) {
+ final var child = parentContainer.findDataTreeNode(qname).orElse(null);
+ if (child instanceof LeafListEffectiveStatement childLeafList) {
+ return childLeafList;
+ }
+ throw new IllegalArgumentException(
+ "Node " + child + " is neither a leaf-list nor currently in a leaf-list");
+ }
+ throw new IllegalArgumentException("Cannot lookup " + qname + " in parent " + parent);
+ }
+
+ public void startChoiceNode(final NodeIdentifier name) {
LOG.debug("Enter choice {}", name);
- final ChoiceEffectiveStatement stmt = dataTree.enterChoice(name.getNodeType());
- verify(stmt instanceof ChoiceSchemaNode, "Node %s is not a choice", stmt);
- final ChoiceSchemaNode ret = (ChoiceSchemaNode) stmt;
- schemaStack.push(ret);
- return ret;
+ schemaStack.push(dataTree.enterChoice(name.getNodeType()));
}
- public SchemaNode startContainerNode(final NodeIdentifier name) {
+ public @NonNull DataTreeAwareEffectiveStatement<QName, ?> startContainerNode(final NodeIdentifier name) {
LOG.debug("Enter container {}", name);
- final SchemaNode schema;
- if (schemaStack.isEmpty() && root instanceof NotificationDefinition) {
+ final DataTreeAwareEffectiveStatement<QName, ?> ret;
+ if (schemaStack.isEmpty() && root instanceof NotificationEffectiveStatement notification
+ && name.getNodeType().equals(notification.argument())) {
// Special case for stacks initialized at notification. We pretend the first container is contained within
// itself.
// FIXME: 8.0.0: factor this special case out to something more reasonable, like being initialized at the
// Notification's parent and knowing to enterSchemaTree() instead of enterDataTree().
- schema = (NotificationDefinition) root;
+ ret = notification;
} else {
- schema = enterDataTree(name);
- checkArgument(schema instanceof ContainerLike, "Node %s is not a container", schema);
+ final var child = enterDataTree(name);
+ if (child instanceof ContainerEffectiveStatement container) {
+ ret = container;
+ } else if (child instanceof InputEffectiveStatement input) {
+ ret = input;
+ } else if (child instanceof OutputEffectiveStatement output) {
+ ret = output;
+ } else {
+ dataTree.exitToDataTree();
+ throw new IllegalArgumentException("Node " + child + " is not a container");
+ }
}
- schemaStack.push(schema);
- return schema;
+ schemaStack.push(ret);
+ return ret;
}
public void startAnyxmlNode(final NodeIdentifier name) {
- final SchemaNode schema = enterDataTree(name);
- checkArgument(schema instanceof AnyxmlSchemaNode, "Node %s is not anyxml", schema);
- schemaStack.push(schema);
+ enterDataTree(name, AnyxmlEffectiveStatement.class, "anyxml");
}
public void startAnydataNode(final NodeIdentifier name) {
- final SchemaNode schema = enterDataTree(name);
- checkArgument(schema instanceof AnydataSchemaNode, "Node %s is not anydata", schema);
- schemaStack.push(schema);
+ enterDataTree(name, AnydataEffectiveStatement.class, "anydata");
}
- public Object endNode() {
- final Object ret = schemaStack.pop();
+ public EffectiveStatement<?, ?> endNode() {
+ final var ret = schemaStack.pop();
// If this is a data tree node, make sure it is updated. Before that, though, we need to check if this is not
// actually listEntry -> list or leafListEntry -> leafList exit.
- if (!(ret instanceof AugmentationSchemaNode) && getParent() != ret) {
+ if (currentStatement() != ret) {
dataTree.exit();
}
return ret;
}
-
- public AugmentationSchemaNode startAugmentationNode(final AugmentationIdentifier identifier) {
- LOG.debug("Enter augmentation {}", identifier);
- Object parent = getParent();
-
- checkArgument(parent instanceof AugmentationTarget, "Augmentation not allowed under %s", parent);
- if (parent instanceof ChoiceSchemaNode) {
- final QName name = Iterables.get(identifier.getPossibleChildNames(), 0);
- parent = findCaseByChild((ChoiceSchemaNode) parent, name);
- }
- checkArgument(parent instanceof DataNodeContainer, "Augmentation allowed only in DataNodeContainer", parent);
- final AugmentationSchemaNode schema = findSchemaForAugment((AugmentationTarget) parent,
- identifier.getPossibleChildNames());
- final AugmentationSchemaNode resolvedSchema = new EffectiveAugmentationSchema(schema,
- (DataNodeContainer) parent);
- schemaStack.push(resolvedSchema);
- return resolvedSchema;
- }
-
- // FIXME: 7.0.0: can we get rid of this?
- private static @NonNull AugmentationSchemaNode findSchemaForAugment(final AugmentationTarget schema,
- final Set<QName> qnames) {
- for (final AugmentationSchemaNode augment : schema.getAvailableAugmentations()) {
- if (qnames.equals(augment.getChildNodes().stream()
- .map(DataSchemaNode::getQName)
- .collect(Collectors.toUnmodifiableSet()))) {
- return augment;
- }
- }
-
- throw new IllegalStateException(
- "Unknown augmentation node detected, identified by: " + qnames + ", in: " + schema);
- }
-
- // FIXME: 7.0.0: can we get rid of this?
- private static SchemaNode findCaseByChild(final ChoiceSchemaNode parent, final QName qname) {
- for (final CaseSchemaNode caze : parent.getCases()) {
- final Optional<DataSchemaNode> potential = caze.findDataChildByName(qname);
- if (potential.isPresent()) {
- return caze;
- }
- }
- return null;
- }
}