Add ReusableImmutableNormalizedNodeStreamWriter
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / ImmutableNormalizedNodeStreamWriter.java
index b91c06dd095a848c854b48fb3b89c8c88a64c56c..2b2e5857fbdc30205a86493a2254a820ab1bfacd 100644 (file)
@@ -12,39 +12,25 @@ import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
 import java.util.ArrayDeque;
-import java.util.Collection;
 import java.util.Deque;
 import javax.xml.transform.dom.DOMSource;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyXmlSchemaNode;
-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.AnyXmlNode;
-import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-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.MapEntryNode;
-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.OrderedMapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
-import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
-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.impl.schema.builder.impl.ImmutableAnyXmlNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
@@ -53,9 +39,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOr
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableYangModeledAnyXmlNodeBuilder;
-import org.opendaylight.yangtools.yang.data.util.LeafInterner;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 
 /**
  * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
@@ -68,15 +52,18 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
  * <p>
  * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
  * written to this builder.
+ *
+ * <p>
+ * This class is not final for purposes of customization, normal users should not need to subclass it.
  */
 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
-
     @SuppressWarnings("rawtypes")
-    private final Deque<NormalizedNodeContainerBuilder> builders = new ArrayDeque<>();
+    private final Deque<NormalizedNodeBuilder> builders = new ArrayDeque<>();
+
     private DataSchemaNode nextSchema;
 
     @SuppressWarnings("rawtypes")
-    protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeContainerBuilder topLevelBuilder) {
+    protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeBuilder topLevelBuilder) {
         builders.push(topLevelBuilder);
     }
 
@@ -118,113 +105,74 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
      * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
      */
     public static NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
-        return new ImmutableNormalizedNodeStreamWriter(result);
-    }
-
-    protected Deque<NormalizedNodeContainerBuilder> getBuilders() {
-        return builders;
-    }
-
-    @SuppressWarnings("rawtypes")
-    protected NormalizedNodeContainerBuilder getCurrent() {
-        return builders.peek();
-    }
-
-    @SuppressWarnings("rawtypes")
-    private void enter(final NormalizedNodeContainerBuilder next) {
-        builders.push(next);
-        nextSchema = null;
-    }
-
-    @SuppressWarnings("unchecked")
-    protected void writeChild(final NormalizedNode<?, ?> child) {
-        getCurrent().addChild(child);
+        return result instanceof NormalizedNodeMetadataResult ? from((NormalizedNodeMetadataResult) result)
+                : new ImmutableNormalizedNodeStreamWriter(result);
     }
 
-    @Override
-    @SuppressWarnings({"rawtypes","unchecked"})
-    public void endNode() {
-        final NormalizedNodeContainerBuilder finishedBuilder = builders.poll();
-        checkState(finishedBuilder != null, "Node which should be closed does not exists.");
-        final NormalizedNodeContainerBuilder current = getCurrent();
-        checkState(current != null, "Reached top level node, which could not be closed in this writer.");
-        final NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
-        current.addChild(product);
-        nextSchema = null;
+    /**
+     * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
+     * (type of NormalizedNode) is determined by first start event.
+     *
+     * <p>
+     * Result is built when {@link #endNode()} associated with that start event is emitted.
+     *
+     * <p>
+     * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
+     * of the first event.
+     *
+     * <p>
+     * This method is useful for clients, which knows there will be one top-level node written, but does not know which
+     * type of {@link NormalizedNode} will be written.
+     *
+     * @param result {@link NormalizedNodeResult} object which will hold result value.
+     * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
+     */
+    public static NormalizedNodeStreamWriter from(final NormalizedNodeMetadataResult result) {
+        return new ImmutableMetadataNormalizedNodeStreamWriter(result);
     }
 
     @Override
-    public void leafNode(final NodeIdentifier name, final Object value) {
+    public void startLeafNode(final NodeIdentifier name) {
         checkDataNodeContainer();
-
-        final LeafNode<Object> sample = ImmutableNodes.leafNode(name, value);
-        final LeafNode<?> node;
-        if (nextSchema instanceof LeafSchemaNode) {
-            node = LeafInterner.forSchema((LeafSchemaNode)nextSchema).intern(sample);
-        } else {
-            node = sample;
-        }
-
-        writeChild(node);
+        enter(name, InterningLeafNodeBuilder.forSchema(nextSchema));
         nextSchema = null;
     }
 
     @Override
     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = UNKNOWN_SIZE == childSizeHint
-                ? InterningLeafSetNodeBuilder.create(nextSchema)
-                        : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint);
-        builder.withNodeIdentifier(name);
-        enter(builder);
+        enter(name, UNKNOWN_SIZE == childSizeHint ? InterningLeafSetNodeBuilder.create(nextSchema)
+                : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint));
     }
 
     @Override
