Bug 1437, Bug 1438 NormalizedNode Stream Writer API and Implementation 96/9496/5
authorTony Tkacik <ttkacik@cisco.com>
Wed, 30 Jul 2014 15:09:08 +0000 (17:09 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Fri, 1 Aug 2014 17:32:37 +0000 (19:32 +0200)
Initial draft of NormalizedNode Stream Writer API, along
with initial implementation which produces immutable NormalizedNodes.

Change-Id: I45513d88eefca7aa070e6679ccfdedf358266d59
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java [new file with mode: 0644]

diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..96b5c15
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.stream;
+
+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;
+
+
+/**
+ * Event Stream Writer based on Normalized Node tree representation
+ *
+ * <h3>Writing Event Stream</h3>
+ *
+ * <ul>
+ * <li><code>container</code> - Container node representation, start event is
+ * emitted using {@link #startContainerNode(NodeIdentifier, int)} and node end event is
+ * emitted using {@link #endNode()}. Container node is implementing
+ * {@link DataObject} interface.
+ *
+ * <li><code>list</code> - YANG list statement has two representation in event
+ * stream - unkeyed list and map. Unkeyed list is YANG list which did not
+ * specify key.</li>
+ *
+ * <ul>
+ * <li><code>Map</code> - Map start event is emitted using
+ * {@link #startMapNode(NodeIdentifier, int)} and is ended using {@link #endNode()}. Each map
+ * entry start is emitted using {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)} with Map of keys
+ * and finished using {@link #endNode()}.</li>
+ *
+ * <li><code>UnkeyedList</code> - Unkeyed list represent list without keys,
+ * unkeyed list start is emmited using {@link #startUnkeyedList(NodeIdentifier, int)} list
+ * end is emmited using {@link #endNode()}. Each list item is emmited using
+ * {@link #startUnkeyedListItem(NodeIdentifier, int)} and ended using {@link #endNode()}.</li>
+ * </ul>
+ *
+ * <li><code>leaf</code> - Leaf node event is emitted using
+ * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emmited for
+ * leaf node.</li>
+ *
+ * <li><code>leaf-list</code> - Leaf list start is emitted using
+ * {@link #startLeafSet(NodeIdentifier, int)}. Leaf list end is emitted using
+ * {@link #endNode()}. Leaf list entries are emmited using
+ * {@link #leafSetEntryNode(Object).
+ *
+ * <li><code>anyxml - Anyxml node event is emitted using
+ * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emmited
+ * for anyxml node.</code></li>
+ *
+ *
+ * <li><code>choice</code> Choice node event is emmited by
+ * {@link #startChoiceNode(NodeIdentifier, int)} event and
+ * finished by invoking {@link #endNode()}
+ * <li>
+ * <code>augment</code> - Represents augmentation, augmentation node is started
+ * by invoking {@link #startAugmentationNode(AugmentationIdentifier)} and
+ * finished by invoking {@link #endNode()}.</li>
+ *
+ * </ul>
+ *
+ * <h3>Implementation notes</h3>
+ *
+ * <p>
+ * Implementations of this interface must not hold user suppled objects
+ * and resources needlessly.
+ *
+ */
+public interface NormalizedNodeStreamWriter {
+
+    /**
+     * Methods in this interface allow users to hint the underlying
+     * implementation about the sizing of container-like constructurs
+     * (leafLists, containers, etc.). These hints may be taken into account by a
+     * particular implementation to improve performance, but clients are not
+     * required to provide hints. This constant should be used by clients who
+     * either do not have the sizing information, or do not wish to divulge it
+     * (for whatever reasons). Implementations are free to ignore these hints
+     * completely, but if they do use them, they are expected to be resilient in
+     * face of missing and mismatched hints, which is to say the user can
+     * specify startLeafSet(..., 1) and then call leafNode() 15 times.
+     * <p>
+     * The acceptable hint values are non-negative integers and this constant,
+     * all other values will result, based on implementation preference, in the
+     * hint being completely ignored or IllegalArgumentException being thrown.
+     */
+    public final int UNKNOWN_SIZE = -1;
+
+    /**
+     *
+     * Emits a leaf node event with supplied value.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param value
+     *            Value of leaf node. v
+     * @throws IllegalArgumentException
+     *             If emitted leaf node has invalid value in current context or
+     *             was emitted multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void leafNode(NodeIdentifier name, Object value) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits a start of leaf set (leaf-list).
+     * <p>
+     * Emits start of leaf set, during writing leaf set event, only
+     * {@link #leafSetEntryNode(Object)} calls are valid. Leaf set event is
+     * finished by calling {@link #endNode()}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted leaf node is invalid in current context or was
+     *             emitted multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startLeafSet(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits a leaf set entry node
+     *
+     * @param value
+     *            Value of leaf set entry node. Supplied object MUST BE constant over time.
+     * @throws IllegalArgumentException
+     *             If emitted leaf node has invalid value.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>leaf set</code> node.
+     */
+    void leafSetEntryNode(Object value) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of new container.
+     *
+     * <p>
+     * End of container event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+    * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
+    * </ul>
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted node is invalid in current context or was emitted
+     *             multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startContainerNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of unkeyed list node event.
+     *
+     * <p>
+     * End of unkeyed list event is emitted by invoking {@link #endNode()}.
+     * Valid subevents is only {@link #startUnkeyedListItem(NodeIdentifier, int)}. All other
+     * methods will throw {@link IllegalArgumentException}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted node is invalid in current context or was emitted
+     *             multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits start of new unkeyed list item.
+     *
+     * <p>
+     * Unkeyed list item event is finished by invoking {@link #endNode()}. Valid
+     * sub-events are:
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+     * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
+     * </ul>
+     *
+     * @param name Identifier of node
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>unkeyed list</code> node.
+     */
+    void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IllegalStateException;
+
+    /**
+     *
+     * Emits start of map node event.
+     *
+     * <p>
+     * End of map node event is emitted by invoking {@link #endNode()}. Valid
+     * subevents is only
+     * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}. All other
+     * methods will throw {@link IllegalArgumentException}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startMapNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of map entry.
+     *
+     * <p>
+     * End of map entry event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+     * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
+     * </ul>
+     *
+     *
+     * @param identifier
+     *            QName to value pairs of keys of map entry node. Values  MUST BE constant over time.
+     * @throws IllegalArgumentException
+     *             If key contains incorrect value.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>map entry</code> node.
+     */
+    void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of map node event.
+     *
+     * <p>
+     * End of map node event is emitted by invoking {@link #endNode()}. Valid
+     * subevents is only
+     * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}. All other
+     * methods will throw {@link IllegalArgumentException}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     *
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits start of augmentation node.
+     *
+     * <p>
+     * End of augmentation event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     *
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+     * </ul>
+     *
+     * @param identifier
+     *            Augmentation identifier
+     * @throws IllegalArgumentException
+     *             If augmentation is invalid in current context.
+     */
+    void startAugmentationNode(AugmentationIdentifier identifier) throws IllegalArgumentException;
+
+    /**
+     * Emits anyxml node event.
+     *
+     *
+     * @param name
+     * @param value
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void anyxmlNode(NodeIdentifier name, Object value) throws IllegalArgumentException;
+
+    /**
+     * Emits end event for node.
+     *
+     * @throws IllegalStateException If there is no start* event to be closed.B
+     *
+     */
+    void endNode() throws IllegalStateException;
+
+}
index 5d3fadb02a80b4a151dab62dcd06baeada4068cf..fc5c20e6fde66e21fa0c6e9159dfadbf0b1dfc80 100644 (file)
@@ -35,13 +35,32 @@ public final class ImmutableNodes {
         return ImmutableMapNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(name));
     }
 
-    public static final <T> LeafNode<T> leafNode(final QName name,final T value) {
+    /**
+     * Construct immutable leaf node
+     *
+     * @param name Identifier of leaf node
+     * @param value Value of leaf node
+     * @return Leaf node with supplied identifier and value
+     */
+    public static final <T> LeafNode<T> leafNode(final NodeIdentifier name,final T value) {
         return ImmutableLeafNodeBuilder.<T>create()
-                .withNodeIdentifier(new NodeIdentifier(name))
+                .withNodeIdentifier(name)
                 .withValue(value)
                 .build();
     }
 
+    /**
+     *
+     * Construct immutable leaf node
+     *
+     * @param name QName which will be used as node identifier
+     * @param value Value of leaf node.
+     * @return Leaf node with supplied identifier and value
+     */
+    public static final <T> LeafNode<T> leafNode(final QName name,final T value) {
+        return leafNode(new NodeIdentifier(name), value);
+    }
+
     public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder(final QName nodeName,final QName keyName,final Object keyValue) {
         return ImmutableMapEntryNodeBuilder.create()
                 .withNodeIdentifier(new NodeIdentifierWithPredicates(nodeName, keyName,keyValue))
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..21fda47
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema;
+
+import com.google.common.base.Preconditions;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+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.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+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.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.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+
+/**
+ *
+ * Implementation of {@link NormalizedNodeStreamWriter}, which constructs
+ * immutable instances of {@link NormalizedNode}s.
+ * <p>
+ * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)}
+ * where resulting NormalizedNode will be stored in supplied result object.
+ *
+ * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)},
+ * where all created nodes will be written to this builder.
+ *
+ *
+ */
+public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+
+
+
+    @SuppressWarnings("rawtypes")
+    private final Deque<NormalizedNodeContainerBuilder> builders;
+
+
+    @SuppressWarnings("rawtypes")
+    private ImmutableNormalizedNodeStreamWriter( final NormalizedNodeContainerBuilder topLevelBuilder) {
+        builders = new ArrayDeque<>();
+        builders.push(topLevelBuilder);
+    }
+
+    /**
+     * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied
+     * {@link NormalizedNode}s and writes them to supplied builder as child nodes.
+     * <p>
+     * Type of supplied {@link NormalizedNodeContainerBuilder} affects,
+     * which events could be emitted in order to ensure proper construction of
+     * data.
+     *
+     * @param builder Builder to which data will be written.
+     * @return {@link NormalizedNodeStreamWriter} which writes data
+     */
+    public static final NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
+        return new ImmutableNormalizedNodeStreamWriter(builder);
+    }
+
+    /**
+     *
+     * 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 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 writen.
+     *
+     *
+     * @param result {@link NormalizedNodeResult} object which will hold result value.
+     * @return {@link NormalizedNodeStreamWriter} whcih will write item to supplied result holder.
+     */
+    public static final NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
+        return new ImmutableNormalizedNodeStreamWriter(new NormalizedNodeResultBuilder(result));
+    }
+
+
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeContainerBuilder getCurrent() {
+        return builders.peek();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void enter(final NormalizedNodeContainerBuilder next) {
+        builders.push(next);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void writeChild(final NormalizedNode<?, ?> child) {
+        getCurrent().addChild(child);
+    }
+
+    @Override
+    @SuppressWarnings({"rawtypes","unchecked"})
+    public void endNode() {
+        final NormalizedNodeContainerBuilder finishedBuilder = builders.poll();
+        Preconditions.checkState(finishedBuilder != null, "Node which should be closed does not exists.");
+        NormalizedNodeContainerBuilder current = getCurrent();
+        Preconditions.checkState(current != null, "Reached top level node, which could not be closed in this writer.");
+        NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
+        current.addChild(product);
+    }
+
+    @Override
+    public void leafNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        writeChild(ImmutableNodes.leafNode(name, value));
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.leafSetBuilder();
+        builder.withNodeIdentifier(name);
+        enter(builder);
+    }
+
+    @Override
+    public void leafSetEntryNode(final Object value) throws IllegalArgumentException {
+        Preconditions.checkArgument(getCurrent() instanceof ImmutableLeafSetNodeBuilder<?>);
+        @SuppressWarnings("unchecked")
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = ((ImmutableLeafSetNodeBuilder<Object>) getCurrent());
+        builder.withChildValue(value);
+    }
+
+    @Override
+    public void anyxmlNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+        checkDataNodeContainer();
+
+
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.containerBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.unkeyedListBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name,final int childSizeHint) throws IllegalStateException {
+        Preconditions.checkArgument(getCurrent() instanceof ImmutableUnkeyedListNodeBuilder);
+        enter(Builders.unkeyedListEntryBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.mapBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier,final int childSizeHint) throws IllegalArgumentException {
+        Preconditions.checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder);
+        enter(Builders.mapEntryBuilder().withNodeIdentifier(identifier));
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.mapBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.choiceBuilder().withNodeIdentifier(name));
+    }
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        Preconditions.checkArgument(!(getCurrent() instanceof ImmutableAugmentationNodeBuilder));
+        enter(Builders.augmentationBuilder().withNodeIdentifier(identifier));
+    }
+
+    private void checkDataNodeContainer() {
+        @SuppressWarnings("rawtypes")
+        NormalizedNodeContainerBuilder current = getCurrent();
+        if(!(current instanceof NormalizedNodeResultBuilder)) {
+        Preconditions.checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private 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 NormalizedNode build() {
+            throw new IllegalStateException("Can not close NormalizedNodeResult");
+        }
+
+        @Override
+        public NormalizedNodeContainerBuilder withNodeIdentifier(final PathArgument nodeIdentifier) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NormalizedNodeContainerBuilder withValue(final List value) {
+            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();
+        }
+
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java
new file mode 100644 (file)
index 0000000..76b28e9
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Client allocated result holder for {@link ImmutableNormalizedNodeStreamWriter}.
+ * which produces instance of NormalizedNode.
+ *
+ * Client may supply result holder to {@link ImmutableNormalizedNodeStreamWriter}
+ * which will be once updated, when result is available.
+ *
+ * This is intended for using {@link ImmutableNormalizedNodeStreamWriter}
+ * without supplying builder, so instantiated writer will select
+ * correct builder based on first event and sets resulting
+ *  {@link NormalizedNode} when end event is invoked for node.
+ *
+ */
+public class NormalizedNodeResult {
+
+    private boolean finished = false;
+    private NormalizedNode<?,?> result;
+
+    public NormalizedNode<?, ?> getResult() {
+        return result;
+    }
+
+    void setResult(final NormalizedNode<?, ?> result) {
+        Preconditions.checkState(!this.finished, "Result was already set.");
+        this.finished = true;
+        this.result = result;
+    }
+
+    public boolean isFinished() {
+        return finished;
+    }
+
+}