package org.opendaylight.netconf.util;
import static com.google.common.base.Preconditions.checkArgument;
-import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
+import java.util.stream.Collectors;
import javax.xml.transform.dom.DOMSource;
import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.QName;
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.NodeIdentifierWithPredicates;
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.data.util.DataSchemaContextNode;
import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
// We try to look up if this node was added by augmentation
if (schema instanceof DataSchemaNode && result.isAugmenting()) {
for (final AugmentationSchemaNode aug : ((AugmentationTarget)schema).getAvailableAugmentations()) {
- if (aug.findDataChildByName(result.getQName()).isPresent()) {
+ if (aug.dataChildByName(result.getQName()) != null) {
return new Augmentation(aug, schema);
}
}
}
static StreamingContext<?> fromDataSchemaNode(final DataSchemaNode potential) {
- if (potential instanceof ContainerSchemaNode) {
- return new Container((ContainerSchemaNode) potential);
- } else if (potential instanceof ListSchemaNode) {
- return fromListSchemaNode((ListSchemaNode) potential);
- } else if (potential instanceof LeafSchemaNode) {
- return new Leaf((LeafSchemaNode) potential);
- } else if (potential instanceof ChoiceSchemaNode) {
- return new Choice((ChoiceSchemaNode) potential);
- } else if (potential instanceof LeafListSchemaNode) {
- return fromLeafListSchemaNode((LeafListSchemaNode) potential);
- } else if (potential instanceof AnyxmlSchemaNode) {
- return new AnyXml((AnyxmlSchemaNode) potential);
- }
+ if (potential instanceof ContainerSchemaNode container) {
+ return new Container(container);
+ } else if (potential instanceof ListSchemaNode list) {
+ return fromListSchemaNode(list);
+ } else if (potential instanceof LeafSchemaNode leaf) {
+ return new Leaf(leaf);
+ } else if (potential instanceof ChoiceSchemaNode choice) {
+ return new Choice(choice);
+ } else if (potential instanceof LeafListSchemaNode leafList) {
+ return fromLeafListSchemaNode(leafList);
+ } else if (potential instanceof AnyxmlSchemaNode anyxml) {
+ return new AnyXml(anyxml);
+ }
+ // FIXME: unhandled anydata!
return null;
}
abstract StreamingContext<?> getChild(PathArgument child);
+ /**
+ * Writing node structure that is described by series of {@link PathArgument}
+ * into {@link NormalizedNodeStreamWriter}.
+ *
+ * @param writer output {@link NormalizedNode} writer
+ * @param first the first {@link PathArgument}
+ * @param others iterator that points to next path arguments
+ * @throws IOException failed to write a stream of path arguments into {@link NormalizedNodeStreamWriter}
+ */
abstract void streamToWriter(NormalizedNodeStreamWriter writer, PathArgument first, Iterator<PathArgument> others)
throws IOException;
+ /**
+ * Writing node structure that is described by provided {@link PathNode} into {@link NormalizedNodeStreamWriter}.
+ *
+ * @param writer output {@link NormalizedNode} writer
+ * @param first the first {@link PathArgument}
+ * @param tree subtree of path arguments that starts with the first path argument
+ * @throws IOException failed to write a stream of path arguments into {@link NormalizedNodeStreamWriter}
+ */
+ abstract void streamToWriter(NormalizedNodeStreamWriter writer, PathArgument first, PathNode tree)
+ throws IOException;
+
abstract boolean isMixin();
private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
@Override
final void streamToWriter(final NormalizedNodeStreamWriter writer, final PathArgument first,
final Iterator<PathArgument> others) throws IOException {
- if (!isMixin()) {
- final QName type = getIdentifier().getNodeType();
- if (type != null) {
- final QName firstType = first.getNodeType();
- checkArgument(type.equals(firstType), "Node QName must be %s was %s", type, firstType);
- }
- }
+ verifyActualPathArgument(first);
- emitElementStart(writer, first);
+ emitElementStart(writer, first, others.hasNext() ? 1 : 0);
if (others.hasNext()) {
final PathArgument childPath = others.next();
final StreamingContext<?> childOp = getChildOperation(childPath);
writer.endNode();
}
- abstract void emitElementStart(NormalizedNodeStreamWriter writer, PathArgument arg) throws IOException;
+ @Override
+ final void streamToWriter(final NormalizedNodeStreamWriter writer, final PathArgument first,
+ final PathNode subtree) throws IOException {
+ verifyActualPathArgument(first);
+
+ final Collection<PathNode> children = subtree.children();
+ emitElementStart(writer, first, children.size());
+ for (final PathNode node : subtree.children()) {
+ emitChildTreeNode(writer, node);
+ }
+ writer.endNode();
+ }
+
+ void emitChildTreeNode(final NormalizedNodeStreamWriter writer, final PathNode node) throws IOException {
+ final PathArgument childPath = node.element();
+ getChildOperation(childPath).streamToWriter(writer, childPath, node);
+ }
+
+ private void verifyActualPathArgument(final PathArgument first) {
+ if (!isMixin()) {
+ final QName type = getIdentifier().getNodeType();
+ final QName firstType = first.getNodeType();
+ checkArgument(type.equals(firstType), "Node QName must be %s was %s", type, firstType);
+ }
+ }
+
+ abstract void emitElementStart(NormalizedNodeStreamWriter writer, PathArgument arg,
+ int childSizeHint) throws IOException;
@SuppressWarnings("checkstyle:illegalCatch")
- private StreamingContext<?> getChildOperation(final PathArgument childPath) {
+ StreamingContext<?> getChildOperation(final PathArgument childPath) {
final StreamingContext<?> childOp;
try {
childOp = getChild(childPath);
}
private StreamingContext<?> fromLocalSchema(final PathArgument child) {
- if (child instanceof AugmentationIdentifier) {
- return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
- .iterator().next());
+ if (child instanceof AugmentationIdentifier aid) {
+ return fromSchemaAndQNameChecked(schema, aid.getPossibleChildNames().iterator().next());
}
return fromSchemaAndQNameChecked(schema, child.getNodeType());
}
private abstract static class AbstractMapMixin extends AbstractComposite<NodeIdentifier> {
private final ListEntry innerNode;
+ private final List<QName> keyLeaves;
AbstractMapMixin(final ListSchemaNode list) {
super(NodeIdentifier.create(list.getQName()));
- this.innerNode = new ListEntry(NodeIdentifierWithPredicates.of(list.getQName()), list);
+ innerNode = new ListEntry(NodeIdentifierWithPredicates.of(list.getQName()), list);
+ keyLeaves = list.getKeyDefinition();
}
@Override
final boolean isMixin() {
return true;
}
+
+ @Override
+ final void emitChildTreeNode(final NormalizedNodeStreamWriter writer, final PathNode node) throws IOException {
+ final PathArgument element = node.element();
+ if (!(element instanceof NodeIdentifierWithPredicates childPath)) {
+ throw new IOException("Child identifier " + element + " is invalid in parent " + getIdentifier());
+ }
+
+ final StreamingContext<?> childOp = getChildOperation(childPath);
+ if (childPath.size() == 0 && node.isEmpty() || childPath.keySet().containsAll(keyLeaves)) {
+ // This is a query for the entire list, or the query specifies everything we need
+ childOp.streamToWriter(writer, childPath, node);
+ return;
+ }
+
+ // Inexact query, we need to also request the leaf nodes we need to for reconstructing a valid instance
+ // NodeIdentifierWithPredicates.
+ childOp.streamToWriter(writer, childPath, node.copyWith(keyLeaves.stream()
+ .filter(qname -> !childPath.containsKey(qname))
+ .map(NodeIdentifier::new)
+ .collect(Collectors.toUnmodifiableList())));
+ }
}
private abstract static class AbstractSimple<T extends PathArgument> extends StreamingContext<T> {
final boolean isMixin() {
return false;
}
+
+ @Override
+ final void streamToWriter(final NormalizedNodeStreamWriter writer, final PathArgument first,
+ final PathNode tree) throws IOException {
+ streamToWriter(writer, first, Collections.emptyIterator());
+ }
}
private static final class AnyXml extends AbstractSimple<NodeIdentifier> {
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startChoiceNode(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startChoiceNode(getIdentifier(), childSizeHint);
}
}
private static final class LeafListEntry extends AbstractSimple<NodeWithValue<?>> {
LeafListEntry(final LeafListSchemaNode potential) {
- super(new NodeWithValue<>(potential.getQName(), null));
+ super(new NodeWithValue<>(potential.getQName(), Empty.value()));
}
@Override
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
final NodeIdentifierWithPredicates identifier = (NodeIdentifierWithPredicates) arg;
- writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
+ writer.startMapEntryNode(identifier, childSizeHint);
for (Entry<QName, Object> entry : identifier.entrySet()) {
writer.startLeafNode(new NodeIdentifier(entry.getKey()));
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startUnkeyedListItem(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startUnkeyedListItem(getIdentifier(), childSizeHint);
}
}
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startContainerNode(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startContainerNode(getIdentifier(), childSizeHint);
}
}
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startOrderedLeafSet(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startOrderedLeafSet(getIdentifier(), childSizeHint);
}
}
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startLeafSet(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startLeafSet(getIdentifier(), childSizeHint);
}
}
private static final class Augmentation extends AbstractDataContainer<AugmentationIdentifier> {
Augmentation(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
super(DataSchemaContextNode.augmentationIdentifierFrom(augmentation),
- EffectiveAugmentationSchema.create(augmentation, schema));
+ new EffectiveAugmentationSchema(augmentation, schema));
}
@Override
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
writer.startAugmentationNode(getIdentifier());
}
}
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startMapNode(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startMapNode(getIdentifier(), childSizeHint);
}
}
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startOrderedMapNode(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startOrderedMapNode(getIdentifier(), childSizeHint);
}
}
UnkeyedListMixin(final ListSchemaNode list) {
super(NodeIdentifier.create(list.getQName()));
- this.innerNode = new UnkeyedListItem(list);
+ innerNode = new UnkeyedListItem(list);
}
@Override
}
@Override
- void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg) throws IOException {
- writer.startUnkeyedList(getIdentifier(), UNKNOWN_SIZE);
+ void emitElementStart(final NormalizedNodeStreamWriter writer, final PathArgument arg,
+ final int childSizeHint) throws IOException {
+ writer.startUnkeyedList(getIdentifier(), childSizeHint);
}
}
}