2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.data.impl.schema;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.util.Objects.requireNonNull;
14 import com.google.common.collect.ClassToInstanceMap;
15 import com.google.common.collect.ImmutableClassToInstanceMap;
16 import java.io.IOException;
17 import java.util.ArrayDeque;
18 import java.util.Deque;
19 import javax.xml.transform.dom.DOMSource;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.stream.AnydataExtension;
29 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
30 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnyXmlNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnydataNodeBuilder;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
37 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
38 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
46 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableYangModeledAnyXmlNodeBuilder;
49 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
52 * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
53 * {@link NormalizedNode}s.
56 * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)} where resulting
57 * NormalizedNode will be stored in supplied result object.
60 * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
61 * written to this builder.
64 * This class is not final for purposes of customization, normal users should not need to subclass it.
66 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter, AnydataExtension {
67 @SuppressWarnings("rawtypes")
68 private final Deque<NormalizedNodeBuilder> builders = new ArrayDeque<>();
70 private DataSchemaNode nextSchema;
72 @SuppressWarnings("rawtypes")
73 protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeBuilder topLevelBuilder) {
74 builders.push(topLevelBuilder);
77 protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeResult result) {
78 this(new NormalizedNodeResultBuilder(result));
82 * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied {@link NormalizedNode}s
83 * and writes them to supplied builder as child nodes.
86 * Type of supplied {@link NormalizedNodeContainerBuilder} affects, which events could be emitted in order
87 * to ensure proper construction of data.
89 * @param builder Builder to which data will be written.
90 * @return {@link NormalizedNodeStreamWriter} which writes data
92 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
93 return new ImmutableNormalizedNodeStreamWriter(builder);
97 * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
98 * (type of NormalizedNode) is determined by first start event.
101 * Result is built when {@link #endNode()} associated with that start event is emitted.
104 * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
105 * of the first event.
108 * This method is useful for clients, which knows there will be one top-level node written, but does not know which
109 * type of {@link NormalizedNode} will be written.
111 * @param result {@link NormalizedNodeResult} object which will hold result value.
112 * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
114 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
115 return result instanceof NormalizedNodeMetadataResult ? from((NormalizedNodeMetadataResult) result)
116 : new ImmutableNormalizedNodeStreamWriter(result);
120 * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
121 * (type of NormalizedNode) is determined by first start event.
124 * Result is built when {@link #endNode()} associated with that start event is emitted.
127 * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
128 * of the first event.
131 * This method is useful for clients, which knows there will be one top-level node written, but does not know which
132 * type of {@link NormalizedNode} will be written.
134 * @param result {@link NormalizedNodeResult} object which will hold result value.
135 * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
137 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeMetadataResult result) {
138 return new ImmutableMetadataNormalizedNodeStreamWriter(result);
142 public ClassToInstanceMap<NormalizedNodeStreamWriterExtension> getExtensions() {
143 return ImmutableClassToInstanceMap.of(AnydataExtension.class, this);
147 public void startLeafNode(final NodeIdentifier name) {
148 checkDataNodeContainer();
149 enter(name, leafNodeBuilder(nextSchema));
154 public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
155 checkDataNodeContainer();
156 enter(name, UNKNOWN_SIZE == childSizeHint ? InterningLeafSetNodeBuilder.create(nextSchema)
157 : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint));
161 public void startLeafSetEntryNode(final NodeWithValue<?> name) {
162 final NormalizedNodeBuilder<?, ?, ?> current = current();
163 checkArgument(current instanceof ImmutableLeafSetNodeBuilder
164 || current instanceof ImmutableOrderedLeafSetNodeBuilder || current instanceof NormalizedNodeResultBuilder,
165 "LeafSetEntryNode is not valid for parent %s", current);
166 enter(name, leafsetEntryNodeBuilder());
171 public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
172 checkDataNodeContainer();
173 enter(name, Builders.orderedLeafSetBuilder());
177 public void startAnyxmlNode(final NodeIdentifier name) {
178 checkDataNodeContainer();
179 enter(name, ImmutableAnyXmlNodeBuilder.create());
184 public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
185 checkDataNodeContainer();
186 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableContainerNodeBuilder.create()
187 : ImmutableContainerNodeBuilder.create(childSizeHint));
191 public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) {
192 checkDataNodeContainer();
194 checkArgument(nextSchema instanceof YangModeledAnyxmlSchemaNode,
195 "Schema of this node should be instance of YangModeledAnyxmlSchemaNode");
196 final YangModeledAnyxmlSchemaNode anyxmlSchema = (YangModeledAnyxmlSchemaNode) nextSchema;
197 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableYangModeledAnyXmlNodeBuilder.create(anyxmlSchema)
198 : ImmutableYangModeledAnyXmlNodeBuilder.create(anyxmlSchema, childSizeHint));
202 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
203 checkDataNodeContainer();
204 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListNodeBuilder.create()
205 : ImmutableUnkeyedListNodeBuilder.create(childSizeHint));
209 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
210 final NormalizedNodeBuilder<?, ?, ?> current = current();
211 checkArgument(current instanceof ImmutableUnkeyedListNodeBuilder
212 || current instanceof NormalizedNodeResultBuilder);
213 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListEntryNodeBuilder.create()
214 : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint));
218 public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
219 checkDataNodeContainer();
220 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableMapNodeBuilder.create()
221 : ImmutableMapNodeBuilder.create(childSizeHint));
225 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
226 final NormalizedNodeBuilder<?, ?, ?> current = current();
227 checkArgument(current instanceof ImmutableMapNodeBuilder || current instanceof ImmutableOrderedMapNodeBuilder
228 || current instanceof NormalizedNodeResultBuilder);
230 enter(identifier, UNKNOWN_SIZE == childSizeHint ? ImmutableMapEntryNodeBuilder.create()
231 : ImmutableMapEntryNodeBuilder.create(childSizeHint));
235 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
236 checkDataNodeContainer();
237 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableOrderedMapNodeBuilder.create()
238 : ImmutableOrderedMapNodeBuilder.create(childSizeHint));
242 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
243 checkDataNodeContainer();
244 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableChoiceNodeBuilder.create()
245 : ImmutableChoiceNodeBuilder.create(childSizeHint));
249 public void startAugmentationNode(final AugmentationIdentifier identifier) {
250 checkDataNodeContainer();
251 checkArgument(!(current() instanceof ImmutableAugmentationNodeBuilder));
252 enter(identifier, Builders.augmentationBuilder());
256 public void flush() {
261 public void close() {
266 public void nextDataSchemaNode(final DataSchemaNode schema) {
267 nextSchema = requireNonNull(schema);
271 public void scalarValue(final Object value) {
272 currentScalar().withValue(value);
276 public void domSourceValue(final DOMSource value) {
277 currentScalar().withValue(value);
281 @SuppressWarnings({ "rawtypes", "unchecked" })
282 public void endNode() {
283 final NormalizedNodeBuilder finishedBuilder = builders.poll();
284 checkState(finishedBuilder != null, "Node which should be closed does not exists.");
285 final NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
292 public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
293 checkDataNodeContainer();
294 enter(name, ImmutableAnydataNodeBuilder.create(objectModel));
295 // We support all object models
300 * Add a child not to the currently-open builder.
302 * @param child A new child
303 * @throws NullPointerException if {@code child} is null
304 * @throws IllegalStateException if there is no open builder
306 @SuppressWarnings({ "rawtypes", "unchecked" })
307 protected final void writeChild(final NormalizedNode<?, ?> child) {
308 final NormalizedNodeContainerBuilder current = currentContainer();
309 checkState(current != null, "Reached top level node, which could not be closed in this writer.");
310 current.addChild(requireNonNull(child));
313 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
314 @SuppressWarnings("rawtypes")
315 void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
316 builders.push(next.withNodeIdentifier(identifier));
320 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
321 protected final NormalizedNodeBuilder popBuilder() {
322 return builders.pop();
325 final void reset(final NormalizedNodeResultBuilder builder) {
328 builders.push(builder);
331 private <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder(final DataSchemaNode schema) {
332 final InterningLeafNodeBuilder<T> interning = InterningLeafNodeBuilder.forSchema(schema);
333 return interning != null ? interning : leafNodeBuilder();
336 <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder() {
337 return new ImmutableLeafNodeBuilder<>();
340 <T> ImmutableLeafSetEntryNodeBuilder<T> leafsetEntryNodeBuilder() {
341 return ImmutableLeafSetEntryNodeBuilder.create();
344 private void checkDataNodeContainer() {
345 @SuppressWarnings("rawtypes")
346 final NormalizedNodeContainerBuilder current = currentContainer();
347 if (!(current instanceof NormalizedNodeResultBuilder)) {
348 checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
352 @SuppressWarnings("rawtypes")
353 private NormalizedNodeBuilder current() {
354 return builders.peek();
357 @SuppressWarnings("rawtypes")
358 private NormalizedNodeContainerBuilder currentContainer() {
359 final NormalizedNodeBuilder current = current();
360 if (current == null) {
363 checkState(current instanceof NormalizedNodeContainerBuilder, "%s is not a node container", current);
364 return (NormalizedNodeContainerBuilder) current;
367 @SuppressWarnings("rawtypes")
368 private NormalizedNodeBuilder currentScalar() {
369 final NormalizedNodeBuilder current = current();
370 checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);