Use instanceof patterns in netconf-util
[netconf.git] / netconf / netconf-util / src / main / java / org / opendaylight / netconf / util / StreamingContext.java
index 52ce54fd6ecfa0c45cff658dc3298889b08bf826..7283841ef914ce3583e0cfa1b7a5c267253528d8 100644 (file)
@@ -8,25 +8,29 @@
 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;
@@ -59,7 +63,7 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         // 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);
                 }
             }
@@ -68,19 +72,20 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
     }
 
     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;
     }
 
@@ -91,9 +96,29 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
 
     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) {
@@ -135,15 +160,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         @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);
@@ -152,10 +171,37 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
             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);
@@ -190,9 +236,8 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         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());
         }
@@ -200,10 +245,12 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
 
     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
@@ -215,6 +262,28 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         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> {
@@ -231,6 +300,12 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         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> {
@@ -274,8 +349,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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);
         }
     }
 
@@ -295,7 +371,7 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
 
     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
@@ -320,9 +396,10 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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()));
@@ -343,8 +420,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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);
         }
     }
 
@@ -359,8 +437,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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);
         }
     }
 
@@ -389,8 +468,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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);
         }
     }
 
@@ -400,15 +480,16 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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
@@ -417,7 +498,8 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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());
         }
     }
@@ -428,8 +510,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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);
         }
     }
 
@@ -439,8 +522,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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);
         }
     }
 
@@ -449,7 +533,7 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
 
         UnkeyedListMixin(final ListSchemaNode list) {
             super(NodeIdentifier.create(list.getQName()));
-            this.innerNode = new UnkeyedListItem(list);
+            innerNode = new UnkeyedListItem(list);
         }
 
         @Override
@@ -463,8 +547,9 @@ abstract class StreamingContext<T extends PathArgument> implements Identifiable<
         }
 
         @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);
         }
     }
 }