863fd0c8af94f6cc9ed26a073feb5ca3e7eca94f
[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 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.io.DataOutput;
16 import java.io.IOException;
17 import java.io.OutputStream;
18 import java.io.StringWriter;
19 import java.nio.charset.StandardCharsets;
20 import java.util.Collection;
21 import java.util.Map.Entry;
22 import java.util.Set;
23 import javax.xml.transform.TransformerException;
24 import javax.xml.transform.TransformerFactory;
25 import javax.xml.transform.TransformerFactoryConfigurationError;
26 import javax.xml.transform.dom.DOMSource;
27 import javax.xml.transform.stream.StreamResult;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
37 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
38 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOutput, NormalizedNodeStreamWriter {
43     private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodeDataOutput.class);
44
45     private final DataOutput output;
46
47     private NormalizedNodeWriter normalizedNodeWriter;
48     private boolean headerWritten;
49     private QName lastLeafSetQName;
50     private boolean inSimple;
51
52     AbstractNormalizedNodeDataOutput(final DataOutput output) {
53         this.output = requireNonNull(output);
54     }
55
56     private void ensureHeaderWritten() throws IOException {
57         if (!headerWritten) {
58             output.writeByte(TokenTypes.SIGNATURE_MARKER);
59             output.writeShort(streamVersion());
60             headerWritten = true;
61         }
62     }
63
64     protected abstract short streamVersion();
65
66     protected abstract void writeString(String string) throws IOException;
67
68     @Override
69     public final void write(final int value) throws IOException {
70         ensureHeaderWritten();
71         output.write(value);
72     }
73
74     @Override
75     public final void write(final byte[] bytes) throws IOException {
76         ensureHeaderWritten();
77         output.write(bytes);
78     }
79
80     @Override
81     public final void write(final byte[] bytes, final int off, final int len) throws IOException {
82         ensureHeaderWritten();
83         output.write(bytes, off, len);
84     }
85
86     @Override
87     public final void writeBoolean(final boolean value) throws IOException {
88         ensureHeaderWritten();
89         output.writeBoolean(value);
90     }
91
92     @Override
93     public final void writeByte(final int value) throws IOException {
94         ensureHeaderWritten();
95         output.writeByte(value);
96     }
97
98     @Override
99     public final void writeShort(final int value) throws IOException {
100         ensureHeaderWritten();
101         output.writeShort(value);
102     }
103
104     @Override
105     public final void writeChar(final int value) throws IOException {
106         ensureHeaderWritten();
107         output.writeChar(value);
108     }
109
110     @Override
111     public final void writeInt(final int value) throws IOException {
112         ensureHeaderWritten();
113         output.writeInt(value);
114     }
115
116     @Override
117     public final void writeLong(final long value) throws IOException {
118         ensureHeaderWritten();
119         output.writeLong(value);
120     }
121
122     @Override
123     public final void writeFloat(final float value) throws IOException {
124         ensureHeaderWritten();
125         output.writeFloat(value);
126     }
127
128     @Override
129     public final void writeDouble(final double value) throws IOException {
130         ensureHeaderWritten();
131         output.writeDouble(value);
132     }
133
134     @Override
135     public final void writeBytes(final String str) throws IOException {
136         ensureHeaderWritten();
137         output.writeBytes(str);
138     }
139
140     @Override
141     public final void writeChars(final String str) throws IOException {
142         ensureHeaderWritten();
143         output.writeChars(str);
144     }
145
146     @Override
147     public final void writeUTF(final String str) throws IOException {
148         ensureHeaderWritten();
149         output.writeUTF(str);
150     }
151
152     private NormalizedNodeWriter normalizedNodeWriter() {
153         if (normalizedNodeWriter == null) {
154             normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(this);
155         }
156
157         return normalizedNodeWriter;
158     }
159
160     @Override
161     public void writeNormalizedNode(final NormalizedNode<?, ?> node) throws IOException {
162         ensureHeaderWritten();
163         normalizedNodeWriter().write(node);
164     }
165
166     @Override
167     public void startLeafNode(final NodeIdentifier name) throws IOException {
168         requireNonNull(name, "Node identifier should not be null");
169         LOG.trace("Starting a new leaf node");
170         startNode(name.getNodeType(), NodeTypes.LEAF_NODE);
171         inSimple = true;
172     }
173
174     @Override
175     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
176         requireNonNull(name, "Node identifier should not be null");
177         LOG.trace("Starting a new leaf set");
178
179         lastLeafSetQName = name.getNodeType();
180         startNode(name.getNodeType(), NodeTypes.LEAF_SET);
181     }
182
183     @Override
184     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
185         requireNonNull(name, "Node identifier should not be null");
186         LOG.trace("Starting a new ordered leaf set");
187
188         lastLeafSetQName = name.getNodeType();
189         startNode(name.getNodeType(), NodeTypes.ORDERED_LEAF_SET);
190     }
191
192     @Override
193     public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
194         LOG.trace("Starting a new leaf set entry node");
195
196         output.writeByte(NodeTypes.LEAF_SET_ENTRY_NODE);
197
198         // lastLeafSetQName is set if the parent LeafSetNode was previously written. Otherwise this is a
199         // stand alone LeafSetEntryNode so write out it's name here.
200         if (lastLeafSetQName == null) {
201             writeQName(name.getNodeType());
202         }
203         inSimple = true;
204     }
205
206     @Override
207     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
208         requireNonNull(name, "Node identifier should not be null");
209
210         LOG.trace("Starting a new container node");
211
212         startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
213     }
214
215     @Override
216     public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
217         requireNonNull(name, "Node identifier should not be null");
218
219         LOG.trace("Starting a new yang modeled anyXml node");
220
221         startNode(name.getNodeType(), NodeTypes.YANG_MODELED_ANY_XML_NODE);
222     }
223
224     @Override
225     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
226         requireNonNull(name, "Node identifier should not be null");
227         LOG.trace("Starting a new unkeyed list");
228
229         startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
230     }
231
232     @Override
233     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
234         requireNonNull(name, "Node identifier should not be null");
235         LOG.trace("Starting a new unkeyed list item");
236
237         startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
238     }
239
240     @Override
241     public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
242         requireNonNull(name, "Node identifier should not be null");
243         LOG.trace("Starting a new map node");
244
245         startNode(name.getNodeType(), NodeTypes.MAP_NODE);
246     }
247
248     @Override
249     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
250             throws IOException {
251         requireNonNull(identifier, "Node identifier should not be null");
252         LOG.trace("Starting a new map entry node");
253         startNode(identifier.getNodeType(), NodeTypes.MAP_ENTRY_NODE);
254
255         writeKeyValueMap(identifier.entrySet());
256     }
257
258     @Override
259     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
260         requireNonNull(name, "Node identifier should not be null");
261         LOG.trace("Starting a new ordered map node");
262
263         startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
264     }
265
266     @Override
267     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
268         requireNonNull(name, "Node identifier should not be null");
269         LOG.trace("Starting a new choice node");
270
271         startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
272     }
273
274     @Override
275     public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
276         requireNonNull(identifier, "Node identifier should not be null");
277         LOG.trace("Starting a new augmentation node");
278
279         output.writeByte(NodeTypes.AUGMENTATION_NODE);
280         writeAugmentationIdentifier(identifier);
281     }
282
283     @Override
284     public void startAnyxmlNode(final NodeIdentifier name) throws IOException {
285         requireNonNull(name, "Node identifier should not be null");
286         LOG.trace("Starting any xml node");
287         startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
288         inSimple = true;
289     }
290
291     @Override
292     public void scalarValue(final Object value) throws IOException {
293         writeObject(value);
294     }
295
296     @Override
297     public void domSourceValue(final DOMSource value) throws IOException {
298         try {
299             StreamResult xmlOutput = new StreamResult(new StringWriter());
300             TransformerFactory.newInstance().newTransformer().transform(value, xmlOutput);
301             writeObject(xmlOutput.getWriter().toString());
302         } catch (TransformerException | TransformerFactoryConfigurationError e) {
303             throw new IOException("Error writing anyXml", e);
304         }
305     }
306
307     @Override
308     public void endNode() throws IOException {
309         LOG.trace("Ending the node");
310         if (!inSimple) {
311             lastLeafSetQName = null;
312             output.writeByte(NodeTypes.END_NODE);
313         }
314         inSimple = false;
315     }
316
317     @Override
318     public void close() throws IOException {
319         flush();
320     }
321
322     @Override
323     public void flush() throws IOException {
324         if (output instanceof OutputStream) {
325             ((OutputStream)output).flush();
326         }
327     }
328
329     private void startNode(final QName qname, final byte nodeType) throws IOException {
330         requireNonNull(qname, "QName of node identifier should not be null.");
331         checkState(!inSimple, "Attempted to start a child in a simple node");
332
333         ensureHeaderWritten();
334
335         // First write the type of node
336         output.writeByte(nodeType);
337         // Write Start Tag
338         writeQName(qname);
339     }
340
341     private void writeObjSet(final Set<?> set) throws IOException {
342         output.writeInt(set.size());
343         for (Object o : set) {
344             checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)", o.getClass(), o);
345             writeString((String) o);
346         }
347     }
348
349     @Override
350     public void writeSchemaPath(final SchemaPath path) throws IOException {
351         ensureHeaderWritten();
352         output.writeBoolean(path.isAbsolute());
353
354         final Collection<QName> qnames = path.getPath();
355         output.writeInt(qnames.size());
356         for (QName qname : qnames) {
357             writeQName(qname);
358         }
359     }
360
361     @Override
362     public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
363         ensureHeaderWritten();
364         writeYangInstanceIdentifierInternal(identifier);
365     }
366
367     private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
368         Collection<PathArgument> pathArguments = identifier.getPathArguments();
369         output.writeInt(pathArguments.size());
370
371         for (PathArgument pathArgument : pathArguments) {
372             writePathArgument(pathArgument);
373         }
374     }
375
376     @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST",
377             justification = "The casts in the switch clauses are indirectly confirmed via the determination of 'type'.")
378     @Override
379     public void writePathArgument(final PathArgument pathArgument) throws IOException {
380
381         byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
382
383         output.writeByte(type);
384
385         switch (type) {
386             case PathArgumentTypes.NODE_IDENTIFIER:
387
388                 NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument;
389
390                 writeQName(nodeIdentifier.getNodeType());
391                 break;
392
393             case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
394
395                 NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
396                     (NodeIdentifierWithPredicates) pathArgument;
397                 writeQName(nodeIdentifierWithPredicates.getNodeType());
398
399                 writeKeyValueMap(nodeIdentifierWithPredicates.entrySet());
400                 break;
401
402             case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
403
404                 NodeWithValue<?> nodeWithValue = (NodeWithValue<?>) pathArgument;
405
406                 writeQName(nodeWithValue.getNodeType());
407                 writeObject(nodeWithValue.getValue());
408                 break;
409
410             case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
411
412                 // No Qname in augmentation identifier
413                 writeAugmentationIdentifier((AugmentationIdentifier) pathArgument);
414                 break;
415             default :
416                 throw new IllegalStateException("Unknown node identifier type is found : "
417                         + pathArgument.getClass().toString());
418         }
419     }
420
421     private void writeKeyValueMap(final Set<Entry<QName, Object>> entrySet) throws IOException {
422         if (!entrySet.isEmpty()) {
423             output.writeInt(entrySet.size());
424             for (Entry<QName, Object> entry : entrySet) {
425                 writeQName(entry.getKey());
426                 writeObject(entry.getValue());
427             }
428         } else {
429             output.writeInt(0);
430         }
431     }
432
433     void writeAugmentationIdentifier(final AugmentationIdentifier aid) throws IOException {
434         final Set<QName> qnames = aid.getPossibleChildNames();
435         // Write each child's qname separately, if list is empty send count as 0
436         if (!qnames.isEmpty()) {
437             output.writeInt(qnames.size());
438             for (QName qname : qnames) {
439                 writeQName(qname);
440             }
441         } else {
442             LOG.debug("augmentation node does not have any child");
443             output.writeInt(0);
444         }
445     }
446
447     private void writeObject(final Object value) throws IOException {
448
449         byte type = ValueTypes.getSerializableType(value);
450         // Write object type first
451         output.writeByte(type);
452
453         switch (type) {
454             case ValueTypes.BOOL_TYPE:
455                 output.writeBoolean((Boolean) value);
456                 break;
457             case ValueTypes.QNAME_TYPE:
458                 writeQName((QName) value);
459                 break;
460             case ValueTypes.INT_TYPE:
461                 output.writeInt((Integer) value);
462                 break;
463             case ValueTypes.BYTE_TYPE:
464                 output.writeByte((Byte) value);
465                 break;
466             case ValueTypes.LONG_TYPE:
467                 output.writeLong((Long) value);
468                 break;
469             case ValueTypes.SHORT_TYPE:
470                 output.writeShort((Short) value);
471                 break;
472             case ValueTypes.BITS_TYPE:
473                 writeObjSet((Set<?>) value);
474                 break;
475             case ValueTypes.BINARY_TYPE:
476                 byte[] bytes = (byte[]) value;
477                 output.writeInt(bytes.length);
478                 output.write(bytes);
479                 break;
480             case ValueTypes.YANG_IDENTIFIER_TYPE:
481                 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
482                 break;
483             case ValueTypes.EMPTY_TYPE:
484                 break;
485             case ValueTypes.STRING_BYTES_TYPE:
486                 final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
487                 output.writeInt(valueBytes.length);
488                 output.write(valueBytes);
489                 break;
490             default:
491                 output.writeUTF(value.toString());
492                 break;
493         }
494     }
495 }