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