Deduplicate MapNode key leaf values
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / stream / LithiumNormalizedNodeInputStreamReader.java
index 3b4f2dad05d1170a9cc96fef48b096693e2e5dea..e069d901d1b56aebd599148360456d97a214b574 100755 (executable)
@@ -12,6 +12,7 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.Sets;
 import java.io.DataInput;
 import java.io.IOException;
 import java.io.StringReader;
@@ -36,13 +37,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 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.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -65,11 +60,6 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
 
     private QName lastLeafSetQName;
 
-    private NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder;
-
-    @SuppressWarnings("rawtypes")
-    private NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder;
-
     LithiumNormalizedNodeInputStreamReader(final DataInput input) {
         this.input = requireNonNull(input);
     }
@@ -85,113 +75,196 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
     }
 
     @Override
-    public NormalizedNode<?, ?> readNormalizedNode() throws IOException {
-        return readNormalizedNodeInternal();
+    public void streamNormalizedNode(final NormalizedNodeStreamWriter writer) throws IOException {
+        streamNormalizedNode(requireNonNull(writer), input.readByte());
     }
 
-    private NormalizedNode<?, ?> readNormalizedNodeInternal() throws IOException {
-        // each node should start with a byte
-        byte nodeType = input.readByte();
-
-        if (nodeType == NodeTypes.END_NODE) {
-            LOG.trace("End node reached. return");
-            lastLeafSetQName = null;
-            return null;
-        }
-
+    private void streamNormalizedNode(final NormalizedNodeStreamWriter writer, final byte nodeType) throws IOException {
         switch (nodeType) {
+            case NodeTypes.ANY_XML_NODE:
+                streamAnyxml(writer);
+                break;
             case NodeTypes.AUGMENTATION_NODE:
-                AugmentationIdentifier augIdentifier = readAugmentationIdentifier();
-                LOG.trace("Reading augmentation node {} ", augIdentifier);
-                return addDataContainerChildren(Builders.augmentationBuilder().withNodeIdentifier(augIdentifier))
-                        .build();
-
+                streamAugmentation(writer);
+                break;
+            case NodeTypes.CHOICE_NODE:
+                streamChoice(writer);
+                break;
+            case NodeTypes.CONTAINER_NODE:
+                streamContainer(writer);
+                break;
+            case NodeTypes.LEAF_NODE:
+                streamLeaf(writer);
+                break;
+            case NodeTypes.LEAF_SET:
+                streamLeafSet(writer);
+                break;
+            case NodeTypes.ORDERED_LEAF_SET:
+                streamOrderedLeafSet(writer);
+                break;
             case NodeTypes.LEAF_SET_ENTRY_NODE:
-                final QName name = lastLeafSetQName != null ? lastLeafSetQName : readQName();
-                final Object value = readObject();
-                final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(name, value);
-                LOG.trace("Reading leaf set entry node {}, value {}", leafIdentifier, value);
-                return leafSetEntryBuilder().withNodeIdentifier(leafIdentifier).withValue(value).build();
-
+                streamLeafSetEntry(writer);
+                break;
             case NodeTypes.MAP_ENTRY_NODE:
-                final NodeIdentifierWithPredicates entryIdentifier = readNormalizedNodeWithPredicates();
-                LOG.trace("Reading map entry node {} ", entryIdentifier);
-                return addDataContainerChildren(Builders.mapEntryBuilder().withNodeIdentifier(entryIdentifier))
-                        .build();
-
+                streamMapEntry(writer);
+                break;
+            case NodeTypes.MAP_NODE:
+                streamMap(writer);
+                break;
+            case NodeTypes.ORDERED_MAP_NODE:
+                streamOrderedMap(writer);
+                break;
+            case NodeTypes.UNKEYED_LIST:
+                streamUnkeyedList(writer);
+                break;
+            case NodeTypes.UNKEYED_LIST_ITEM:
+                streamUnkeyedListItem(writer);
+                break;
             default:
-                return readNodeIdentifierDependentNode(nodeType, readNodeIdentifier());
+                throw new InvalidNormalizedNodeStreamException("Unexpected node " + nodeType);
         }
     }
 
-    private NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder() {
-        if (leafBuilder == null) {
-            leafBuilder = Builders.leafBuilder();
-        }
+    private void streamAnyxml(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming anyxml node {}", identifier);
+        writer.startAnyxmlNode(identifier);
+        writer.domSourceValue(readDOMSource());
+        writer.endNode();
+    }
 
-        return leafBuilder;
+    private void streamAugmentation(final NormalizedNodeStreamWriter writer) throws IOException {
+        final AugmentationIdentifier augIdentifier = readAugmentationIdentifier();
+        LOG.trace("Streaming augmentation node {}", augIdentifier);
+        writer.startAugmentationNode(augIdentifier);
+        commonStreamContainer(writer);
     }
 
-    @SuppressWarnings("rawtypes")
-    private NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder() {
-        if (leafSetEntryBuilder == null) {
-            leafSetEntryBuilder = Builders.leafSetEntryBuilder();
-        }
+    private void streamChoice(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming choice node {}", identifier);
+        writer.startChoiceNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
 
-        return leafSetEntryBuilder;
+    private void streamContainer(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming container node {}", identifier);
+        writer.startContainerNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
     }
 
-    private NormalizedNode<?, ?> readNodeIdentifierDependentNode(final byte nodeType, final NodeIdentifier identifier)
-        throws IOException {
+    private void streamLeaf(final NormalizedNodeStreamWriter writer) throws IOException {
+        startLeaf(writer);
+        endLeaf(writer, readObject());
+    }
 
-        switch (nodeType) {
-            case NodeTypes.LEAF_NODE:
-                LOG.trace("Read leaf node {}", identifier);
-                // Read the object value
-                return leafBuilder().withNodeIdentifier(identifier).withValue(readObject()).build();
+    // Leaf inside a MapEntryNode, it can potentially be a key leaf, in which case we want to de-duplicate values.
+    private void streamLeaf(final NormalizedNodeStreamWriter writer, final NodeIdentifierWithPredicates entryId)
+            throws IOException {
+        final NodeIdentifier identifier = startLeaf(writer);
+        final Object value = readObject();
+        final Object entryValue = entryId.getValue(identifier.getNodeType());
+        endLeaf(writer, entryValue == null ? value : entryValue);
+    }
 
-            case NodeTypes.ANY_XML_NODE:
-                LOG.trace("Read xml node");
-                return Builders.anyXmlBuilder().withNodeIdentifier(identifier).withValue(readDOMSource()).build();
+    private NodeIdentifier startLeaf(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming leaf node {}", identifier);
+        writer.startLeafNode(identifier);
+        return identifier;
+    }
 
-            case NodeTypes.MAP_NODE:
-                LOG.trace("Read map node {}", identifier);
-                return addDataContainerChildren(Builders.mapBuilder().withNodeIdentifier(identifier)).build();
+    private static void endLeaf(final NormalizedNodeStreamWriter writer, final Object value) throws IOException {
+        writer.scalarValue(value);
+        writer.endNode();
+    }
 
-            case NodeTypes.CHOICE_NODE:
-                LOG.trace("Read choice node {}", identifier);
-                return addDataContainerChildren(Builders.choiceBuilder().withNodeIdentifier(identifier)).build();
+    private void streamLeafSet(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming leaf set node {}", identifier);
+        writer.startLeafSet(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamLeafSet(writer, identifier);
+    }
 
-            case NodeTypes.ORDERED_MAP_NODE:
-                LOG.trace("Reading ordered map node {}", identifier);
-                return addDataContainerChildren(Builders.orderedMapBuilder().withNodeIdentifier(identifier)).build();
+    private void streamOrderedLeafSet(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming ordered leaf set node {}", identifier);
+        writer.startOrderedLeafSet(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamLeafSet(writer, identifier);
+    }
 
-            case NodeTypes.UNKEYED_LIST:
-                LOG.trace("Read unkeyed list node {}", identifier);
-                return addDataContainerChildren(Builders.unkeyedListBuilder().withNodeIdentifier(identifier)).build();
+    private void commonStreamLeafSet(final NormalizedNodeStreamWriter writer, final NodeIdentifier identifier)
+            throws IOException {
+        lastLeafSetQName = identifier.getNodeType();
+        try {
+            commonStreamContainer(writer);
+        } finally {
+            // Make sure we never leak this
+            lastLeafSetQName = null;
+        }
+    }
 
-            case NodeTypes.UNKEYED_LIST_ITEM:
-                LOG.trace("Read unkeyed list item node {}", identifier);
-                return addDataContainerChildren(Builders.unkeyedListEntryBuilder()
-                        .withNodeIdentifier(identifier)).build();
+    private void streamLeafSetEntry(final NormalizedNodeStreamWriter writer) throws IOException {
+        final QName name = lastLeafSetQName != null ? lastLeafSetQName : readQName();
+        final Object value = readObject();
+        final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(name, value);
+        LOG.trace("Streaming leaf set entry node {}, value {}", leafIdentifier, value);
+        writer.startLeafSetEntryNode(leafIdentifier);
+        writer.scalarValue(value);
+        writer.endNode();
+    }
 
-            case NodeTypes.CONTAINER_NODE:
-                LOG.trace("Read container node {}", identifier);
-                return addDataContainerChildren(Builders.containerBuilder().withNodeIdentifier(identifier)).build();
+    private void streamMap(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming map node {}", identifier);
+        writer.startMapNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
 
-            case NodeTypes.LEAF_SET:
-                LOG.trace("Read leaf set node {}", identifier);
-                return addLeafSetChildren(identifier.getNodeType(),
-                        Builders.leafSetBuilder().withNodeIdentifier(identifier)).build();
+    private void streamOrderedMap(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming ordered map node {}", identifier);
+        writer.startOrderedMapNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
 
-            case NodeTypes.ORDERED_LEAF_SET:
-                LOG.trace("Read ordered leaf set node {}", identifier);
-                return addLeafSetChildren(identifier.getNodeType(),
-                        Builders.orderedLeafSetBuilder().withNodeIdentifier(identifier)).build();
+    private void streamMapEntry(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifierWithPredicates entryIdentifier = readNormalizedNodeWithPredicates();
+        LOG.trace("Streaming map entry node {}", entryIdentifier);
+        writer.startMapEntryNode(entryIdentifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+
+        // Same loop as commonStreamContainer(), but ...
+        for (byte nodeType = input.readByte(); nodeType != NodeTypes.END_NODE; nodeType = input.readByte()) {
+            if (nodeType == NodeTypes.LEAF_NODE) {
+                // ... leaf nodes may need de-duplication
+                streamLeaf(writer, entryIdentifier);
+            } else {
+                streamNormalizedNode(writer, nodeType);
+            }
+        }
+        writer.endNode();
+    }
 
-            default:
-                return null;
+    private void streamUnkeyedList(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming unkeyed list node {}", identifier);
+        writer.startUnkeyedList(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void streamUnkeyedListItem(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming unkeyed list item node {}", identifier);
+        writer.startUnkeyedListItem(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void commonStreamContainer(final NormalizedNodeStreamWriter writer) throws IOException {
+        for (byte nodeType = input.readByte(); nodeType != NodeTypes.END_NODE; nodeType = input.readByte()) {
+            streamNormalizedNode(writer, nodeType);
         }
+        writer.endNode();
     }
 
     private DOMSource readDOMSource() throws IOException {
@@ -207,17 +280,17 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
         }
     }
 
-    QName readQName() throws IOException {
+    @Override
+    public QName readQName() throws IOException {
         // Read in the same sequence of writing
         String localName = readCodedString();
         String namespace = readCodedString();
         String revision = Strings.emptyToNull(readCodedString());
 
-        return QNameFactory.create(new QNameFactory.Key(localName, namespace, revision));
+        return QNameFactory.create(localName, namespace, revision);
     }
 
-
-    private String readCodedString() throws IOException {
+    final String readCodedString() throws IOException {
         final byte valueType = input.readByte();
         switch (valueType) {
             case TokenTypes.IS_NULL_VALUE:
@@ -240,8 +313,8 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
 
     private Set<QName> readQNameSet() throws IOException {
         // Read the children count
-        int count = input.readInt();
-        Set<QName> children = new HashSet<>(count);
+        final int count = input.readInt();
+        final Set<QName> children = Sets.newHashSetWithExpectedSize(count);
         for (int i = 0; i < count; i++) {
             children.add(readQName());
         }
@@ -249,7 +322,7 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
     }
 
     AugmentationIdentifier readAugmentationIdentifier() throws IOException {
-        return new AugmentationIdentifier(readQNameSet());
+        return AugmentationIdentifier.create(readQNameSet());
     }
 
     NodeIdentifier readNodeIdentifier() throws IOException {
@@ -261,9 +334,9 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
         final int count = input.readInt();
         switch (count) {
             case 0:
-                return new NodeIdentifierWithPredicates(qname);
+                return NodeIdentifierWithPredicates.of(qname);
             case 1:
-                return new NodeIdentifierWithPredicates(qname, readQName(), readObject());
+                return NodeIdentifierWithPredicates.of(qname, readQName(), readObject());
             default:
                 // ImmutableList is used by ImmutableOffsetMapTemplate for lookups, hence we use that.
                 final Builder<QName> keys = ImmutableList.builderWithExpectedSize(count);
@@ -273,7 +346,7 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
                     values[i] = readObject();
                 }
 
-                return new NodeIdentifierWithPredicates(qname, ImmutableOffsetMapTemplate.ordered(keys.build())
+                return NodeIdentifierWithPredicates.of(qname, ImmutableOffsetMapTemplate.ordered(keys.build())
                     .instantiateWithValues(values));
         }
     }
@@ -396,35 +469,4 @@ class LithiumNormalizedNodeInputStreamReader extends ForwardingDataInput impleme
                 return null;
         }
     }
-
-    @SuppressWarnings("unchecked")
-    private ListNodeBuilder<Object, LeafSetEntryNode<Object>> addLeafSetChildren(final QName nodeType,
-            final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder) throws IOException {
-
-        LOG.trace("Reading children of leaf set");
-
-        lastLeafSetQName = nodeType;
-
-        LeafSetEntryNode<Object> child = (LeafSetEntryNode<Object>)readNormalizedNodeInternal();
-
-        while (child != null) {
-            builder.withChild(child);
-            child = (LeafSetEntryNode<Object>)readNormalizedNodeInternal();
-        }
-        return builder;
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    private NormalizedNodeContainerBuilder addDataContainerChildren(
-            final NormalizedNodeContainerBuilder builder) throws IOException {
-        LOG.trace("Reading data container (leaf nodes) nodes");
-
-        NormalizedNode<?, ?> child = readNormalizedNodeInternal();
-
-        while (child != null) {
-            builder.addChild(child);
-            child = readNormalizedNodeInternal();
-        }
-        return builder;
-    }
 }