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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.io.DataOutput;
13 import java.io.IOException;
14 import java.io.OutputStream;
15 import java.io.StringWriter;
16 import java.nio.charset.StandardCharsets;
17 import java.util.Collection;
20 import javax.xml.transform.TransformerException;
21 import javax.xml.transform.TransformerFactory;
22 import javax.xml.transform.TransformerFactoryConfigurationError;
23 import javax.xml.transform.dom.DOMSource;
24 import javax.xml.transform.stream.StreamResult;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
34 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
35 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOutput, NormalizedNodeStreamWriter {
40 private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodeDataOutput.class);
42 private final DataOutput output;
44 private NormalizedNodeWriter normalizedNodeWriter;
45 private boolean headerWritten;
46 private QName lastLeafSetQName;
48 AbstractNormalizedNodeDataOutput(final DataOutput output) {
49 this.output = Preconditions.checkNotNull(output);
52 private void ensureHeaderWritten() throws IOException {
54 output.writeByte(TokenTypes.SIGNATURE_MARKER);
55 output.writeShort(streamVersion());
60 protected abstract short streamVersion();
62 protected abstract void writeQName(QName qname) throws IOException;
64 protected abstract void writeString(String string) throws IOException;
67 public final void write(final int value) throws IOException {
68 ensureHeaderWritten();
73 public final void write(final byte[] bytes) throws IOException {
74 ensureHeaderWritten();
79 public final void write(final byte[] bytes, final int off, final int len) throws IOException {
80 ensureHeaderWritten();
81 output.write(bytes, off, len);
85 public final void writeBoolean(final boolean value) throws IOException {
86 ensureHeaderWritten();
87 output.writeBoolean(value);
91 public final void writeByte(final int value) throws IOException {
92 ensureHeaderWritten();
93 output.writeByte(value);
97 public final void writeShort(final int value) throws IOException {
98 ensureHeaderWritten();
99 output.writeShort(value);
103 public final void writeChar(final int value) throws IOException {
104 ensureHeaderWritten();
105 output.writeChar(value);
109 public final void writeInt(final int value) throws IOException {
110 ensureHeaderWritten();
111 output.writeInt(value);
115 public final void writeLong(final long value) throws IOException {
116 ensureHeaderWritten();
117 output.writeLong(value);
121 public final void writeFloat(final float value) throws IOException {
122 ensureHeaderWritten();
123 output.writeFloat(value);
127 public final void writeDouble(final double value) throws IOException {
128 ensureHeaderWritten();
129 output.writeDouble(value);
133 public final void writeBytes(final String str) throws IOException {
134 ensureHeaderWritten();
135 output.writeBytes(str);
139 public final void writeChars(final String str) throws IOException {
140 ensureHeaderWritten();
141 output.writeChars(str);
145 public final void writeUTF(final String str) throws IOException {
146 ensureHeaderWritten();
147 output.writeUTF(str);
150 private NormalizedNodeWriter normalizedNodeWriter() {
151 if (normalizedNodeWriter == null) {
152 normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(this);
155 return normalizedNodeWriter;
159 public void writeNormalizedNode(final NormalizedNode<?, ?> node) throws IOException {
160 ensureHeaderWritten();
161 normalizedNodeWriter().write(node);
165 public void leafNode(final NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
166 Preconditions.checkNotNull(name, "Node identifier should not be null");
167 LOG.trace("Writing a new leaf node");
168 startNode(name.getNodeType(), NodeTypes.LEAF_NODE);
174 public void startLeafSet(final NodeIdentifier name, final int childSizeHint)
176 throws IOException, IllegalArgumentException {
177 Preconditions.checkNotNull(name, "Node identifier should not be null");
178 LOG.trace("Starting a new leaf set");
180 lastLeafSetQName = name.getNodeType();
181 startNode(name.getNodeType(), NodeTypes.LEAF_SET);
185 public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint)
186 throws IOException, IllegalArgumentException {
187 Preconditions.checkNotNull(name, "Node identifier should not be null");
188 LOG.trace("Starting a new ordered leaf set");
190 lastLeafSetQName = name.getNodeType();
191 startNode(name.getNodeType(), NodeTypes.ORDERED_LEAF_SET);
195 public void leafSetEntryNode(final QName name, final Object value) throws IOException, IllegalArgumentException {
196 LOG.trace("Writing a new leaf set entry node");
198 output.writeByte(NodeTypes.LEAF_SET_ENTRY_NODE);
200 // lastLeafSetQName is set if the parent LeafSetNode was previously written. Otherwise this is a
201 // stand alone LeafSetEntryNode so write out it's name here.
202 if (lastLeafSetQName == null) {
210 public void startContainerNode(final NodeIdentifier name, final int childSizeHint)
211 throws IOException, IllegalArgumentException {
212 Preconditions.checkNotNull(name, "Node identifier should not be null");
214 LOG.trace("Starting a new container node");
216 startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
220 public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint)
221 throws IOException, IllegalArgumentException {
222 Preconditions.checkNotNull(name, "Node identifier should not be null");
224 LOG.trace("Starting a new yang modeled anyXml node");
226 startNode(name.getNodeType(), NodeTypes.YANG_MODELED_ANY_XML_NODE);
230 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint)
231 throws IOException, IllegalArgumentException {
232 Preconditions.checkNotNull(name, "Node identifier should not be null");
233 LOG.trace("Starting a new unkeyed list");
235 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
239 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint)
240 throws IOException, IllegalStateException {
241 Preconditions.checkNotNull(name, "Node identifier should not be null");
242 LOG.trace("Starting a new unkeyed list item");
244 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
248 public void startMapNode(final NodeIdentifier name, final int childSizeHint)
249 throws IOException, IllegalArgumentException {
250 Preconditions.checkNotNull(name, "Node identifier should not be null");
251 LOG.trace("Starting a new map node");
253 startNode(name.getNodeType(), NodeTypes.MAP_NODE);
257 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
258 throws IOException, IllegalArgumentException {
259 Preconditions.checkNotNull(identifier, "Node identifier should not be null");
260 LOG.trace("Starting a new map entry node");
261 startNode(identifier.getNodeType(), NodeTypes.MAP_ENTRY_NODE);
263 writeKeyValueMap(identifier.getKeyValues());
268 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint)
269 throws IOException, IllegalArgumentException {
270 Preconditions.checkNotNull(name, "Node identifier should not be null");
271 LOG.trace("Starting a new ordered map node");
273 startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
277 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint)
278 throws IOException, IllegalArgumentException {
279 Preconditions.checkNotNull(name, "Node identifier should not be null");
280 LOG.trace("Starting a new choice node");
282 startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
286 public void startAugmentationNode(final AugmentationIdentifier identifier)
287 throws IOException, IllegalArgumentException {
288 Preconditions.checkNotNull(identifier, "Node identifier should not be null");
289 LOG.trace("Starting a new augmentation node");
291 output.writeByte(NodeTypes.AUGMENTATION_NODE);
292 writeQNameSet(identifier.getPossibleChildNames());
296 public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
297 Preconditions.checkNotNull(name, "Node identifier should not be null");
298 LOG.trace("Writing any xml node");
300 startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
303 StreamResult xmlOutput = new StreamResult(new StringWriter());
304 TransformerFactory.newInstance().newTransformer().transform((DOMSource)value, xmlOutput);
305 writeObject(xmlOutput.getWriter().toString());
306 } catch (TransformerException | TransformerFactoryConfigurationError e) {
307 throw new IOException("Error writing anyXml", e);
312 public void endNode() throws IOException, IllegalStateException {
313 LOG.trace("Ending the node");
314 lastLeafSetQName = null;
315 output.writeByte(NodeTypes.END_NODE);
319 public void close() throws IOException {
324 public void flush() throws IOException {
325 if (output instanceof OutputStream) {
326 ((OutputStream)output).flush();
330 private void startNode(final QName qname, final byte nodeType) throws IOException {
331 Preconditions.checkNotNull(qname, "QName of node identifier should not be null.");
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 Preconditions.checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)",
347 writeString((String) o);
352 public void writeSchemaPath(final SchemaPath path) throws IOException {
353 ensureHeaderWritten();
354 output.writeBoolean(path.isAbsolute());
356 final Collection<QName> qnames = path.getPath();
357 output.writeInt(qnames.size());
358 for (QName qname : qnames) {
364 public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
365 ensureHeaderWritten();
366 writeYangInstanceIdentifierInternal(identifier);
369 private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
370 Collection<PathArgument> pathArguments = identifier.getPathArguments();
371 output.writeInt(pathArguments.size());
373 for (PathArgument pathArgument : pathArguments) {
374 writePathArgument(pathArgument);
378 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST",
379 justification = "The casts in the switch clauses are indirectly confirmed via the determination of 'type'.")
381 public void writePathArgument(final PathArgument pathArgument) throws IOException {
383 byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
385 output.writeByte(type);
388 case PathArgumentTypes.NODE_IDENTIFIER:
390 NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument;
392 writeQName(nodeIdentifier.getNodeType());
395 case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
397 NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
398 (NodeIdentifierWithPredicates) pathArgument;
399 writeQName(nodeIdentifierWithPredicates.getNodeType());
401 writeKeyValueMap(nodeIdentifierWithPredicates.getKeyValues());
404 case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
406 NodeWithValue<?> nodeWithValue = (NodeWithValue<?>) pathArgument;
408 writeQName(nodeWithValue.getNodeType());
409 writeObject(nodeWithValue.getValue());
412 case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
414 AugmentationIdentifier augmentationIdentifier = (AugmentationIdentifier) pathArgument;
416 // No Qname in augmentation identifier
417 writeQNameSet(augmentationIdentifier.getPossibleChildNames());
420 throw new IllegalStateException("Unknown node identifier type is found : "
421 + pathArgument.getClass().toString());
425 private void writeKeyValueMap(final Map<QName, Object> keyValueMap) throws IOException {
426 if (keyValueMap != null && !keyValueMap.isEmpty()) {
427 output.writeInt(keyValueMap.size());
429 for (Map.Entry<QName, Object> entry : keyValueMap.entrySet()) {
430 writeQName(entry.getKey());
431 writeObject(entry.getValue());
438 private void writeQNameSet(final Set<QName> children) throws IOException {
439 // Write each child's qname separately, if list is empty send count as 0
440 if (children != null && !children.isEmpty()) {
441 output.writeInt(children.size());
442 for (QName qname : children) {
446 LOG.debug("augmentation node does not have any child");
451 private void writeObject(final Object value) throws IOException {
453 byte type = ValueTypes.getSerializableType(value);
454 // Write object type first
455 output.writeByte(type);
458 case ValueTypes.BOOL_TYPE:
459 output.writeBoolean((Boolean) value);
461 case ValueTypes.QNAME_TYPE:
462 writeQName((QName) value);
464 case ValueTypes.INT_TYPE:
465 output.writeInt((Integer) value);
467 case ValueTypes.BYTE_TYPE:
468 output.writeByte((Byte) value);
470 case ValueTypes.LONG_TYPE:
471 output.writeLong((Long) value);
473 case ValueTypes.SHORT_TYPE:
474 output.writeShort((Short) value);
476 case ValueTypes.BITS_TYPE:
477 writeObjSet((Set<?>) value);
479 case ValueTypes.BINARY_TYPE:
480 byte[] bytes = (byte[]) value;
481 output.writeInt(bytes.length);
484 case ValueTypes.YANG_IDENTIFIER_TYPE:
485 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
487 case ValueTypes.NULL_TYPE :
489 case ValueTypes.STRING_BYTES_TYPE:
490 final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
491 output.writeInt(valueBytes.length);
492 output.write(valueBytes);
495 output.writeUTF(value.toString());