-    public void leafSetEntryNode(final QName name, final Object value) {
-        if (getCurrent() instanceof ImmutableOrderedLeafSetNodeBuilder) {
-            @SuppressWarnings("unchecked")
-            ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder =
-                (ImmutableOrderedLeafSetNodeBuilder<Object>) getCurrent();
-            builder.withChildValue(value);
-        } else if (getCurrent() instanceof ImmutableLeafSetNodeBuilder) {
-            @SuppressWarnings("unchecked")
-            ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder =
-                (ImmutableLeafSetNodeBuilder<Object>) getCurrent();
-            builder.withChildValue(value);
-        } else {
-            throw new IllegalArgumentException("LeafSetEntryNode is not valid for parent " + getCurrent());
-        }
-
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) {
+        final NormalizedNodeBuilder<?, ?, ?> current = current();
+        checkArgument(current instanceof ImmutableLeafSetNodeBuilder
+            || current instanceof ImmutableOrderedLeafSetNodeBuilder || current instanceof NormalizedNodeResultBuilder,
+            "LeafSetEntryNode is not valid for parent %s", current);
+        enter(name, ImmutableLeafSetEntryNodeBuilder.create());
         nextSchema = null;
     }
 
     @Override
     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.orderedLeafSetBuilder();
-        builder.withNodeIdentifier(name);
-        enter(builder);
+        enter(name, Builders.orderedLeafSetBuilder());
     }
 
     @Override
-    public void anyxmlNode(final NodeIdentifier name, final Object value) {
+    public void startAnyxmlNode(final NodeIdentifier name) {
         checkDataNodeContainer();
-
-        final AnyXmlNode node = ImmutableAnyXmlNodeBuilder.create().withNodeIdentifier(name)
-                .withValue((DOMSource) value).build();
-        writeChild(node);
-
+        enter(name, ImmutableAnyXmlNodeBuilder.create());
         nextSchema = null;
     }
 
     @Override
     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-
-        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
-                UNKNOWN_SIZE == childSizeHint ? ImmutableContainerNodeBuilder.create()
-                        : ImmutableContainerNodeBuilder.create(childSizeHint);
-        enter(builder.withNodeIdentifier(name));
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableContainerNodeBuilder.create()
+                : ImmutableContainerNodeBuilder.create(childSizeHint));
     }
 
     @Override
@@ -233,142 +181,160 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
 
         checkArgument(nextSchema instanceof YangModeledAnyXmlSchemaNode,
                 "Schema of this node should be instance of YangModeledAnyXmlSchemaNode");
-        final DataContainerNodeAttrBuilder<NodeIdentifier, YangModeledAnyXmlNode> builder =
-                UNKNOWN_SIZE == childSizeHint
-                ? ImmutableYangModeledAnyXmlNodeBuilder.create((YangModeledAnyXmlSchemaNode) nextSchema)
-                        : ImmutableYangModeledAnyXmlNodeBuilder.create(
-                (YangModeledAnyXmlSchemaNode) nextSchema, childSizeHint);
-        enter(builder.withNodeIdentifier(name));
+        final YangModeledAnyXmlSchemaNode anyxmlSchema = (YangModeledAnyXmlSchemaNode) nextSchema;
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableYangModeledAnyXmlNodeBuilder.create(anyxmlSchema)
+                : ImmutableYangModeledAnyXmlNodeBuilder.create(anyxmlSchema, childSizeHint));
     }
 
     @Override
     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-
