BUG-4626: create AbstractNormalizedNodeDataOutput
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / stream / AbstractNormalizedNodeDataOutput.java
1 /*
2  * Copyright (c) 2015 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.controller.cluster.datastore.node.utils.stream;
9
10 import com.google.common.base.Preconditions;
11 import java.io.DataOutput;
12 import java.io.IOException;
13 import java.io.OutputStream;
14 import java.nio.charset.StandardCharsets;
15 import java.util.Collection;
16 import java.util.Map;
17 import java.util.Set;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
22 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOutput, NormalizedNodeStreamWriter {
27     private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodeDataOutput.class);
28
29     private final DataOutput output;
30
31     private NormalizedNodeWriter normalizedNodeWriter;
32     private boolean headerWritten;
33
34     AbstractNormalizedNodeDataOutput(final DataOutput output) {
35         this.output = Preconditions.checkNotNull(output);
36     }
37
38     private void ensureHeaderWritten() throws IOException {
39         if (!headerWritten) {
40             output.writeByte(TokenTypes.SIGNATURE_MARKER);
41             output.writeShort(streamVersion());
42             headerWritten = true;
43         }
44     }
45
46     protected abstract short streamVersion();
47     protected abstract void writeQName(QName qname) throws IOException;
48     protected abstract void writeString(String string) throws IOException;
49
50     @Override
51     public final void write(final int b) throws IOException {
52         ensureHeaderWritten();
53         output.write(b);
54     }
55
56     @Override
57     public final void write(final byte[] b) throws IOException {
58         ensureHeaderWritten();
59         output.write(b);
60     }
61
62     @Override
63     public final void write(final byte[] b, final int off, final int len) throws IOException {
64         ensureHeaderWritten();
65         output.write(b, off, len);
66     }
67
68     @Override
69     public final void writeBoolean(final boolean v) throws IOException {
70         ensureHeaderWritten();
71         output.writeBoolean(v);
72     }
73
74     @Override
75     public final void writeByte(final int v) throws IOException {
76         ensureHeaderWritten();
77         output.writeByte(v);
78     }
79
80     @Override
81     public final void writeShort(final int v) throws IOException {
82         ensureHeaderWritten();
83         output.writeShort(v);
84     }
85
86     @Override
87     public final void writeChar(final int v) throws IOException {
88         ensureHeaderWritten();
89         output.writeChar(v);
90     }
91
92     @Override
93     public final void writeInt(final int v) throws IOException {
94         ensureHeaderWritten();
95         output.writeInt(v);
96     }
97
98     @Override
99     public final void writeLong(final long v) throws IOException {
100         ensureHeaderWritten();
101         output.writeLong(v);
102     }
103
104     @Override
105     public final void writeFloat(final float v) throws IOException {
106         ensureHeaderWritten();
107         output.writeFloat(v);
108     }
109
110     @Override
111     public final void writeDouble(final double v) throws IOException {
112         ensureHeaderWritten();
113         output.writeDouble(v);
114     }
115
116     @Override
117     public final void writeBytes(final String s) throws IOException {
118         ensureHeaderWritten();
119         output.writeBytes(s);
120     }
121
122     @Override
123     public final void writeChars(final String s) throws IOException {
124         ensureHeaderWritten();
125         output.writeChars(s);
126     }
127
128     @Override
129     public final void writeUTF(final String s) throws IOException {
130         ensureHeaderWritten();
131         output.writeUTF(s);
132     }
133
134     private NormalizedNodeWriter normalizedNodeWriter() {
135         if(normalizedNodeWriter == null) {
136             normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(this);
137         }
138
139         return normalizedNodeWriter;
140     }
141
142     @Override
143     public void writeNormalizedNode(final NormalizedNode<?, ?> node) throws IOException {
144         ensureHeaderWritten();
145         normalizedNodeWriter().write(node);
146     }
147
148     @Override
149     public void leafNode(final YangInstanceIdentifier.NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
150         Preconditions.checkNotNull(name, "Node identifier should not be null");
151         LOG.debug("Writing a new leaf node");
152         startNode(name.getNodeType(), NodeTypes.LEAF_NODE);
153
154         writeObject(value);
155     }
156
157     @Override
158     public void startLeafSet(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
159         Preconditions.checkNotNull(name, "Node identifier should not be null");
160         LOG.debug("Starting a new leaf set");
161
162         startNode(name.getNodeType(), NodeTypes.LEAF_SET);
163     }
164
165     @Override
166     public void leafSetEntryNode(final Object value) throws IOException, IllegalArgumentException {
167         LOG.debug("Writing a new leaf set entry node");
168
169         output.writeByte(NodeTypes.LEAF_SET_ENTRY_NODE);
170         writeObject(value);
171     }
172
173     @Override
174     public void startContainerNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
175         Preconditions.checkNotNull(name, "Node identifier should not be null");
176
177         LOG.debug("Starting a new container node");
178
179         startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
180     }
181
182     @Override
183     public void startYangModeledAnyXmlNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
184         Preconditions.checkNotNull(name, "Node identifier should not be null");
185
186         LOG.debug("Starting a new yang modeled anyXml node");
187
188         startNode(name.getNodeType(), NodeTypes.YANG_MODELED_ANY_XML_NODE);
189     }
190
191     @Override
192     public void startUnkeyedList(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
193         Preconditions.checkNotNull(name, "Node identifier should not be null");
194         LOG.debug("Starting a new unkeyed list");
195
196         startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
197     }
198
199     @Override
200     public void startUnkeyedListItem(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalStateException {
201         Preconditions.checkNotNull(name, "Node identifier should not be null");
202         LOG.debug("Starting a new unkeyed list item");
203
204         startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
205     }
206
207     @Override
208     public void startMapNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
209         Preconditions.checkNotNull(name, "Node identifier should not be null");
210         LOG.debug("Starting a new map node");
211
212         startNode(name.getNodeType(), NodeTypes.MAP_NODE);
213     }
214
215     @Override
216     public void startMapEntryNode(final YangInstanceIdentifier.NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException, IllegalArgumentException {
217         Preconditions.checkNotNull(identifier, "Node identifier should not be null");
218         LOG.debug("Starting a new map entry node");
219         startNode(identifier.getNodeType(), NodeTypes.MAP_ENTRY_NODE);
220
221         writeKeyValueMap(identifier.getKeyValues());
222
223     }
224
225     @Override
226     public void startOrderedMapNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
227         Preconditions.checkNotNull(name, "Node identifier should not be null");
228         LOG.debug("Starting a new ordered map node");
229
230         startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
231     }
232
233     @Override
234     public void startChoiceNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
235         Preconditions.checkNotNull(name, "Node identifier should not be null");
236         LOG.debug("Starting a new choice node");
237
238         startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
239     }
240
241     @Override
242     public void startAugmentationNode(final YangInstanceIdentifier.AugmentationIdentifier identifier) throws IOException, IllegalArgumentException {
243         Preconditions.checkNotNull(identifier, "Node identifier should not be null");
244         LOG.debug("Starting a new augmentation node");
245
246         output.writeByte(NodeTypes.AUGMENTATION_NODE);
247         writeQNameSet(identifier.getPossibleChildNames());
248     }
249
250     @Override
251     public void anyxmlNode(final YangInstanceIdentifier.NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
252         Preconditions.checkNotNull(name, "Node identifier should not be null");
253         LOG.debug("Writing a new xml node");
254
255         startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
256
257         writeObject(value);
258     }
259
260     @Override
261     public void endNode() throws IOException, IllegalStateException {
262         LOG.debug("Ending the node");
263
264         output.writeByte(NodeTypes.END_NODE);
265     }
266
267     @Override
268     public void close() throws IOException {
269         flush();
270     }
271
272     @Override
273     public void flush() throws IOException {
274         if (output instanceof OutputStream) {
275             ((OutputStream)output).flush();
276         }
277     }
278
279     private void startNode(final QName qName, final byte nodeType) throws IOException {
280
281         Preconditions.checkNotNull(qName, "QName of node identifier should not be null.");
282
283         ensureHeaderWritten();
284
285         // First write the type of node
286         output.writeByte(nodeType);
287         // Write Start Tag
288         writeQName(qName);
289     }
290
291     private void writeObjSet(final Set<?> set) throws IOException {
292         output.writeInt(set.size());
293         for (Object o : set) {
294             Preconditions.checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)",
295                 o.getClass(), o);
296
297             writeString((String) o);
298         }
299     }
300
301     @Override
302     public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
303         ensureHeaderWritten();
304         writeYangInstanceIdentifierInternal(identifier);
305     }
306
307     private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
308         Collection<YangInstanceIdentifier.PathArgument> pathArguments = identifier.getPathArguments();
309         output.writeInt(pathArguments.size());
310
311         for(YangInstanceIdentifier.PathArgument pathArgument : pathArguments) {
312             writePathArgument(pathArgument);
313         }
314     }
315
316     @Override
317     public void writePathArgument(final YangInstanceIdentifier.PathArgument pathArgument) throws IOException {
318
319         byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
320
321         output.writeByte(type);
322
323         switch(type) {
324             case PathArgumentTypes.NODE_IDENTIFIER:
325
326                 YangInstanceIdentifier.NodeIdentifier nodeIdentifier =
327                     (YangInstanceIdentifier.NodeIdentifier) pathArgument;
328
329                 writeQName(nodeIdentifier.getNodeType());
330                 break;
331
332             case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
333
334                 YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
335                     (YangInstanceIdentifier.NodeIdentifierWithPredicates) pathArgument;
336                 writeQName(nodeIdentifierWithPredicates.getNodeType());
337
338                 writeKeyValueMap(nodeIdentifierWithPredicates.getKeyValues());
339                 break;
340
341             case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
342
343                 YangInstanceIdentifier.NodeWithValue nodeWithValue =
344                     (YangInstanceIdentifier.NodeWithValue) pathArgument;
345
346                 writeQName(nodeWithValue.getNodeType());
347                 writeObject(nodeWithValue.getValue());
348                 break;
349
350             case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
351
352                 YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifier =
353                     (YangInstanceIdentifier.AugmentationIdentifier) pathArgument;
354
355                 // No Qname in augmentation identifier
356                 writeQNameSet(augmentationIdentifier.getPossibleChildNames());
357                 break;
358             default :
359                 throw new IllegalStateException("Unknown node identifier type is found : " + pathArgument.getClass().toString() );
360         }
361     }
362
363     private void writeKeyValueMap(final Map<QName, Object> keyValueMap) throws IOException {
364         if (keyValueMap != null && !keyValueMap.isEmpty()) {
365             output.writeInt(keyValueMap.size());
366
367             for (QName qName : keyValueMap.keySet()) {
368                 writeQName(qName);
369                 writeObject(keyValueMap.get(qName));
370             }
371         } else {
372             output.writeInt(0);
373         }
374     }
375
376     private void writeQNameSet(final Set<QName> children) throws IOException {
377         // Write each child's qname separately, if list is empty send count as 0
378         if (children != null && !children.isEmpty()) {
379             output.writeInt(children.size());
380             for (QName qName : children) {
381                 writeQName(qName);
382             }
383         } else {
384             LOG.debug("augmentation node does not have any child");
385             output.writeInt(0);
386         }
387     }
388
389     private void writeObject(final Object value) throws IOException {
390
391         byte type = ValueTypes.getSerializableType(value);
392         // Write object type first
393         output.writeByte(type);
394
395         switch (type) {
396             case ValueTypes.BOOL_TYPE:
397                 output.writeBoolean((Boolean) value);
398                 break;
399             case ValueTypes.QNAME_TYPE:
400                 writeQName((QName) value);
401                 break;
402             case ValueTypes.INT_TYPE:
403                 output.writeInt((Integer) value);
404                 break;
405             case ValueTypes.BYTE_TYPE:
406                 output.writeByte((Byte) value);
407                 break;
408             case ValueTypes.LONG_TYPE:
409                 output.writeLong((Long) value);
410                 break;
411             case ValueTypes.SHORT_TYPE:
412                 output.writeShort((Short) value);
413                 break;
414             case ValueTypes.BITS_TYPE:
415                 writeObjSet((Set<?>) value);
416                 break;
417             case ValueTypes.BINARY_TYPE:
418                 byte[] bytes = (byte[]) value;
419                 output.writeInt(bytes.length);
420                 output.write(bytes);
421                 break;
422             case ValueTypes.YANG_IDENTIFIER_TYPE:
423                 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
424                 break;
425             case ValueTypes.NULL_TYPE :
426                 break;
427             case ValueTypes.STRING_BYTES_TYPE:
428                 final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
429                 output.writeInt(valueBytes.length);
430                 output.write(valueBytes);
431                 break;
432             default:
433                 output.writeUTF(value.toString());
434                 break;
435         }
436     }
437 }