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 com.google.common.base.Preconditions;
11 import java.io.DataOutput;
12 import java.io.IOException;
13 import java.io.OutputStream;
14 import java.io.StringWriter;
15 import java.nio.charset.StandardCharsets;
16 import java.util.Collection;
19 import javax.xml.transform.TransformerException;
20 import javax.xml.transform.TransformerFactory;
21 import javax.xml.transform.TransformerFactoryConfigurationError;
22 import javax.xml.transform.dom.DOMSource;
23 import javax.xml.transform.stream.StreamResult;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
33 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOutput, NormalizedNodeStreamWriter {
38 private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodeDataOutput.class);
40 private final DataOutput output;
42 private NormalizedNodeWriter normalizedNodeWriter;
43 private boolean headerWritten;
44 private QName lastLeafSetQName;
46 AbstractNormalizedNodeDataOutput(final DataOutput output) {
47 this.output = Preconditions.checkNotNull(output);
50 private void ensureHeaderWritten() throws IOException {
52 output.writeByte(TokenTypes.SIGNATURE_MARKER);
53 output.writeShort(streamVersion());
58 protected abstract short streamVersion();
60 protected abstract void writeQName(QName qname) throws IOException;
62 protected abstract void writeString(String string) throws IOException;
65 public final void write(final int value) throws IOException {
66 ensureHeaderWritten();
71 public final void write(final byte[] bytes) throws IOException {
72 ensureHeaderWritten();
77 public final void write(final byte[] bytes, final int off, final int len) throws IOException {
78 ensureHeaderWritten();
79 output.write(bytes, off, len);
83 public final void writeBoolean(final boolean value) throws IOException {
84 ensureHeaderWritten();
85 output.writeBoolean(value);
89 public final void writeByte(final int value) throws IOException {
90 ensureHeaderWritten();
91 output.writeByte(value);
95 public final void writeShort(final int value) throws IOException {
96 ensureHeaderWritten();
97 output.writeShort(value);
101 public final void writeChar(final int value) throws IOException {
102 ensureHeaderWritten();
103 output.writeChar(value);
107 public final void writeInt(final int value) throws IOException {
108 ensureHeaderWritten();
109 output.writeInt(value);
113 public final void writeLong(final long value) throws IOException {
114 ensureHeaderWritten();
115 output.writeLong(value);
119 public final void writeFloat(final float value) throws IOException {
120 ensureHeaderWritten();
121 output.writeFloat(value);
125 public final void writeDouble(final double value) throws IOException {
126 ensureHeaderWritten();
127 output.writeDouble(value);
131 public final void writeBytes(final String str) throws IOException {
132 ensureHeaderWritten();
133 output.writeBytes(str);
137 public final void writeChars(final String str) throws IOException {
138 ensureHeaderWritten();
139 output.writeChars(str);
143 public final void writeUTF(final String str) throws IOException {
144 ensureHeaderWritten();
145 output.writeUTF(str);
148 private NormalizedNodeWriter normalizedNodeWriter() {
149 if (normalizedNodeWriter == null) {
150 normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(this);
153 return normalizedNodeWriter;
157 public void writeNormalizedNode(final NormalizedNode<?, ?> node) throws IOException {
158 ensureHeaderWritten();
159 normalizedNodeWriter().write(node);
163 public void leafNode(final NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
164 Preconditions.checkNotNull(name, "Node identifier should not be null");
165 LOG.debug("Writing a new leaf node");
166 startNode(name.getNodeType(), NodeTypes.LEAF_NODE);
172 public void startLeafSet(final NodeIdentifier name, final int childSizeHint)
174 throws IOException, IllegalArgumentException {
175 Preconditions.checkNotNull(name, "Node identifier should not be null");
176 LOG.debug("Starting a new leaf set");
178 lastLeafSetQName = name.getNodeType();
179 startNode(name.getNodeType(), NodeTypes.LEAF_SET);
183 public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint)
184 throws IOException, IllegalArgumentException {
185 Preconditions.checkNotNull(name, "Node identifier should not be null");
186 LOG.debug("Starting a new ordered leaf set");
188 lastLeafSetQName = name.getNodeType();
189 startNode(name.getNodeType(), NodeTypes.ORDERED_LEAF_SET);
193 public void leafSetEntryNode(final QName name, final Object value) throws IOException, IllegalArgumentException {
194 LOG.debug("Writing 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) {
208 public void startContainerNode(final NodeIdentifier name, final int childSizeHint)
209 throws IOException, IllegalArgumentException {
210 Preconditions.checkNotNull(name, "Node identifier should not be null");
212 LOG.debug("Starting a new container node");
214 startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
218 public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint)
219 throws IOException, IllegalArgumentException {
220 Preconditions.checkNotNull(name, "Node identifier should not be null");
222 LOG.debug("Starting a new yang modeled anyXml node");
224 startNode(name.getNodeType(), NodeTypes.YANG_MODELED_ANY_XML_NODE);
228 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint)
229 throws IOException, IllegalArgumentException {
230 Preconditions.checkNotNull(name, "Node identifier should not be null");
231 LOG.debug("Starting a new unkeyed list");
233 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
237 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint)
238 throws IOException, IllegalStateException {
239 Preconditions.checkNotNull(name, "Node identifier should not be null");
240 LOG.debug("Starting a new unkeyed list item");
242 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
246 public void startMapNode(final NodeIdentifier name, final int childSizeHint)
247 throws IOException, IllegalArgumentException {
248 Preconditions.checkNotNull(name, "Node identifier should not be null");
249 LOG.debug("Starting a new map node");
251 startNode(name.getNodeType(), NodeTypes.MAP_NODE);
255 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
256 throws IOException, IllegalArgumentException {
257 Preconditions.checkNotNull(identifier, "Node identifier should not be null");
258 LOG.debug("Starting a new map entry node");
259 startNode(identifier.getNodeType(), NodeTypes.MAP_ENTRY_NODE);
261 writeKeyValueMap(identifier.getKeyValues());
266 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint)
267 throws IOException, IllegalArgumentException {
268 Preconditions.checkNotNull(name, "Node identifier should not be null");
269 LOG.debug("Starting a new ordered map node");
271 startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
275 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint)
276 throws IOException, IllegalArgumentException {
277 Preconditions.checkNotNull(name, "Node identifier should not be null");
278 LOG.debug("Starting a new choice node");
280 startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
284 public void startAugmentationNode(final AugmentationIdentifier identifier)
285 throws IOException, IllegalArgumentException {
286 Preconditions.checkNotNull(identifier, "Node identifier should not be null");
287 LOG.debug("Starting a new augmentation node");
289 output.writeByte(NodeTypes.AUGMENTATION_NODE);
290 writeQNameSet(identifier.getPossibleChildNames());
294 public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
295 Preconditions.checkNotNull(name, "Node identifier should not be null");
296 LOG.debug("Writing any xml node");
298 startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
301 StreamResult xmlOutput = new StreamResult(new StringWriter());
302 TransformerFactory.newInstance().newTransformer().transform((DOMSource)value, xmlOutput);
303 writeObject(xmlOutput.getWriter().toString());
304 } catch (TransformerException | TransformerFactoryConfigurationError e) {
305 throw new IOException("Error writing anyXml", e);
310 public void endNode() throws IOException, IllegalStateException {
311 LOG.debug("Ending the node");
313 output.writeByte(NodeTypes.END_NODE);
317 public void close() throws IOException {
322 public void flush() throws IOException {
323 if (output instanceof OutputStream) {
324 ((OutputStream)output).flush();
328 private void startNode(final QName qname, final byte nodeType) throws IOException {
329 Preconditions.checkNotNull(qname, "QName of node identifier should not be null.");
331 ensureHeaderWritten();
333 // First write the type of node
334 output.writeByte(nodeType);
339 private void writeObjSet(final Set<?> set) throws IOException {
340 output.writeInt(set.size());
341 for (Object o : set) {
342 Preconditions.checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)",
345 writeString((String) o);
350 public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
351 ensureHeaderWritten();
352 writeYangInstanceIdentifierInternal(identifier);
355 private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
356 Collection<PathArgument> pathArguments = identifier.getPathArguments();
357 output.writeInt(pathArguments.size());
359 for (PathArgument pathArgument : pathArguments) {
360 writePathArgument(pathArgument);
365 public void writePathArgument(final PathArgument pathArgument) throws IOException {
367 byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
369 output.writeByte(type);
372 case PathArgumentTypes.NODE_IDENTIFIER:
374 NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument;
376 writeQName(nodeIdentifier.getNodeType());
379 case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
381 NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
382 (NodeIdentifierWithPredicates) pathArgument;
383 writeQName(nodeIdentifierWithPredicates.getNodeType());
385 writeKeyValueMap(nodeIdentifierWithPredicates.getKeyValues());
388 case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
390 NodeWithValue<?> nodeWithValue = (NodeWithValue<?>) pathArgument;
392 writeQName(nodeWithValue.getNodeType());
393 writeObject(nodeWithValue.getValue());
396 case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
398 AugmentationIdentifier augmentationIdentifier = (AugmentationIdentifier) pathArgument;
400 // No Qname in augmentation identifier
401 writeQNameSet(augmentationIdentifier.getPossibleChildNames());
404 throw new IllegalStateException("Unknown node identifier type is found : "
405 + pathArgument.getClass().toString() );
409 private void writeKeyValueMap(final Map<QName, Object> keyValueMap) throws IOException {
410 if (keyValueMap != null && !keyValueMap.isEmpty()) {
411 output.writeInt(keyValueMap.size());
413 for (QName qname : keyValueMap.keySet()) {
415 writeObject(keyValueMap.get(qname));
422 private void writeQNameSet(final Set<QName> children) throws IOException {
423 // Write each child's qname separately, if list is empty send count as 0
424 if (children != null && !children.isEmpty()) {
425 output.writeInt(children.size());
426 for (QName qname : children) {
430 LOG.debug("augmentation node does not have any child");
435 private void writeObject(final Object value) throws IOException {
437 byte type = ValueTypes.getSerializableType(value);
438 // Write object type first
439 output.writeByte(type);
442 case ValueTypes.BOOL_TYPE:
443 output.writeBoolean((Boolean) value);
445 case ValueTypes.QNAME_TYPE:
446 writeQName((QName) value);
448 case ValueTypes.INT_TYPE:
449 output.writeInt((Integer) value);
451 case ValueTypes.BYTE_TYPE:
452 output.writeByte((Byte) value);
454 case ValueTypes.LONG_TYPE:
455 output.writeLong((Long) value);
457 case ValueTypes.SHORT_TYPE:
458 output.writeShort((Short) value);
460 case ValueTypes.BITS_TYPE:
461 writeObjSet((Set<?>) value);
463 case ValueTypes.BINARY_TYPE:
464 byte[] bytes = (byte[]) value;
465 output.writeInt(bytes.length);
468 case ValueTypes.YANG_IDENTIFIER_TYPE:
469 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
471 case ValueTypes.NULL_TYPE :
473 case ValueTypes.STRING_BYTES_TYPE:
474 final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
475 output.writeInt(valueBytes.length);
476 output.write(valueBytes);
479 output.writeUTF(value.toString());