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