80d18c2512f3fcae6115295ba9e8736b94720bff
[yangtools.git] / data / 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.io.IOException;
15 import java.util.ArrayDeque;
16 import java.util.Deque;
17 import javax.xml.transform.dom.DOMSource;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
24 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
26 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
27 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
28 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
29 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
30 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserLeafSetNodeBuilder;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserMapNodeBuilder;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38
39 /**
40  * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
41  * {@link NormalizedNode}s.
42  *
43  * <p>
44  * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)} where resulting
45  * NormalizedNode will be stored in supplied result object.
46  *
47  * <p>
48  * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
49  * written to this builder.
50  *
51  * <p>
52  * This class is not final for purposes of customization, normal users should not need to subclass it.
53  */
54 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
55     @SuppressWarnings("rawtypes")
56     private final Deque<NormalizedNodeBuilder> builders = new ArrayDeque<>();
57
58     private DataSchemaNode nextSchema;
59
60     @SuppressWarnings("rawtypes")
61     protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeBuilder topLevelBuilder) {
62         builders.push(topLevelBuilder);
63     }
64
65     protected ImmutableNormalizedNodeStreamWriter(final NormalizationResultHolder holder) {
66         this(new NormalizationResultBuilder(holder));
67     }
68
69     /**
70      * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied {@link NormalizedNode}s
71      * and writes them to supplied builder as child nodes.
72      *
73      * <p>
74      * Type of supplied {@link NormalizedNodeContainerBuilder} affects, which events could be emitted in order
75      * to ensure proper construction of data.
76      *
77      * @param builder Builder to which data will be written.
78      * @return {@link NormalizedNodeStreamWriter} which writes data
79      */
80     public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
81         return new ImmutableNormalizedNodeStreamWriter(builder);
82     }
83
84     /**
85      * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
86      * (type of NormalizedNode) is determined by first start event.
87      *
88      * <p>
89      * Result is built when {@link #endNode()} associated with that start event is emitted.
90      *
91      * <p>
92      * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
93      * of the first event.
94      *
95      * <p>
96      * This method is useful for clients, which knows there will be one top-level node written, but does not know which
97      * type of {@link NormalizedNode} will be written.
98      *
99      * @param holder {@link NormalizationResultHolder} object which will hold result value.
100      * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
101      */
102     public static @NonNull NormalizedNodeStreamWriter from(final NormalizationResultHolder holder) {
103         return new ImmutableMetadataNormalizedNodeStreamWriter(holder);
104     }
105
106     @Override
107     public void startLeafNode(final NodeIdentifier name) {
108         checkDataNodeContainer();
109         enter(name, leafNodeBuilder(nextSchema));
110         nextSchema = null;
111     }
112
113     @Override
114     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
115         checkDataNodeContainer();
116         enter(name, UNKNOWN_SIZE == childSizeHint ? InterningLeafSetNodeBuilder.create(nextSchema)
117                 : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint));
118     }
119
120     @Override
121     public void startLeafSetEntryNode(final NodeWithValue<?> name) {
122         final NormalizedNodeBuilder<?, ?, ?> current = current();
123         checkArgument(current instanceof ImmutableLeafSetNodeBuilder
124             || current instanceof ImmutableUserLeafSetNodeBuilder || current instanceof NormalizationResultBuilder,
125             "LeafSetEntryNode is not valid for parent %s", current);
126         enter(name, leafsetEntryNodeBuilder());
127         nextSchema = null;
128     }
129
130     @Override
131     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
132         checkDataNodeContainer();
133         enter(name, Builders.orderedLeafSetBuilder());
134     }
135
136     @Override
137     public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) {
138         checkDataNodeContainer();
139         if (DOMSource.class.isAssignableFrom(objectModel)) {
140             enter(name, Builders.anyXmlBuilder());
141             nextSchema = null;
142             return true;
143         }
144         return false;
145     }
146
147     @Override
148     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
149         checkDataNodeContainer();
150         enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.containerBuilder()
151             : Builders.containerBuilder(childSizeHint));
152     }
153
154     @Override
155     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
156         checkDataNodeContainer();
157         enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.unkeyedListBuilder()
158             : Builders.unkeyedListBuilder(childSizeHint));
159     }
160
161     @Override
162     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
163         final NormalizedNodeBuilder<?, ?, ?> current = current();
164         checkArgument(current instanceof ImmutableUnkeyedListNodeBuilder
165             || current instanceof NormalizationResultBuilder);
166         enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.unkeyedListEntryBuilder()
167             : Builders.unkeyedListEntryBuilder(childSizeHint));
168     }
169
170     @Override
171     public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
172         checkDataNodeContainer();
173         enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.mapBuilder() : Builders.mapBuilder(childSizeHint));
174     }
175
176     @Override
177     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
178         final NormalizedNodeBuilder<?, ?, ?> current = current();
179         checkArgument(current instanceof ImmutableMapNodeBuilder || current instanceof ImmutableUserMapNodeBuilder
180             || current instanceof NormalizationResultBuilder);
181
182         enter(identifier, UNKNOWN_SIZE == childSizeHint ? Builders.mapEntryBuilder()
183             : Builders.mapEntryBuilder(childSizeHint));
184     }
185
186     @Override
187     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
188         checkDataNodeContainer();
189         enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.orderedMapBuilder()
190             : Builders.orderedMapBuilder(childSizeHint));
191     }
192
193     @Override
194     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
195         checkDataNodeContainer();
196         enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.choiceBuilder() : Builders.choiceBuilder(childSizeHint));
197     }
198
199     @Override
200     public void startAugmentationNode(final AugmentationIdentifier identifier) {
201         checkDataNodeContainer();
202         checkArgument(!(current() instanceof ImmutableAugmentationNodeBuilder));
203         enter(identifier, Builders.augmentationBuilder());
204     }
205
206     @Override
207     public void flush() {
208         // no-op
209     }
210
211     @Override
212     public void close() {
213         // no-op
214     }
215
216     @Override
217     public void nextDataSchemaNode(final DataSchemaNode schema) {
218         nextSchema = requireNonNull(schema);
219     }
220
221     @Override
222     public void scalarValue(final Object value) {
223         currentScalar().withValue(value);
224     }
225
226     @Override
227     public void domSourceValue(final DOMSource value) {
228         currentScalar().withValue(value);
229     }
230
231     @Override
232     @SuppressWarnings("rawtypes")
233     public void endNode() {
234         final NormalizedNodeBuilder finishedBuilder = builders.poll();
235         checkState(finishedBuilder != null, "Node which should be closed does not exists.");
236         final NormalizedNode product = finishedBuilder.build();
237         nextSchema = null;
238
239         writeChild(product);
240     }
241
242     @Override
243     public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
244         checkDataNodeContainer();
245         enter(name, Builders.anydataBuilder(objectModel));
246         // We support all object models
247         return true;
248     }
249
250     /**
251      * Add a child not to the currently-open builder.
252      *
253      * @param child A new child
254      * @throws NullPointerException if {@code child} is null
255      * @throws IllegalStateException if there is no open builder
256      */
257     @SuppressWarnings({ "rawtypes", "unchecked" })
258     protected final void writeChild(final NormalizedNode child) {
259         final NormalizedNodeContainerBuilder current = currentContainer();
260         checkState(current != null, "Reached top level node, which could not be closed in this writer.");
261         current.addChild(requireNonNull(child));
262     }
263
264     // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
265     @SuppressWarnings("rawtypes")
266     void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
267         builders.push(next.withNodeIdentifier(identifier));
268         nextSchema = null;
269     }
270
271     // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
272     protected final NormalizedNodeBuilder popBuilder() {
273         return builders.pop();
274     }
275
276     final void reset(final NormalizationResultBuilder builder) {
277         nextSchema = null;
278         builders.clear();
279         builders.push(builder);
280     }
281
282     private <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder(final DataSchemaNode schema) {
283         final InterningLeafNodeBuilder<T> interning = InterningLeafNodeBuilder.forSchema(schema);
284         return interning != null ? interning : leafNodeBuilder();
285     }
286
287     <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder() {
288         return new ImmutableLeafNodeBuilder<>();
289     }
290
291     <T> ImmutableLeafSetEntryNodeBuilder<T> leafsetEntryNodeBuilder() {
292         return ImmutableLeafSetEntryNodeBuilder.create();
293     }
294
295     private void checkDataNodeContainer() {
296         @SuppressWarnings("rawtypes")
297         final NormalizedNodeContainerBuilder current = currentContainer();
298         if (!(current instanceof NormalizationResultBuilder)) {
299             checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
300         }
301     }
302
303     @SuppressWarnings("rawtypes")
304     private NormalizedNodeBuilder current() {
305         return builders.peek();
306     }
307
308     @SuppressWarnings("rawtypes")
309     private NormalizedNodeContainerBuilder currentContainer() {
310         final NormalizedNodeBuilder current = current();
311         if (current == null) {
312             return null;
313         }
314         checkState(current instanceof NormalizedNodeContainerBuilder, "%s is not a node container", current);
315         return (NormalizedNodeContainerBuilder) current;
316     }
317
318     @SuppressWarnings("rawtypes")
319     private NormalizedNodeBuilder currentScalar() {
320         final NormalizedNodeBuilder current = current();
321         checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);
322         return current;
323     }
324 }