Update NormalizedNodeStreamWriter
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / ImmutableNormalizedNodeStreamWriter.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.data.impl.schema;
9
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;
13
14 import java.util.ArrayDeque;
15 import java.util.Collection;
16 import java.util.Deque;
17 import javax.xml.transform.dom.DOMSource;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyXmlSchemaNode;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
25 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
37 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
38 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnyXmlNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
46 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
49 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
50 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
51 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
52 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
53 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
54 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableYangModeledAnyXmlNodeBuilder;
55 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
56
57 /**
58  * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
59  * {@link NormalizedNode}s.
60  *
61  * <p>
62  * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)} where resulting
63  * NormalizedNode will be stored in supplied result object.
64  *
65  * <p>
66  * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
67  * written to this builder.
68  */
69 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
70
71     @SuppressWarnings("rawtypes")
72     private final Deque<NormalizedNodeBuilder> builders = new ArrayDeque<>();
73     private DataSchemaNode nextSchema;
74
75     @SuppressWarnings("rawtypes")
76     protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeContainerBuilder topLevelBuilder) {
77         builders.push(topLevelBuilder);
78     }
79
80     protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeResult result) {
81         this(new NormalizedNodeResultBuilder(result));
82     }
83
84     /**
85      * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied {@link NormalizedNode}s
86      * and writes them to supplied builder as child nodes.
87      *
88      * <p>
89      * Type of supplied {@link NormalizedNodeContainerBuilder} affects, which events could be emitted in order
90      * to ensure proper construction of data.
91      *
92      * @param builder Builder to which data will be written.
93      * @return {@link NormalizedNodeStreamWriter} which writes data
94      */
95     public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
96         return new ImmutableNormalizedNodeStreamWriter(builder);
97     }
98
99     /**
100      * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
101      * (type of NormalizedNode) is determined by first start event.
102      *
103      * <p>
104      * Result is built when {@link #endNode()} associated with that start event is emitted.
105      *
106      * <p>
107      * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
108      * of the first event.
109      *
110      * <p>
111      * This method is useful for clients, which knows there will be one top-level node written, but does not know which
112      * type of {@link NormalizedNode} will be written.
113      *
114      * @param result {@link NormalizedNodeResult} object which will hold result value.
115      * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
116      */
117     public static NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
118         return new ImmutableNormalizedNodeStreamWriter(result);
119     }
120
121     @SuppressWarnings("rawtypes")
122     protected NormalizedNodeBuilder getCurrent() {
123         return builders.peek();
124     }
125
126     @SuppressWarnings("rawtypes")
127     protected final NormalizedNodeContainerBuilder getCurrentContainer() {
128         final NormalizedNodeBuilder current = getCurrent();
129         if (current == null) {
130             return null;
131         }
132         checkState(current instanceof NormalizedNodeContainerBuilder, "%s is not a node container", current);
133         return (NormalizedNodeContainerBuilder) current;
134     }
135
136     @SuppressWarnings("rawtypes")
137     protected NormalizedNodeBuilder getCurrentScalar() {
138         final NormalizedNodeBuilder current = getCurrent();
139         checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);
140         return current;
141     }
142
143     @SuppressWarnings("rawtypes")
144     private void enter(final NormalizedNodeBuilder next) {
145         builders.push(next);
146         nextSchema = null;
147     }
148
149     @SuppressWarnings("unchecked")
150     protected void writeChild(final NormalizedNode<?, ?> child) {
151         getCurrentContainer().addChild(child);
152     }
153
154     @Override
155     @SuppressWarnings({"rawtypes","unchecked"})
156     public void endNode() {
157         final NormalizedNodeBuilder finishedBuilder = builders.poll();
158         checkState(finishedBuilder != null, "Node which should be closed does not exists.");
159         final NormalizedNodeContainerBuilder current = getCurrentContainer();
160         checkState(current != null, "Reached top level node, which could not be closed in this writer.");
161         final NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
162         current.addChild(product);
163         nextSchema = null;
164     }
165
166     @Override
167     public void startLeafNode(final NodeIdentifier name) {
168         checkDataNodeContainer();
169         enter(InterningLeafNodeBuilder.forSchema(nextSchema).withNodeIdentifier(name));
170         nextSchema = null;
171     }
172
173     @Override
174     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
175         checkDataNodeContainer();
176         final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = UNKNOWN_SIZE == childSizeHint
177                 ? InterningLeafSetNodeBuilder.create(nextSchema)
178                         : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint);
179         builder.withNodeIdentifier(name);
180         enter(builder);
181     }
182
183     @Override
184     public void startLeafSetEntryNode(final NodeWithValue<?> name) {
185         @SuppressWarnings("rawtypes")
186         final NormalizedNodeBuilder current = getCurrent();
187         checkArgument(current instanceof ImmutableOrderedLeafSetNodeBuilder
188             || current instanceof ImmutableLeafSetNodeBuilder, "LeafSetEntryNode is not valid for parent %s", current);
189         enter(ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(name));
190         nextSchema = null;
191     }
192
193     @Override
194     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
195         checkDataNodeContainer();
196         final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.orderedLeafSetBuilder();
197         builder.withNodeIdentifier(name);
198         enter(builder);
199     }
200
201     @Override
202     public void startAnyxmlNode(final NodeIdentifier name) {
203         checkDataNodeContainer();
204         enter(ImmutableAnyXmlNodeBuilder.create().withNodeIdentifier(name));
205         nextSchema = null;
206     }
207
208     @Override
209     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
210         checkDataNodeContainer();
211
212         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
213                 UNKNOWN_SIZE == childSizeHint ? ImmutableContainerNodeBuilder.create()
214                         : ImmutableContainerNodeBuilder.create(childSizeHint);
215         enter(builder.withNodeIdentifier(name));
216     }
217
218     @Override
219     public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) {
220         checkDataNodeContainer();
221
222         checkArgument(nextSchema instanceof YangModeledAnyXmlSchemaNode,
223                 "Schema of this node should be instance of YangModeledAnyXmlSchemaNode");
224         final DataContainerNodeAttrBuilder<NodeIdentifier, YangModeledAnyXmlNode> builder =
225                 UNKNOWN_SIZE == childSizeHint
226                 ? ImmutableYangModeledAnyXmlNodeBuilder.create((YangModeledAnyXmlSchemaNode) nextSchema)
227                         : ImmutableYangModeledAnyXmlNodeBuilder.create(
228                 (YangModeledAnyXmlSchemaNode) nextSchema, childSizeHint);
229         enter(builder.withNodeIdentifier(name));
230     }
231
232     @Override
233     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
234         checkDataNodeContainer();
235
236         final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder =
237                 UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListNodeBuilder.create()
238                         : ImmutableUnkeyedListNodeBuilder.create(childSizeHint);
239         enter(builder.withNodeIdentifier(name));
240     }
241
242     @Override
243     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
244         checkArgument(getCurrent() instanceof NormalizedNodeResultBuilder
245                 || getCurrent() instanceof ImmutableUnkeyedListNodeBuilder);
246         final DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder =
247                 UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListEntryNodeBuilder.create()
248                         : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint);
249         enter(builder.withNodeIdentifier(name));
250     }
251
252     @Override
253     public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
254         checkDataNodeContainer();
255
256         final CollectionNodeBuilder<MapEntryNode, MapNode> builder = UNKNOWN_SIZE == childSizeHint
257                 ? ImmutableMapNodeBuilder.create() : ImmutableMapNodeBuilder.create(childSizeHint);
258         enter(builder.withNodeIdentifier(name));
259     }
260
261     @Override
262     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
263         if (!(getCurrent() instanceof NormalizedNodeResultBuilder)) {
264             checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder
265                 || getCurrent() instanceof ImmutableOrderedMapNodeBuilder);
266         }
267
268         final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
269                 UNKNOWN_SIZE == childSizeHint ? ImmutableMapEntryNodeBuilder.create()
270                         : ImmutableMapEntryNodeBuilder.create(childSizeHint);
271         enter(builder.withNodeIdentifier(identifier));
272     }
273
274     @Override
275     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
276         checkDataNodeContainer();
277
278         final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> builder = UNKNOWN_SIZE == childSizeHint
279                 ? ImmutableOrderedMapNodeBuilder.create() : ImmutableOrderedMapNodeBuilder.create(childSizeHint);
280         enter(builder.withNodeIdentifier(name));
281     }
282
283     @Override
284     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
285         checkDataNodeContainer();
286
287         final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = UNKNOWN_SIZE == childSizeHint
288                 ? ImmutableChoiceNodeBuilder.create() : ImmutableChoiceNodeBuilder.create(childSizeHint);
289         enter(builder.withNodeIdentifier(name));
290     }
291
292     @Override
293     public void startAugmentationNode(final AugmentationIdentifier identifier) {
294         checkDataNodeContainer();
295         checkArgument(!(getCurrent() instanceof ImmutableAugmentationNodeBuilder));
296         enter(Builders.augmentationBuilder().withNodeIdentifier(identifier));
297     }
298
299     private void checkDataNodeContainer() {
300         @SuppressWarnings("rawtypes")
301         final NormalizedNodeContainerBuilder current = getCurrentContainer();
302         if (!(current instanceof NormalizedNodeResultBuilder)) {
303             checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
304         }
305     }
306
307     @SuppressWarnings("rawtypes")
308     protected static final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
309
310         private final NormalizedNodeResult result;
311
312         public NormalizedNodeResultBuilder(final NormalizedNodeResult result) {
313             this.result = result;
314         }
315
316         @Override
317         public NormalizedNodeBuilder withValue(final Object value) {
318             throw new UnsupportedOperationException();
319         }
320
321         @Override
322         public NormalizedNodeContainerBuilder withValue(final Collection value) {
323             throw new UnsupportedOperationException();
324         }
325
326         @Override
327         public NormalizedNode build() {
328             throw new IllegalStateException("Can not close NormalizedNodeResult");
329         }
330
331         @Override
332         public NormalizedNodeContainerBuilder withNodeIdentifier(final PathArgument nodeIdentifier) {
333             throw new UnsupportedOperationException();
334         }
335
336         @Override
337         public NormalizedNodeContainerBuilder addChild(final NormalizedNode child) {
338             result.setResult(child);
339             return this;
340         }
341
342         @Override
343         public NormalizedNodeContainerBuilder removeChild(final PathArgument key) {
344             throw new UnsupportedOperationException();
345         }
346     }
347
348     @Override
349     public void flush() {
350         // no-op
351     }
352
353     @Override
354     public void close() {
355         // no-op
356     }
357
358     @Override
359     public void nextDataSchemaNode(final DataSchemaNode schema) {
360         nextSchema = requireNonNull(schema);
361     }
362
363     @Override
364     public void scalarValue(final Object value) {
365         getCurrentScalar().withValue(value);
366     }
367
368     @Override
369     public void domSourceValue(final DOMSource value) {
370         getCurrentScalar().withValue(value);
371     }
372 }