Allow startAnyxmlNode() to handle differing object models
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / ImmutableNormalizedNodeStreamWriter.java
index bb24d867b26f1ee3516ec5583d395c91c75da050..d8b9dc438e17282277b1941dff256d14ac82fb87 100644 (file)
@@ -11,38 +11,28 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
+import java.io.IOException;
 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.odlext.model.api.YangModeledAnyxmlSchemaNode;
 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.ChoiceNode;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-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.ImmutableAnydataNodeBuilder;
 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.ImmutableLeafNodeBuilder;
 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;
@@ -65,15 +55,18 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
  * <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<NormalizedNodeBuilder> builders = new ArrayDeque<>();
+
     private DataSchemaNode nextSchema;
 
     @SuppressWarnings("rawtypes")
-    protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeContainerBuilder topLevelBuilder) {
+    protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeBuilder topLevelBuilder) {
         builders.push(topLevelBuilder);
     }
 
@@ -114,235 +107,144 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
      * @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 NormalizedNodeResult result) {
-        return new ImmutableNormalizedNodeStreamWriter(result);
-    }
-
-    @SuppressWarnings("rawtypes")
-    protected NormalizedNodeBuilder getCurrent() {
-        return builders.peek();
-    }
-
-    @SuppressWarnings("rawtypes")
-    protected final NormalizedNodeContainerBuilder getCurrentContainer() {
-        final NormalizedNodeBuilder current = getCurrent();
-        if (current == null) {
-            return null;
-        }
-        checkState(current instanceof NormalizedNodeContainerBuilder, "%s is not a node container", current);
-        return (NormalizedNodeContainerBuilder) current;
-    }
-
-    @SuppressWarnings("rawtypes")
-    protected NormalizedNodeBuilder getCurrentScalar() {
-        final NormalizedNodeBuilder current = getCurrent();
-        checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);
-        return current;
-    }
-
-    @SuppressWarnings("rawtypes")
-    private void enter(final NormalizedNodeBuilder next) {
-        builders.push(next);
-        nextSchema = null;
-    }
-
-    @SuppressWarnings("unchecked")
-    protected void writeChild(final NormalizedNode<?, ?> child) {
-        getCurrentContainer().addChild(child);
+    public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
+        return result instanceof NormalizedNodeMetadataResult ? from((NormalizedNodeMetadataResult) result)
+                : new ImmutableNormalizedNodeStreamWriter(result);
     }
 
-    @Override
-    @SuppressWarnings({"rawtypes","unchecked"})
-    public void endNode() {
-        final NormalizedNodeBuilder finishedBuilder = builders.poll();
-        checkState(finishedBuilder != null, "Node which should be closed does not exists.");
-        final NormalizedNodeContainerBuilder current = getCurrentContainer();
-        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 @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeMetadataResult result) {
+        return new ImmutableMetadataNormalizedNodeStreamWriter(result);
     }
 
     @Override
     public void startLeafNode(final NodeIdentifier name) {
         checkDataNodeContainer();
-        enter(InterningLeafNodeBuilder.forSchema(nextSchema).withNodeIdentifier(name));
+        enter(name, leafNodeBuilder(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 startLeafSetEntryNode(final NodeWithValue<?> name) {
-        @SuppressWarnings("rawtypes")
-        final NormalizedNodeBuilder current = getCurrent();
-        checkArgument(current instanceof ImmutableOrderedLeafSetNodeBuilder
-            || current instanceof ImmutableLeafSetNodeBuilder, "LeafSetEntryNode is not valid for parent %s", current);
-        enter(ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(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, leafsetEntryNodeBuilder());
         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 startAnyxmlNode(final NodeIdentifier name) {
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) {
         checkDataNodeContainer();
-        enter(ImmutableAnyXmlNodeBuilder.create().withNodeIdentifier(name));
-        nextSchema = null;
+        if (DOMSource.class.isAssignableFrom(objectModel)) {
+            enter(name, ImmutableAnyXmlNodeBuilder.create());
+            nextSchema = null;
+            return true;
+        }
+        return false;
     }
 
     @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
     public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
 
-        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));
+        checkArgument(nextSchema instanceof YangModeledAnyxmlSchemaNode,
+                "Schema of this node should be instance of YangModeledAnyxmlSchemaNode");
+        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));
-    }
-
-    private void checkDataNodeContainer() {
-        @SuppressWarnings("rawtypes")
-        final NormalizedNodeContainerBuilder current = getCurrentContainer();
-        if (!(current instanceof NormalizedNodeResultBuilder)) {
-            checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
-        }
-    }
-
-    @SuppressWarnings("rawtypes")
-    protected static final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
-
-        private final NormalizedNodeResult result;
-
-        public NormalizedNodeResultBuilder(final NormalizedNodeResult result) {
-            this.result = result;
-        }
-
-        @Override
-        public NormalizedNodeBuilder withValue(final Object value) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public NormalizedNodeContainerBuilder withValue(final Collection value) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public NormalizedNode build() {
-            throw new IllegalStateException("Can not close NormalizedNodeResult");
-        }
-
-        @Override
-        public NormalizedNodeContainerBuilder withNodeIdentifier(final PathArgument nodeIdentifier) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public NormalizedNodeContainerBuilder addChild(final NormalizedNode child) {
-            result.setResult(child);
-            return this;
-        }
-
-        @Override
-        public NormalizedNodeContainerBuilder removeChild(final PathArgument key) {
-            throw new UnsupportedOperationException();
-        }
+        checkArgument(!(current() instanceof ImmutableAugmentationNodeBuilder));
+        enter(identifier, Builders.augmentationBuilder());
     }
 
     @Override
@@ -362,11 +264,105 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
 
     @Override
     public void scalarValue(final Object value) {
-        getCurrentScalar().withValue(value);
+        currentScalar().withValue(value);
     }
 
     @Override
     public void domSourceValue(final DOMSource value) {
-        getCurrentScalar().withValue(value);
+        currentScalar().withValue(value);
+    }
+
+    @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;
+
+        writeChild(product);
+    }
+
+    @Override
+    public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        checkDataNodeContainer();
+        enter(name, ImmutableAnydataNodeBuilder.create(objectModel));
+        // We support all object models
+        return true;
+    }
+
+    /**
+     * 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));
+    }
+
+    // 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();
+    }
+
+    final void reset(final NormalizedNodeResultBuilder builder) {
+        nextSchema = null;
+        builders.clear();
+        builders.push(builder);
+    }
+
+    private <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder(final DataSchemaNode schema) {
+        final InterningLeafNodeBuilder<T> interning = InterningLeafNodeBuilder.forSchema(schema);
+        return interning != null ? interning : leafNodeBuilder();
+    }
+
+    <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder() {
+        return new ImmutableLeafNodeBuilder<>();
+    }
+
+    <T> ImmutableLeafSetEntryNodeBuilder<T> leafsetEntryNodeBuilder() {
+        return ImmutableLeafSetEntryNodeBuilder.create();
+    }
+
+    private void checkDataNodeContainer() {
+        @SuppressWarnings("rawtypes")
+        final NormalizedNodeContainerBuilder current = currentContainer();
+        if (!(current instanceof NormalizedNodeResultBuilder)) {
+            checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeBuilder current() {
+        return builders.peek();
+    }
+
+    @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;
+    }
+
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeBuilder currentScalar() {
+        final NormalizedNodeBuilder current = current();
+        checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);
+        return current;
     }
 }