4a761198225cae542e425d0be989689d1dcf2053
[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 startOrderedLeafSet(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
167         Preconditions.checkNotNull(name, "Node identifier should not be null");
168         LOG.debug("Starting a new ordered leaf set");
169
170         startNode(name.getNodeType(), NodeTypes.ORDERED_LEAF_SET);
171     }
172
173     @Override
174     public void leafSetEntryNode(final Object value) throws IOException, IllegalArgumentException {
175         LOG.debug("Writing a new leaf set entry node");
176
177         output.writeByte(NodeTypes.LEAF_SET_ENTRY_NODE);
178         writeObject(value);
179     }
180
181     @Override
182     public void startContainerNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
183         Preconditions.checkNotNull(name, "Node identifier should not be null");
184
185         LOG.debug("Starting a new container node");
186
187         startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
188     }
189
190     @Override
191     public void startYangModeledAnyXmlNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
192         Preconditions.checkNotNull(name, "Node identifier should not be null");
193
194         LOG.debug("Starting a new yang modeled anyXml node");
195
196         startNode(name.getNodeType(), NodeTypes.YANG_MODELED_ANY_XML_NODE);
197     }
198
199     @Override
200     public void startUnkeyedList(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
201         Preconditions.checkNotNull(name, "Node identifier should not be null");
202         LOG.debug("Starting a new unkeyed list");
203
204         startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
205     }
206
207     @Override
208     public void startUnkeyedListItem(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalStateException {
209         Preconditions.checkNotNull(name, "Node identifier should not be null");
210         LOG.debug("Starting a new unkeyed list item");
211
212         startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
213     }
214
215     @Override
216     public void startMapNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
217         Preconditions.checkNotNull(name, "Node identifier should not be null");
218         LOG.debug("Starting a new map node");
219
220         startNode(name.getNodeType(), NodeTypes.MAP_NODE);
221     }
222
223     @Override
224     public void startMapEntryNode(final YangInstanceIdentifier.NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException, IllegalArgumentException {
225         Preconditions.checkNotNull(identifier, "Node identifier should not be null");
226         LOG.debug("Starting a new map entry node");
227         startNode(identifier.getNodeType(), NodeTypes.MAP_ENTRY_NODE);
228
229         writeKeyValueMap(identifier.getKeyValues());
230
231     }
232
233     @Override
234     public void startOrderedMapNode(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 ordered map node");
237
238         startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
239     }
240
241     @Override
242     public void startChoiceNode(final YangInstanceIdentifier.NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
243         Preconditions.checkNotNull(name, "Node identifier should not be null");
244         LOG.debug("Starting a new choice node");
245
246         startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
247     }
248
249     @Override
250     public void startAugmentationNode(final YangInstanceIdentifier.AugmentationIdentifier identifier) throws IOException, IllegalArgumentException {
251         Preconditions.checkNotNull(identifier, "Node identifier should not be null");
252         LOG.debug("Starting a new augmentation node");
253
254         output.writeByte(NodeTypes.AUGMENTATION_NODE);
255         writeQNameSet(identifier.getPossibleChildNames());
256     }
257
258     @Override
259     public void anyxmlNode(final YangInstanceIdentifier.NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
260         Preconditions.checkNotNull(name, "Node identifier should not be null");
261         LOG.debug("Writing a new xml node");
262
263         startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
264
265         writeObject(value);
266     }
267
268     @Override
269     public void endNode() throws IOException, IllegalStateException {
270         LOG.debug("Ending the node");
271
272         output.writeByte(NodeTypes.END_NODE);
273     }
274
275     @Override
276     public void close() throws IOException {
277         flush();
278     }
279
280     @Override
281     public void flush() throws IOException {
282         if (output instanceof OutputStream) {
283             ((OutputStream)output).flush();
284         }
285     }
286
287     private void startNode(final QName qName, final byte nodeType) throws IOException {
288
289         Preconditions.checkNotNull(qName, "QName of node identifier should not be null.");
290
291         ensureHeaderWritten();
292
293         // First write the type of node
294         output.writeByte(nodeType);
295         // Write Start Tag
296         writeQName(qName);
297     }
298
299     private void writeObjSet(final Set<?> set) throws IOException {
300         output.writeInt(set.size());
301         for (Object o : set) {
302             Preconditions.checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)",
303                 o.getClass(), o);
304
305             writeString((String) o);
306         }
307     }
308
309     @Override
310     public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
311         ensureHeaderWritten();
312         writeYangInstanceIdentifierInternal(identifier);
313     }
314
315     private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
316         Collection<YangInstanceIdentifier.PathArgument> pathArguments = identifier.getPathArguments();
317         output.writeInt(pathArguments.size());
318
319         for(YangInstanceIdentifier.PathArgument pathArgument : pathArguments) {
320             writePathArgument(pathArgument);
321         }
322     }
323
324     @Override
325     public void writePathArgument(final YangInstanceIdentifier.PathArgument pathArgument) throws IOException {
326
327         byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
328
329         output.writeByte(type);
330
331         switch(type) {
332             case PathArgumentTypes.NODE_IDENTIFIER:
333
334                 YangInstanceIdentifier.NodeIdentifier nodeIdentifier =
335                     (YangInstanceIdentifier.NodeIdentifier) pathArgument;
336
337                 writeQName(nodeIdentifier.getNodeType());
338                 break;
339
340             case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
341
342                 YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
343                     (YangInstanceIdentifier.NodeIdentifierWithPredicates) pathArgument;
344                 writeQName(nodeIdentifierWithPredicates.getNodeType());
345
346                 writeKeyValueMap(nodeIdentifierWithPredicates.getKeyValues());
347                 break;
348
349             case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
350
351                 YangInstanceIdentifier.NodeWithValue nodeWithValue =
352                     (YangInstanceIdentifier.NodeWithValue) pathArgument;
353
354                 writeQName(nodeWithValue.getNodeType());
355                 writeObject(nodeWithValue.getValue());
356                 break;
357
358             case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
359
360                 YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifier =
361                     (YangInstanceIdentifier.AugmentationIdentifier) pathArgument;
362
363                 // No Qname in augmentation identifier
364                 writeQNameSet(augmentationIdentifier.getPossibleChildNames());
365                 break;
366             default :
367                 throw new IllegalStateException("Unknown node identifier type is found : " + pathArgument.getClass().toString() );
368         }
369     }
370
371     private void writeKeyValueMap(final Map<QName, Object> keyValueMap) throws IOException {
372         if (keyValueMap != null && !keyValueMap.isEmpty()) {
373             output.writeInt(keyValueMap.size());
374
375             for (QName qName : keyValueMap.keySet()) {
376                 writeQName(qName);
377                 writeObject(keyValueMap.get(qName));
378             }
379         } else {
380             output.writeInt(0);
381         }
382     }
383
384     private void writeQNameSet(final Set<QName> children) throws IOException {
385         // Write each child's qname separately, if list is empty send count as 0
386         if (children != null && !children.isEmpty()) {
387             output.writeInt(children.size());
388             for (QName qName : children) {
389                 writeQName(qName);
390             }
391         } else {
392             LOG.debug("augmentation node does not have any child");
393             output.writeInt(0);
394         }
395     }
396
397     private void writeObject(final Object value) throws IOException {
398
399         byte type = ValueTypes.getSerializableType(value);
400         // Write object type first
401         output.writeByte(type);
402
403         switch (type) {
404             case ValueTypes.BOOL_TYPE:
405                 output.writeBoolean((Boolean) value);
406                 break;
407             case ValueTypes.QNAME_TYPE:
408                 writeQName((QName) value);
409                 break;
410             case ValueTypes.INT_TYPE:
411                 output.writeInt((Integer) value);
412                 break;
413             case ValueTypes.BYTE_TYPE:
414                 output.writeByte((Byte) value);
415                 break;
416             case ValueTypes.LONG_TYPE:
417                 output.writeLong((Long) value);
418                 break;
419             case ValueTypes.SHORT_TYPE:
420                 output.writeShort((Short) value);
421                 break;
422             case ValueTypes.BITS_TYPE:
423                 writeObjSet((Set<?>) value);
424                 break;
425             case ValueTypes.BINARY_TYPE:
426                 byte[] bytes = (byte[]) value;
427                 output.writeInt(bytes.length);
428                 output.write(bytes);
429                 break;
430             case ValueTypes.YANG_IDENTIFIER_TYPE:
431                 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
432                 break;
433             case ValueTypes.NULL_TYPE :
434                 break;
435             case ValueTypes.STRING_BYTES_TYPE:
436                 final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
437                 output.writeInt(valueBytes.length);
438                 output.write(valueBytes);
439                 break;
440             default:
441                 output.writeUTF(value.toString());
442                 break;
443         }
444     }
445 }