-        final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder =
-                UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListNodeBuilder.create()
-                        : ImmutableUnkeyedListNodeBuilder.create(childSizeHint);
-        enter(builder.withNodeIdentifier(name));
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListNodeBuilder.create()
+                : ImmutableUnkeyedListNodeBuilder.create(childSizeHint));
     }
 
     @Override
     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
-        checkArgument(getCurrent() instanceof NormalizedNodeResultBuilder
-                || getCurrent() instanceof ImmutableUnkeyedListNodeBuilder);
-        final DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder =
-                UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListEntryNodeBuilder.create()
-                        : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint);
-        enter(builder.withNodeIdentifier(name));
+        final NormalizedNodeBuilder<?, ?, ?> current = current();
+        checkArgument(current instanceof ImmutableUnkeyedListNodeBuilder
+            || current instanceof NormalizedNodeResultBuilder);
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListEntryNodeBuilder.create()
+                : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint));
     }
 
     @Override
     public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-
-        final CollectionNodeBuilder<MapEntryNode, MapNode> builder = UNKNOWN_SIZE == childSizeHint
-                ? ImmutableMapNodeBuilder.create() : ImmutableMapNodeBuilder.create(childSizeHint);
-        enter(builder.withNodeIdentifier(name));
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableMapNodeBuilder.create()
+                : ImmutableMapNodeBuilder.create(childSizeHint));
     }
 
     @Override
     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
-        if (!(getCurrent() instanceof NormalizedNodeResultBuilder)) {
-            checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder
-                || getCurrent() instanceof ImmutableOrderedMapNodeBuilder);
-        }
+        final NormalizedNodeBuilder<?, ?, ?> current = current();
+        checkArgument(current instanceof ImmutableMapNodeBuilder || current instanceof ImmutableOrderedMapNodeBuilder
+            || current instanceof NormalizedNodeResultBuilder);
 
-        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
-                UNKNOWN_SIZE == childSizeHint ? ImmutableMapEntryNodeBuilder.create()
-                        : ImmutableMapEntryNodeBuilder.create(childSizeHint);
-        enter(builder.withNodeIdentifier(identifier));
+        enter(identifier, UNKNOWN_SIZE == childSizeHint ? ImmutableMapEntryNodeBuilder.create()
+                : ImmutableMapEntryNodeBuilder.create(childSizeHint));
     }
 
     @Override
     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-
-        final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> builder = UNKNOWN_SIZE == childSizeHint
-                ? ImmutableOrderedMapNodeBuilder.create() : ImmutableOrderedMapNodeBuilder.create(childSizeHint);
-        enter(builder.withNodeIdentifier(name));
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableOrderedMapNodeBuilder.create()
+                : ImmutableOrderedMapNodeBuilder.create(childSizeHint));
     }
 
     @Override
     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-
-        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = UNKNOWN_SIZE == childSizeHint
-                ? ImmutableChoiceNodeBuilder.create() : ImmutableChoiceNodeBuilder.create(childSizeHint);
-        enter(builder.withNodeIdentifier(name));
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableChoiceNodeBuilder.create()
+                : ImmutableChoiceNodeBuilder.create(childSizeHint));
     }
 
     @Override
     public void startAugmentationNode(final AugmentationIdentifier identifier) {
         checkDataNodeContainer();
-        checkArgument(!(getCurrent() instanceof ImmutableAugmentationNodeBuilder));
-        enter(Builders.augmentationBuilder().withNodeIdentifier(identifier));
+        checkArgument(!(current() instanceof ImmutableAugmentationNodeBuilder));
+        enter(identifier, Builders.augmentationBuilder());
     }
 
