2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.cluster.datastore.node.utils.stream;
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;
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;
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;
42 abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOutput, NormalizedNodeStreamWriter {
43 private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodeDataOutput.class);
45 private final DataOutput output;
47 private NormalizedNodeWriter normalizedNodeWriter;
48 private boolean headerWritten;
49 private QName lastLeafSetQName;
50 private boolean inSimple;
52 AbstractNormalizedNodeDataOutput(final DataOutput output) {
53 this.output = requireNonNull(output);
56 private void ensureHeaderWritten() throws IOException {
58 output.writeByte(TokenTypes.SIGNATURE_MARKER);
59 output.writeShort(streamVersion());
64 protected abstract short streamVersion();
66 protected abstract void writeString(String string) throws IOException;
69 public final void write(final int value) throws IOException {
70 ensureHeaderWritten();
75 public final void write(final byte[] bytes) throws IOException {
76 ensureHeaderWritten();
81 public final void write(final byte[] bytes, final int off, final int len) throws IOException {
82 ensureHeaderWritten();
83 output.write(bytes, off, len);
87 public final void writeBoolean(final boolean value) throws IOException {
88 ensureHeaderWritten();
89 output.writeBoolean(value);
93 public final void writeByte(final int value) throws IOException {
94 ensureHeaderWritten();
95 output.writeByte(value);
99 public final void writeShort(final int value) throws IOException {
100 ensureHeaderWritten();
101 output.writeShort(value);
105 public final void writeChar(final int value) throws IOException {
106 ensureHeaderWritten();
107 output.writeChar(value);
111 public final void writeInt(final int value) throws IOException {
112 ensureHeaderWritten();
113 output.writeInt(value);
117 public final void writeLong(final long value) throws IOException {
118 ensureHeaderWritten();
119 output.writeLong(value);
123 public final void writeFloat(final float value) throws IOException {
124 ensureHeaderWritten();
125 output.writeFloat(value);
129 public final void writeDouble(final double value) throws IOException {
130 ensureHeaderWritten();
131 output.writeDouble(value);
135 public final void writeBytes(final String str) throws IOException {
136 ensureHeaderWritten();
137 output.writeBytes(str);
141 public final void writeChars(final String str) throws IOException {
142 ensureHeaderWritten();
143 output.writeChars(str);
147 public final void writeUTF(final String str) throws IOException {
148 ensureHeaderWritten();
149 output.writeUTF(str);
152 private NormalizedNodeWriter normalizedNodeWriter() {
153 if (normalizedNodeWriter == null) {
154 normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(this);
157 return normalizedNodeWriter;
161 public void writeNormalizedNode(final NormalizedNode<?, ?> node) throws IOException {
162 ensureHeaderWritten();
163 normalizedNodeWriter().write(node);
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);
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");
179 lastLeafSetQName = name.getNodeType();
180 startNode(name.getNodeType(), NodeTypes.LEAF_SET);
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");
188 lastLeafSetQName = name.getNodeType();
189 startNode(name.getNodeType(), NodeTypes.ORDERED_LEAF_SET);
193 public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
194 LOG.trace("Starting a new leaf set entry node");
196 output.writeByte(NodeTypes.LEAF_SET_ENTRY_NODE);
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());
207 public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
208 requireNonNull(name, "Node identifier should not be null");
210 LOG.trace("Starting a new container node");
212 startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
216 public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
217 requireNonNull(name, "Node identifier should not be null");
219 LOG.trace("Starting a new yang modeled anyXml node");
221 startNode(name.getNodeType(), NodeTypes.YANG_MODELED_ANY_XML_NODE);
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");
229 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
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");
237 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
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");
245 startNode(name.getNodeType(), NodeTypes.MAP_NODE);
249 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
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);
255 writeKeyValueMap(identifier.entrySet());
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");
263 startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
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");
271 startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
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");
279 output.writeByte(NodeTypes.AUGMENTATION_NODE);
280 writeAugmentationIdentifier(identifier);
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);
292 public void scalarValue(final Object value) throws IOException {
297 public void domSourceValue(final DOMSource value) throws IOException {
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);
308 public void endNode() throws IOException {
309 LOG.trace("Ending the node");
311 lastLeafSetQName = null;
312 output.writeByte(NodeTypes.END_NODE);
318 public void close() throws IOException {
323 public void flush() throws IOException {
324 if (output instanceof OutputStream) {
325 ((OutputStream)output).flush();
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");
333 ensureHeaderWritten();
335 // First write the type of node
336 output.writeByte(nodeType);
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);
350 public void writeSchemaPath(final SchemaPath path) throws IOException {
351 ensureHeaderWritten();
352 output.writeBoolean(path.isAbsolute());
354 final Collection<QName> qnames = path.getPath();
355 output.writeInt(qnames.size());
356 for (QName qname : qnames) {
362 public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
363 ensureHeaderWritten();
364 writeYangInstanceIdentifierInternal(identifier);
367 private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
368 Collection<PathArgument> pathArguments = identifier.getPathArguments();
369 output.writeInt(pathArguments.size());
371 for (PathArgument pathArgument : pathArguments) {
372 writePathArgument(pathArgument);
376 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST",
377 justification = "The casts in the switch clauses are indirectly confirmed via the determination of 'type'.")
379 public void writePathArgument(final PathArgument pathArgument) throws IOException {
381 byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
383 output.writeByte(type);
386 case PathArgumentTypes.NODE_IDENTIFIER:
388 NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument;
390 writeQName(nodeIdentifier.getNodeType());
393 case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
395 NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
396 (NodeIdentifierWithPredicates) pathArgument;
397 writeQName(nodeIdentifierWithPredicates.getNodeType());
399 writeKeyValueMap(nodeIdentifierWithPredicates.entrySet());
402 case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
404 NodeWithValue<?> nodeWithValue = (NodeWithValue<?>) pathArgument;
406 writeQName(nodeWithValue.getNodeType());
407 writeObject(nodeWithValue.getValue());
410 case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
412 // No Qname in augmentation identifier
413 writeAugmentationIdentifier((AugmentationIdentifier) pathArgument);
416 throw new IllegalStateException("Unknown node identifier type is found : "
417 + pathArgument.getClass().toString());
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());
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) {
442 LOG.debug("augmentation node does not have any child");
447 private void writeObject(final Object value) throws IOException {
449 byte type = ValueTypes.getSerializableType(value);
450 // Write object type first
451 output.writeByte(type);
454 case ValueTypes.BOOL_TYPE:
455 output.writeBoolean((Boolean) value);
457 case ValueTypes.QNAME_TYPE:
458 writeQName((QName) value);
460 case ValueTypes.INT_TYPE:
461 output.writeInt((Integer) value);
463 case ValueTypes.BYTE_TYPE:
464 output.writeByte((Byte) value);
466 case ValueTypes.LONG_TYPE:
467 output.writeLong((Long) value);
469 case ValueTypes.SHORT_TYPE:
470 output.writeShort((Short) value);
472 case ValueTypes.BITS_TYPE:
473 writeObjSet((Set<?>) value);
475 case ValueTypes.BINARY_TYPE:
476 byte[] bytes = (byte[]) value;
477 output.writeInt(bytes.length);
480 case ValueTypes.YANG_IDENTIFIER_TYPE:
481 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
483 case ValueTypes.EMPTY_TYPE:
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);
491 output.writeUTF(value.toString());