Introduce map-related ImmutableNodes operations
[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 com.google.common.base.Preconditions;
11 import java.util.ArrayDeque;
12 import java.util.Collection;
13 import java.util.Deque;
14 import javax.annotation.Nonnull;
15 import javax.xml.transform.dom.DOMSource;
16 import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyXmlSchemaNode;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
22 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
37 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
38 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnyXmlNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
46 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
49 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
50 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
51 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
52 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableYangModeledAnyXmlNodeBuilder;
53 import org.opendaylight.yangtools.yang.data.util.LeafInterner;
54 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
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<NormalizedNodeContainerBuilder> 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 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     protected Deque<NormalizedNodeContainerBuilder> getBuilders() {
122         return builders;
123     }
124
125     @SuppressWarnings("rawtypes")
126     protected NormalizedNodeContainerBuilder getCurrent() {
127         return builders.peek();
128     }
129
130     @SuppressWarnings("rawtypes")
131     private void enter(final NormalizedNodeContainerBuilder next) {
132         builders.push(next);
133         nextSchema = null;
134     }
135
136     @SuppressWarnings("unchecked")
137     protected void writeChild(final NormalizedNode<?, ?> child) {
138         getCurrent().addChild(child);
139     }
140
141     @Override
142     @SuppressWarnings({"rawtypes","unchecked"})
143     public void endNode() {
144         final NormalizedNodeContainerBuilder finishedBuilder = builders.poll();
145         Preconditions.checkState(finishedBuilder != null, "Node which should be closed does not exists.");
146         final NormalizedNodeContainerBuilder current = getCurrent();
147         Preconditions.checkState(current != null, "Reached top level node, which could not be closed in this writer.");
148         final NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
149         current.addChild(product);
150         nextSchema = null;
151     }
152
153     @Override
154     public void leafNode(final NodeIdentifier name, final Object value) {
155         checkDataNodeContainer();
156
157         final LeafNode<Object> sample = ImmutableNodes.leafNode(name, value);
158         final LeafNode<?> node;
159         if (nextSchema instanceof LeafSchemaNode) {
160             node = LeafInterner.forSchema((LeafSchemaNode)nextSchema).intern(sample);
161         } else {
162             node = sample;
163         }
164
165         writeChild(node);
166         nextSchema = null;
167     }
168
169     @Override
170     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
171         checkDataNodeContainer();
172         final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = UNKNOWN_SIZE == childSizeHint
173                 ? InterningLeafSetNodeBuilder.create(nextSchema)
174                         : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint);
175         builder.withNodeIdentifier(name);
176         enter(builder);
177     }
178
179     @Override
180     public void leafSetEntryNode(final QName name, final Object value) {
181         if (getCurrent() instanceof ImmutableOrderedLeafSetNodeBuilder) {
182             @SuppressWarnings("unchecked")
183             ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder =
184                 (ImmutableOrderedLeafSetNodeBuilder<Object>) getCurrent();
185             builder.withChildValue(value);
186         } else if (getCurrent() instanceof ImmutableLeafSetNodeBuilder) {
187             @SuppressWarnings("unchecked")
188             ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder =
189                 (ImmutableLeafSetNodeBuilder<Object>) getCurrent();
190             builder.withChildValue(value);
191         } else {
192             throw new IllegalArgumentException("LeafSetEntryNode is not valid for parent " + getCurrent());
193         }
194
195         nextSchema = null;
196     }
197
198     @Override
199     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
200         checkDataNodeContainer();
201         final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.orderedLeafSetBuilder();
202         builder.withNodeIdentifier(name);
203         enter(builder);
204     }
205
206     @Override
207     public void anyxmlNode(final NodeIdentifier name, final Object value) {
208         checkDataNodeContainer();
209
210         final AnyXmlNode node = ImmutableAnyXmlNodeBuilder.create().withNodeIdentifier(name)
211                 .withValue((DOMSource) value).build();
212         writeChild(node);
213
214         nextSchema = null;
215     }
216
217     @Override
218     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
219         checkDataNodeContainer();
220
221         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
222                 UNKNOWN_SIZE == childSizeHint ? ImmutableContainerNodeBuilder.create()
223                         : ImmutableContainerNodeBuilder.create(childSizeHint);
224         enter(builder.withNodeIdentifier(name));
225     }
226
227     @Override
228     public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) {
229         checkDataNodeContainer();
230
231         Preconditions.checkArgument(nextSchema instanceof YangModeledAnyXmlSchemaNode,
232                 "Schema of this node should be instance of YangModeledAnyXmlSchemaNode");
233         final DataContainerNodeAttrBuilder<NodeIdentifier, YangModeledAnyXmlNode> builder =
234                 UNKNOWN_SIZE == childSizeHint
235                 ? ImmutableYangModeledAnyXmlNodeBuilder.create((YangModeledAnyXmlSchemaNode) nextSchema)
236                         : ImmutableYangModeledAnyXmlNodeBuilder.create(
237                 (YangModeledAnyXmlSchemaNode) nextSchema, childSizeHint);
238         enter(builder.withNodeIdentifier(name));
239     }
240
241     @Override
242     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
243         checkDataNodeContainer();
244
245         final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder =
246                 UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListNodeBuilder.create()
247                         : ImmutableUnkeyedListNodeBuilder.create(childSizeHint);
248         enter(builder.withNodeIdentifier(name));
249     }
250
251     @Override
252     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
253         Preconditions.checkArgument(getCurrent() instanceof NormalizedNodeResultBuilder
254                 || getCurrent() instanceof ImmutableUnkeyedListNodeBuilder);
255         final DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder =
256                 UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListEntryNodeBuilder.create()
257                         : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint);
258         enter(builder.withNodeIdentifier(name));
259     }
260
261     @Override
262     public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
263         checkDataNodeContainer();
264
265         final CollectionNodeBuilder<MapEntryNode, MapNode> builder = UNKNOWN_SIZE == childSizeHint
266                 ? ImmutableMapNodeBuilder.create() : ImmutableMapNodeBuilder.create(childSizeHint);
267         enter(builder.withNodeIdentifier(name));
268     }
269
270     @Override
271     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
272         if (!(getCurrent() instanceof NormalizedNodeResultBuilder)) {
273             Preconditions.checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder
274                 || getCurrent() instanceof ImmutableOrderedMapNodeBuilder);
275         }
276
277         final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
278                 UNKNOWN_SIZE == childSizeHint ? ImmutableMapEntryNodeBuilder.create()
279                         : ImmutableMapEntryNodeBuilder.create(childSizeHint);
280         enter(builder.withNodeIdentifier(identifier));
281     }
282
283     @Override
284     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
285         checkDataNodeContainer();
286
287         final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> builder = UNKNOWN_SIZE == childSizeHint
288                 ? ImmutableOrderedMapNodeBuilder.create() : ImmutableOrderedMapNodeBuilder.create(childSizeHint);
289         enter(builder.withNodeIdentifier(name));
290     }
291
292     @Override
293     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
294         checkDataNodeContainer();
295
296         final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = UNKNOWN_SIZE == childSizeHint
297                 ? ImmutableChoiceNodeBuilder.create() : ImmutableChoiceNodeBuilder.create(childSizeHint);
298         enter(builder.withNodeIdentifier(name));
299     }
300
301     @Override
302     public void startAugmentationNode(final AugmentationIdentifier identifier) {
303         checkDataNodeContainer();
304         Preconditions.checkArgument(!(getCurrent() instanceof ImmutableAugmentationNodeBuilder));
305         enter(Builders.augmentationBuilder().withNodeIdentifier(identifier));
306     }
307
308     private void checkDataNodeContainer() {
309         @SuppressWarnings("rawtypes")
310         final NormalizedNodeContainerBuilder current = getCurrent();
311         if (!(current instanceof NormalizedNodeResultBuilder)) {
312             Preconditions.checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
313         }
314     }
315
316     @SuppressWarnings("rawtypes")
317     protected static final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
318
319         private final NormalizedNodeResult result;
320
321         public NormalizedNodeResultBuilder(final NormalizedNodeResult result) {
322             this.result = result;
323         }
324
325         @Override
326         public NormalizedNodeBuilder withValue(final Object value) {
327             throw new UnsupportedOperationException();
328         }
329
330         @Override
331         public NormalizedNodeContainerBuilder withValue(final Collection value) {
332             throw new UnsupportedOperationException();
333         }
334
335         @Override
336         public NormalizedNode build() {
337             throw new IllegalStateException("Can not close NormalizedNodeResult");
338         }
339
340         @Override
341         public NormalizedNodeContainerBuilder withNodeIdentifier(final PathArgument nodeIdentifier) {
342             throw new UnsupportedOperationException();
343         }
344
345         @Override
346         public NormalizedNodeContainerBuilder addChild(final NormalizedNode child) {
347             result.setResult(child);
348             return this;
349         }
350
351         @Override
352         public NormalizedNodeContainerBuilder removeChild(final PathArgument key) {
353             throw new UnsupportedOperationException();
354         }
355     }
356
357     @Override
358     public void flush() {
359         // no-op
360     }
361
362     @Override
363     public void close() {
364         // no-op
365     }
366
367     @Override
368     public void nextDataSchemaNode(@Nonnull final DataSchemaNode schema) {
369         nextSchema = Preconditions.checkNotNull(schema);
370     }
371 }