Switch to Objects.requireNonNull
[mdsal.git] / binding2 / mdsal-binding2-dom-codec / src / main / java / org / opendaylight / mdsal / binding / javav2 / dom / codec / impl / serializer / BindingToNormalizedStreamWriter.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.codec.impl.serializer;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Preconditions;
14 import java.io.IOException;
15 import java.util.AbstractMap;
16 import java.util.ArrayDeque;
17 import java.util.Deque;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.CaseNodeCodecContext;
21 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.KeyedListNodeCodecContext;
22 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.AnyxmlNodeCodecContext;
23 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.DataContainerCodecContext;
24 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.LeafNodeCodecContext;
25 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.NodeCodecContext;
26 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.TreeNodeCodecContext;
27 import org.opendaylight.mdsal.binding.javav2.spec.base.IdentifiableItem;
28 import org.opendaylight.mdsal.binding.javav2.spec.base.Item;
29 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
30 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
31 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
32 import org.opendaylight.yangtools.concepts.Delegator;
33 import org.opendaylight.yangtools.concepts.Identifiable;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
38 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
39 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
40
41 /**
42  * Stream event writer of Binding v2 representation.
43  */
44 @Beta
45 public final class BindingToNormalizedStreamWriter
46         implements BindingStreamEventWriter, Delegator<NormalizedNodeStreamWriter> {
47
48     private final Deque<NodeCodecContext<?>> schema = new ArrayDeque<>();
49     private final NormalizedNodeStreamWriter delegate;
50     private final NodeCodecContext<?> rootNodeSchema;
51
52     private BindingToNormalizedStreamWriter(final NodeCodecContext<?> rootNodeSchema,
53             final NormalizedNodeStreamWriter delegate) {
54         this.rootNodeSchema = requireNonNull(rootNodeSchema);
55         this.delegate = requireNonNull(delegate);
56     }
57
58     /**
59      * Create instance of Binding v2 representation writer.
60      *
61      * @param schema
62      *            - codec schema
63      * @param delegate
64      *            - DOM writer delegator
65      * @return instance of binding writer
66      */
67     public static BindingToNormalizedStreamWriter create(final NodeCodecContext<?> schema,
68             final NormalizedNodeStreamWriter delegate) {
69         return new BindingToNormalizedStreamWriter(schema, delegate);
70     }
71
72     private void emitSchema(final Object schemaNode) {
73         delegate.nextDataSchemaNode((DataSchemaNode) schemaNode);
74     }
75
76     /**
77      * Retrieves, but does not remove, the head of the queue represented by node
78      * codec context.
79      *
80      * @return head of queue
81      */
82     NodeCodecContext<?> current() {
83         return schema.peek();
84     }
85
86     private NodeIdentifier duplicateSchemaEnter() {
87         final NodeCodecContext<?> next;
88         if (current() == null) {
89             // Entry of first node
90             next = rootNodeSchema;
91         } else {
92             next = current();
93         }
94         this.schema.push(next);
95         return (NodeIdentifier) current().getDomPathArgument();
96     }
97
98     @SuppressWarnings({ "unchecked", "rawtypes" })
99     private <T extends YangInstanceIdentifier.PathArgument> T enter(final Class<?> name, final Class<T> identifier) {
100         final NodeCodecContext<?> next;
101         if (current() == null) {
102             // Entry of first node
103             next = rootNodeSchema;
104         } else {
105             Preconditions.checkArgument((current() instanceof DataContainerCodecContext), "Could not start node %s",
106                     name);
107             next = ((DataContainerCodecContext) current()).streamChild(name);
108         }
109         this.schema.push(next);
110         return (T) next.getDomPathArgument();
111     }
112
113     @SuppressWarnings("rawtypes")
114     private <T extends YangInstanceIdentifier.PathArgument> T enter(final String localName, final Class<T> identifier) {
115         final NodeCodecContext<?> current = current();
116         final NodeCodecContext<?> next = ((TreeNodeCodecContext) current).getLeafChild(localName);
117         this.schema.push(next);
118         @SuppressWarnings("unchecked")
119         final T arg = (T) next.getDomPathArgument();
120         return arg;
121     }
122
123     @Override
124     public NormalizedNodeStreamWriter getDelegate() {
125         return delegate;
126     }
127
128     @Override
129     public void endNode() throws IOException {
130         final NodeCodecContext<?> left = schema.pop();
131         // NormalizedNode writer does not have entry into case, but into choice
132         // so for leaving case, we do not emit endNode.
133         if (!(left instanceof CaseNodeCodecContext)) {
134             getDelegate().endNode();
135         }
136     }
137
138     private Map.Entry<NodeIdentifier, Object> serializeLeaf(final String localName, final Object value) {
139         Preconditions.checkArgument(current() instanceof TreeNodeCodecContext);
140
141         final TreeNodeCodecContext<?, ?> currentCasted = (TreeNodeCodecContext<?, ?>) current();
142         final LeafNodeCodecContext<?> leafContext = currentCasted.getLeafChild(localName);
143
144         final NodeIdentifier domArg = (NodeIdentifier) leafContext.getDomPathArgument();
145         final Object domValue = leafContext.getValueCodec().serialize(value);
146         emitSchema(leafContext.getSchema());
147         return new AbstractMap.SimpleEntry<>(domArg, domValue);
148     }
149
150     private Map.Entry<NodeIdentifier, Object> serializeAnyxml(final String localName, final Object value) {
151         Preconditions.checkArgument(current() instanceof TreeNodeCodecContext);
152
153         final TreeNodeCodecContext<?, ?> currentCasted = (TreeNodeCodecContext<?, ?>) current();
154         final AnyxmlNodeCodecContext<?> anyxmlContext = currentCasted.getAnyxmlChild(localName);
155
156         final NodeIdentifier domArg = (NodeIdentifier) anyxmlContext.getDomPathArgument();
157         final Object domValue = anyxmlContext.getValueCodec().serialize(value);
158         emitSchema(anyxmlContext.getSchema());
159         return new AbstractMap.SimpleEntry<>(domArg, domValue);
160     }
161
162     @Override
163     public void leafNode(final String localName, final Object value) throws IOException {
164         final Entry<NodeIdentifier, Object> dom = serializeLeaf(localName, value);
165         getDelegate().leafNode(dom.getKey(), dom.getValue());
166     }
167
168     @Override
169     public void anyxmlNode(final String name, final Object value) throws IOException {
170         final Entry<NodeIdentifier, Object> dom = serializeAnyxml(name, value);
171         getDelegate().anyxmlNode(dom.getKey(), dom.getValue());
172     }
173
174     @Override
175     public void leafSetEntryNode(final Object value) throws IOException {
176         final LeafNodeCodecContext<?> ctx = (LeafNodeCodecContext<?>) current();
177         getDelegate().leafSetEntryNode(ctx.getSchema().getQName(), ctx.getValueCodec().serialize(value));
178     }
179
180     @Override
181     public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType) throws IOException {
182         getDelegate().startAugmentationNode(enter(augmentationType, AugmentationIdentifier.class));
183     }
184
185     @Override
186     public void startCase(final Class<? extends TreeNode> caze, final int childSizeHint) {
187         enter(caze, NodeIdentifier.class);
188     }
189
190     @Override
191     public <T extends TreeNode> void startChoiceNode(final Item<T> choice, final int childSizeHint) throws IOException {
192         getDelegate().startChoiceNode(enter(choice.getType(), NodeIdentifier.class), childSizeHint);
193     }
194
195     @Override
196     public void startContainerNode(final Class<? extends TreeNode> object, final int childSizeHint)
197             throws IOException {
198         getDelegate().startContainerNode(enter(object, NodeIdentifier.class), childSizeHint);
199     }
200
201     @Override
202     public void startLeafSet(final String localName, final int childSizeHint) throws IOException {
203         final NodeIdentifier id = enter(localName, NodeIdentifier.class);
204         emitSchema(current().getSchema());
205         getDelegate().startLeafSet(id, childSizeHint);
206     }
207
208     @Override
209     public void startOrderedLeafSet(final String localName, final int childSizeHint) throws IOException {
210         getDelegate().startOrderedLeafSet(enter(localName, NodeIdentifier.class), childSizeHint);
211     }
212
213     @Override
214     public <I extends TreeNode, T> void startMapEntryNode(final IdentifiableItem<I, T> keyValues,
215             final int childSizeHint) throws IOException {
216         duplicateSchemaEnter();
217         final NodeIdentifierWithPredicates identifier = ((KeyedListNodeCodecContext<?>) current()).serialize(keyValues);
218         getDelegate().startMapEntryNode(identifier, childSizeHint);
219     }
220
221     @Override
222     public <T extends TreeNode & Identifiable<?>> void startMapNode(final Class<T> mapEntryType,
223             final int childSizeHint) throws IOException {
224         getDelegate().startMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
225     }
226
227     @Override
228     public <T extends TreeNode & Identifiable<?>> void startOrderedMapNode(final Class<T> mapEntryType,
229             final int childSizeHint) throws IOException {
230         getDelegate().startOrderedMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
231     }
232
233     @Override
234     public void startUnkeyedList(final Class<? extends TreeNode> localName, final int childSizeHint)
235             throws IOException {
236         getDelegate().startUnkeyedList(enter(localName, NodeIdentifier.class), childSizeHint);
237     }
238
239     @Override
240     public void startUnkeyedListItem(final int childSizeHint) throws IOException {
241         getDelegate().startUnkeyedListItem(duplicateSchemaEnter(), childSizeHint);
242     }
243
244     @Override
245     public void flush() throws IOException {
246         getDelegate().flush();
247     }
248
249     @Override
250     public void close() throws IOException {
251         getDelegate().close();
252     }
253
254     @Override
255     public void startAnydataNode(final String name, final Object value) throws IOException {
256         // TODO will be done when https://bugs.opendaylight.org/show_bug.cgi?id=8516 is completed
257     }
258 }