b096a445f387dca2d84a05159f370c6240dba27c
[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.util.Collection;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import javax.xml.transform.TransformerException;
23 import javax.xml.transform.TransformerFactory;
24 import javax.xml.transform.TransformerFactoryConfigurationError;
25 import javax.xml.transform.dom.DOMSource;
26 import javax.xml.transform.stream.StreamResult;
27 import org.eclipse.jdt.annotation.NonNull;
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         LOG.trace("Starting a new leaf node");
169         startNode(name, NodeTypes.LEAF_NODE);
170         inSimple = true;
171     }
172
173     @Override
174     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
175         LOG.trace("Starting a new leaf set");
176         commonStartLeafSet(name, NodeTypes.LEAF_SET);
177     }
178
179     @Override
180     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
181         LOG.trace("Starting a new ordered leaf set");
182         commonStartLeafSet(name, NodeTypes.ORDERED_LEAF_SET);
183     }
184
185     private void commonStartLeafSet(final NodeIdentifier name, final byte nodeType) throws IOException {
186         startNode(name, nodeType);
187         lastLeafSetQName = name.getNodeType();
188     }
189
190     @Override
191     public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
192         LOG.trace("Starting a new leaf set entry node");
193
194         output.writeByte(NodeTypes.LEAF_SET_ENTRY_NODE);
195
196         // lastLeafSetQName is set if the parent LeafSetNode was previously written. Otherwise this is a
197         // stand alone LeafSetEntryNode so write out it's name here.
198         if (lastLeafSetQName == null) {
199             writeQName(name.getNodeType());
200         }
201         inSimple = true;
202     }
203
204     @Override
205     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
206         LOG.trace("Starting a new container node");
207         startNode(name, NodeTypes.CONTAINER_NODE);
208     }
209
210     @Override
211     public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
212         LOG.trace("Starting a new yang modeled anyXml node");
213         startNode(name, NodeTypes.YANG_MODELED_ANY_XML_NODE);
214     }
215
216     @Override
217     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
218         LOG.trace("Starting a new unkeyed list");
219         startNode(name, NodeTypes.UNKEYED_LIST);
220     }
221
222     @Override
223     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
224         LOG.trace("Starting a new unkeyed list item");
225         startNode(name, NodeTypes.UNKEYED_LIST_ITEM);
226     }
227
228     @Override
229     public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
230         LOG.trace("Starting a new map node");
231         startNode(name, NodeTypes.MAP_NODE);
232     }
233
234     @Override
235     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
236             throws IOException {
237         LOG.trace("Starting a new map entry node");
238         startNode(identifier, NodeTypes.MAP_ENTRY_NODE);
239         writeKeyValueMap(identifier.entrySet());
240     }
241
242     @Override
243     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
244         LOG.trace("Starting a new ordered map node");
245         startNode(name, NodeTypes.ORDERED_MAP_NODE);
246     }
247
248     @Override
249     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
250         LOG.trace("Starting a new choice node");
251         startNode(name, NodeTypes.CHOICE_NODE);
252     }
253
254     @Override
255     public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
256         requireNonNull(identifier, "Node identifier should not be null");
257         LOG.trace("Starting a new augmentation node");
258
259         output.writeByte(NodeTypes.AUGMENTATION_NODE);
260         writeAugmentationIdentifier(identifier);
261     }
262
263     @Override
264     public void startAnyxmlNode(final NodeIdentifier name) throws IOException {
265         LOG.trace("Starting any xml node");
266         startNode(name, NodeTypes.ANY_XML_NODE);
267         inSimple = true;
268     }
269
270     @Override
271     public void scalarValue(final Object value) throws IOException {
272         writeObject(value);
273     }
274
275     @Override
276     public void domSourceValue(final DOMSource value) throws IOException {
277         try {
278             StreamResult xmlOutput = new StreamResult(new StringWriter());
279             TransformerFactory.newInstance().newTransformer().transform(value, xmlOutput);
280             writeObject(xmlOutput.getWriter().toString());
281         } catch (TransformerException | TransformerFactoryConfigurationError e) {
282             throw new IOException("Error writing anyXml", e);
283         }
284     }
285
286     @Override
287     public void endNode() throws IOException {
288         LOG.trace("Ending the node");
289         if (!inSimple) {
290             lastLeafSetQName = null;
291             output.writeByte(NodeTypes.END_NODE);
292         }
293         inSimple = false;
294     }
295
296     @Override
297     public void close() throws IOException {
298         flush();
299     }
300
301     @Override
302     public void flush() throws IOException {
303         if (output instanceof OutputStream) {
304             ((OutputStream)output).flush();
305         }
306     }
307
308     private void startNode(final PathArgument arg, final byte nodeType) throws IOException {
309         requireNonNull(arg, "Node identifier should not be null");
310         checkState(!inSimple, "Attempted to start a child in a simple node");
311
312         ensureHeaderWritten();
313
314         // First write the type of node
315         output.writeByte(nodeType);
316         // Write Start Tag
317         writeQName(arg.getNodeType());
318     }
319
320     final void writeObjSet(final Set<?> set) throws IOException {
321         output.writeInt(set.size());
322         for (Object o : set) {
323             checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)", o.getClass(), o);
324             writeString((String) o);
325         }
326     }
327
328     @Override
329     public void writeSchemaPath(final SchemaPath path) throws IOException {
330         ensureHeaderWritten();
331         output.writeBoolean(path.isAbsolute());
332
333         final Collection<QName> qnames = path.getPath();
334         output.writeInt(qnames.size());
335         for (QName qname : qnames) {
336             writeQName(qname);
337         }
338     }
339
340     @Override
341     public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
342         ensureHeaderWritten();
343         writeYangInstanceIdentifierInternal(identifier);
344     }
345
346     final void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
347         Collection<PathArgument> pathArguments = identifier.getPathArguments();
348         output.writeInt(pathArguments.size());
349
350         for (PathArgument pathArgument : pathArguments) {
351             writePathArgument(pathArgument);
352         }
353     }
354
355     @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST",
356             justification = "The casts in the switch clauses are indirectly confirmed via the determination of 'type'.")
357     @Override
358     public void writePathArgument(final PathArgument pathArgument) throws IOException {
359
360         byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
361
362         output.writeByte(type);
363
364         switch (type) {
365             case PathArgumentTypes.NODE_IDENTIFIER:
366
367                 NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument;
368
369                 writeQName(nodeIdentifier.getNodeType());
370                 break;
371
372             case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
373
374                 NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
375                     (NodeIdentifierWithPredicates) pathArgument;
376                 writeQName(nodeIdentifierWithPredicates.getNodeType());
377
378                 writeKeyValueMap(nodeIdentifierWithPredicates.entrySet());
379                 break;
380
381             case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
382
383                 NodeWithValue<?> nodeWithValue = (NodeWithValue<?>) pathArgument;
384
385                 writeQName(nodeWithValue.getNodeType());
386                 writeObject(nodeWithValue.getValue());
387                 break;
388
389             case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
390
391                 // No Qname in augmentation identifier
392                 writeAugmentationIdentifier((AugmentationIdentifier) pathArgument);
393                 break;
394             default :
395                 throw new IllegalStateException("Unknown node identifier type is found : "
396                         + pathArgument.getClass().toString());
397         }
398     }
399
400     private void writeKeyValueMap(final Set<Entry<QName, Object>> entrySet) throws IOException {
401         if (!entrySet.isEmpty()) {
402             output.writeInt(entrySet.size());
403             for (Entry<QName, Object> entry : entrySet) {
404                 writeQName(entry.getKey());
405                 writeObject(entry.getValue());
406             }
407         } else {
408             output.writeInt(0);
409         }
410     }
411
412     void writeAugmentationIdentifier(final AugmentationIdentifier aid) throws IOException {
413         final Set<QName> qnames = aid.getPossibleChildNames();
414         // Write each child's qname separately, if list is empty send count as 0
415         if (!qnames.isEmpty()) {
416             output.writeInt(qnames.size());
417             for (QName qname : qnames) {
418                 writeQName(qname);
419             }
420         } else {
421             LOG.debug("augmentation node does not have any child");
422             output.writeInt(0);
423         }
424     }
425
426     abstract void writeObject(@NonNull DataOutput output, @NonNull Object value) throws IOException;
427
428     private void writeObject(final Object value) throws IOException {
429         writeObject(output, value);
430     }
431 }