-    private void checkDataNodeContainer() {
-        @SuppressWarnings("rawtypes")
-        final NormalizedNodeContainerBuilder current = getCurrent();
-        if (!(current instanceof NormalizedNodeResultBuilder)) {
-            checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
-        }
+    @Override
+    public void flush() {
+        // no-op
     }
 
-    @SuppressWarnings("rawtypes")
-    protected static final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
+    @Override
+    public void close() {
+        // no-op
+    }
 
-        private final NormalizedNodeResult result;
+    @Override
+    public void nextDataSchemaNode(final DataSchemaNode schema) {
+        nextSchema = requireNonNull(schema);
+    }
 
-        public NormalizedNodeResultBuilder(final NormalizedNodeResult result) {
-            this.result = result;
-        }
+    @Override
+    public void scalarValue(final Object value) {
+        currentScalar().withValue(value);
+    }
 
-        @Override
-        public NormalizedNodeBuilder withValue(final Object value) {
-            throw new UnsupportedOperationException();
-        }
+    @Override
+    public void domSourceValue(final DOMSource value) {
+        currentScalar().withValue(value);
+    }
 
-        @Override
-        public NormalizedNodeContainerBuilder withValue(final Collection value) {
-            throw new UnsupportedOperationException();
-        }
+    @Override
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void endNode() {
+        final NormalizedNodeBuilder finishedBuilder = builders.poll();
+        checkState(finishedBuilder != null, "Node which should be closed does not exists.");
+        final NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
+        nextSchema = null;
 
-        @Override
-        public NormalizedNode build() {
-            throw new IllegalStateException("Can not close NormalizedNodeResult");
-        }
+        writeChild(product);
+    }
 
-        @Override
-        public NormalizedNodeContainerBuilder withNodeIdentifier(final PathArgument nodeIdentifier) {
-            throw new UnsupportedOperationException();
-        }
+    /**
+     * Add a child not to the currently-open builder.
+     *
+     * @param child A new child
+     * @throws NullPointerException if {@code child} is null
+     * @throws IllegalStateException if there is no open builder
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected final void writeChild(final NormalizedNode<?, ?> child) {
+        final NormalizedNodeContainerBuilder current = currentContainer();
+        checkState(current != null, "Reached top level node, which could not be closed in this writer.");
+        current.addChild(requireNonNull(child));
+    }
 
-        @Override
-        public NormalizedNodeContainerBuilder addChild(final NormalizedNode child) {
-            result.setResult(child);
-            return this;
-        }
+    // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
+    @SuppressWarnings("rawtypes")
+    void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
+        builders.push(next.withNodeIdentifier(identifier));
+        nextSchema = null;
+    }
+
+    // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
+    protected final NormalizedNodeBuilder popBuilder() {
+        return builders.pop();
+    }
 
-        @Override
-        public NormalizedNodeContainerBuilder removeChild(final PathArgument key) {
-            throw new UnsupportedOperationException();
+    final void reset(final NormalizedNodeResultBuilder builder) {
+        nextSchema = null;
+        builders.clear();
+        builders.push(builder);
+    }
+
+    private void checkDataNodeContainer() {
+        @SuppressWarnings("rawtypes")
+        final NormalizedNodeContainerBuilder current = currentContainer();
+        if (!(current instanceof NormalizedNodeResultBuilder)) {
+            checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
         }
     }
 
-    @Override
-    public void flush() {
-        // no-op
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeBuilder current() {
+        return builders.peek();
     }
 
-    @Override
-    public void close() {
-        // no-op
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeContainerBuilder currentContainer() {
+        final NormalizedNodeBuilder current = current();
+        if (current == null) {
+            return null;
+        }
+        checkState(current instanceof NormalizedNodeContainerBuilder, "%s is not a node container", current);
+        return (NormalizedNodeContainerBuilder) current;
     }
 
-    @Override
-    public void nextDataSchemaNode(final DataSchemaNode schema) {
-        nextSchema = requireNonNull(schema);
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeBuilder currentScalar() {
+        final NormalizedNodeBuilder current = current();
+        checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);
+        return current;
     }
 }