From f46c7ff17a42c7190f5458354ac1925457dc6ff7 Mon Sep 17 00:00:00 2001 From: Tony Tkacik Date: Wed, 30 Jul 2014 17:09:08 +0200 Subject: [PATCH] Bug 1437, Bug 1438 NormalizedNode Stream Writer API and Implementation Initial draft of NormalizedNode Stream Writer API, along with initial implementation which produces immutable NormalizedNodes. Change-Id: I45513d88eefca7aa070e6679ccfdedf358266d59 Signed-off-by: Tony Tkacik --- .../stream/NormalizedNodeStreamWriter.java | 368 ++++++++++++++++++ .../yang/data/impl/schema/ImmutableNodes.java | 23 +- .../ImmutableNormalizedNodeStreamWriter.java | 250 ++++++++++++ .../impl/schema/NormalizedNodeResult.java | 45 +++ 4 files changed, 684 insertions(+), 2 deletions(-) create mode 100644 yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java create mode 100644 yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java create mode 100644 yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java 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 index 0000000000..96b5c15e91 --- /dev/null +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java @@ -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 + * + *

Writing Event Stream

+ * + * + * + *

Implementation notes

+ * + *

+ * 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. + *

+ * 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 map, + * choice unkeyed list node. + */ + void leafNode(NodeIdentifier name, Object value) throws IllegalArgumentException; + + /** + * + * Emits a start of leaf set (leaf-list). + *

+ * 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 map, + * choice unkeyed list 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 leaf set node. + */ + void leafSetEntryNode(Object value) throws IllegalArgumentException; + + /** + * + * Emits start of new container. + * + *

+ * End of container event is emitted by invoking {@link #endNode()}. + * + *

+ * Valid sub-events are: + *

+ * + * @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 map, + * choice unkeyed list node. + */ + void startContainerNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException; + + /** + * + * Emits start of unkeyed list node event. + * + *

+ * 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 map, + * choice unkeyed list node. + */ + void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException; + + /** + * Emits start of new unkeyed list item. + * + *

+ * Unkeyed list item event is finished by invoking {@link #endNode()}. Valid + * sub-events are: + *

+ * + * @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 unkeyed list node. + */ + void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IllegalStateException; + + /** + * + * Emits start of map node event. + * + *

+ * 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 map, + * choice unkeyed list node. + */ + void startMapNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException; + + /** + * + * Emits start of map entry. + * + *

+ * End of map entry event is emitted by invoking {@link #endNode()}. + * + *

+ * Valid sub-events are: + *

+ * + * + * @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 map entry node. + */ + void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IllegalArgumentException; + + /** + * + * Emits start of map node event. + * + *

+ * 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 map, + * choice unkeyed list 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 map, + * choice unkeyed list node. + */ + void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException; + + /** + * Emits start of augmentation node. + * + *

+ * End of augmentation event is emitted by invoking {@link #endNode()}. + * + *

+ * Valid sub-events are: + * + *

+ * + * @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 map, + * choice unkeyed list 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; + +} diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java index 5d3fadb02a..fc5c20e6fd 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java @@ -35,13 +35,32 @@ public final class ImmutableNodes { return ImmutableMapNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(name)); } - public static final LeafNode 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 LeafNode leafNode(final NodeIdentifier name,final T value) { return ImmutableLeafNodeBuilder.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 LeafNode leafNode(final QName name,final T value) { + return leafNode(new NodeIdentifier(name), value); + } + public static DataContainerNodeBuilder 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 index 0000000000..21fda47433 --- /dev/null +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java @@ -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. + *

+ * 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 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. + *

+ * 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. + *

+ * Result is built when {@link #endNode()} associated with that start event + * is emitted. + *

+ * Writer properly creates also nested {@link NormalizedNode} instances, + * if their are supported inside the scope of first event. + *

+ * 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 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> 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> builder = ((ImmutableLeafSetNodeBuilder) 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 index 0000000000..76b28e93aa --- /dev/null +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java @@ -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; + } + +} -- 2.36.6