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();
59 protected abstract void writeQName(QName qname) throws IOException;
60 protected abstract void writeString(String string) throws IOException;
63 public final void write(final int b) throws IOException {
64 ensureHeaderWritten();
69 public final void write(final byte[] b) throws IOException {
70 ensureHeaderWritten();
75 public final void write(final byte[] b, final int off, final int len) throws IOException {
76 ensureHeaderWritten();
77 output.write(b, off, len);
81 public final void writeBoolean(final boolean v) throws IOException {
82 ensureHeaderWritten();
83 output.writeBoolean(v);
87 public final void writeByte(final int v) throws IOException {
88 ensureHeaderWritten();
93 public final void writeShort(final int v) throws IOException {
94 ensureHeaderWritten();
99 public final void writeChar(final int v) throws IOException {
100 ensureHeaderWritten();
105 public final void writeInt(final int v) throws IOException {
106 ensureHeaderWritten();
111 public final void writeLong(final long v) throws IOException {
112 ensureHeaderWritten();
117 public final void writeFloat(final float v) throws IOException {
118 ensureHeaderWritten();
119 output.writeFloat(v);
123 public final void writeDouble(final double v) throws IOException {
124 ensureHeaderWritten();
125 output.writeDouble(v);
129 public final void writeBytes(final String s) throws IOException {
130 ensureHeaderWritten();
131 output.writeBytes(s);
135 public final void writeChars(final String s) throws IOException {
136 ensureHeaderWritten();
137 output.writeChars(s);
141 public final void writeUTF(final String s) throws IOException {
142 ensureHeaderWritten();
146 private NormalizedNodeWriter normalizedNodeWriter() {
147 if(normalizedNodeWriter == null) {
148 normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(this);
151 return normalizedNodeWriter;
155 public void writeNormalizedNode(final NormalizedNode<?, ?> node) throws IOException {
156 ensureHeaderWritten();
157 normalizedNodeWriter().write(node);
161 public void leafNode(final NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
162 Preconditions.checkNotNull(name, "Node identifier should not be null");
163 LOG.debug("Writing a new leaf node");
164 startNode(name.getNodeType(), NodeTypes.LEAF_NODE);
170 public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
171 Preconditions.checkNotNull(name, "Node identifier should not be null");
172 LOG.debug("Starting a new leaf set");
174 lastLeafSetQName = name.getNodeType();
175 startNode(name.getNodeType(), NodeTypes.LEAF_SET);
179 public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
180 Preconditions.checkNotNull(name, "Node identifier should not be null");
181 LOG.debug("Starting a new ordered leaf set");
183 lastLeafSetQName = name.getNodeType();
184 startNode(name.getNodeType(), NodeTypes.ORDERED_LEAF_SET);
188 public void leafSetEntryNode(final QName name, final Object value) throws IOException, IllegalArgumentException {
189 LOG.debug("Writing a new leaf set entry node");
191 output.writeByte(NodeTypes.LEAF_SET_ENTRY_NODE);
193 // lastLeafSetQName is set if the parent LeafSetNode was previously written. Otherwise this is a
194 // stand alone LeafSetEntryNode so write out it's name here.
195 if(lastLeafSetQName == null) {
203 public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
204 Preconditions.checkNotNull(name, "Node identifier should not be null");
206 LOG.debug("Starting a new container node");
208 startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
212 public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
213 Preconditions.checkNotNull(name, "Node identifier should not be null");
215 LOG.debug("Starting a new yang modeled anyXml node");
217 startNode(name.getNodeType(), NodeTypes.YANG_MODELED_ANY_XML_NODE);
221 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
222 Preconditions.checkNotNull(name, "Node identifier should not be null");
223 LOG.debug("Starting a new unkeyed list");
225 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
229 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalStateException {
230 Preconditions.checkNotNull(name, "Node identifier should not be null");
231 LOG.debug("Starting a new unkeyed list item");
233 startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
237 public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
238 Preconditions.checkNotNull(name, "Node identifier should not be null");
239 LOG.debug("Starting a new map node");
241 startNode(name.getNodeType(), NodeTypes.MAP_NODE);
245 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException, IllegalArgumentException {
246 Preconditions.checkNotNull(identifier, "Node identifier should not be null");
247 LOG.debug("Starting a new map entry node");
248 startNode(identifier.getNodeType(), NodeTypes.MAP_ENTRY_NODE);
250 writeKeyValueMap(identifier.getKeyValues());
255 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
256 Preconditions.checkNotNull(name, "Node identifier should not be null");
257 LOG.debug("Starting a new ordered map node");
259 startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
263 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException, IllegalArgumentException {
264 Preconditions.checkNotNull(name, "Node identifier should not be null");
265 LOG.debug("Starting a new choice node");
267 startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
271 public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException, IllegalArgumentException {
272 Preconditions.checkNotNull(identifier, "Node identifier should not be null");
273 LOG.debug("Starting a new augmentation node");
275 output.writeByte(NodeTypes.AUGMENTATION_NODE);
276 writeQNameSet(identifier.getPossibleChildNames());
280 public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
281 Preconditions.checkNotNull(name, "Node identifier should not be null");
282 LOG.debug("Writing any xml node");
284 startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
287 StreamResult xmlOutput = new StreamResult(new StringWriter());
288 TransformerFactory.newInstance().newTransformer().transform((DOMSource)value, xmlOutput);
289 writeObject(xmlOutput.getWriter().toString());
290 } catch (TransformerException | TransformerFactoryConfigurationError e) {
291 throw new IOException("Error writing anyXml", e);
296 public void endNode() throws IOException, IllegalStateException {
297 LOG.debug("Ending the node");
299 output.writeByte(NodeTypes.END_NODE);
303 public void close() throws IOException {
308 public void flush() throws IOException {
309 if (output instanceof OutputStream) {
310 ((OutputStream)output).flush();
314 private void startNode(final QName qName, final byte nodeType) throws IOException {
316 Preconditions.checkNotNull(qName, "QName of node identifier should not be null.");
318 ensureHeaderWritten();
320 // First write the type of node
321 output.writeByte(nodeType);
326 private void writeObjSet(final Set<?> set) throws IOException {
327 output.writeInt(set.size());
328 for (Object o : set) {
329 Preconditions.checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)",
332 writeString((String) o);
337 public void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
338 ensureHeaderWritten();
339 writeYangInstanceIdentifierInternal(identifier);
342 private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
343 Collection<PathArgument> pathArguments = identifier.getPathArguments();
344 output.writeInt(pathArguments.size());
346 for(PathArgument pathArgument : pathArguments) {
347 writePathArgument(pathArgument);
352 public void writePathArgument(final PathArgument pathArgument) throws IOException {
354 byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
356 output.writeByte(type);
359 case PathArgumentTypes.NODE_IDENTIFIER:
361 NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument;
363 writeQName(nodeIdentifier.getNodeType());
366 case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
368 NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
369 (NodeIdentifierWithPredicates) pathArgument;
370 writeQName(nodeIdentifierWithPredicates.getNodeType());
372 writeKeyValueMap(nodeIdentifierWithPredicates.getKeyValues());
375 case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
377 NodeWithValue<?> nodeWithValue = (NodeWithValue<?>) pathArgument;
379 writeQName(nodeWithValue.getNodeType());
380 writeObject(nodeWithValue.getValue());
383 case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
385 AugmentationIdentifier augmentationIdentifier = (AugmentationIdentifier) pathArgument;
387 // No Qname in augmentation identifier
388 writeQNameSet(augmentationIdentifier.getPossibleChildNames());
391 throw new IllegalStateException("Unknown node identifier type is found : " + pathArgument.getClass().toString() );
395 private void writeKeyValueMap(final Map<QName, Object> keyValueMap) throws IOException {
396 if (keyValueMap != null && !keyValueMap.isEmpty()) {
397 output.writeInt(keyValueMap.size());
399 for (QName qName : keyValueMap.keySet()) {
401 writeObject(keyValueMap.get(qName));
408 private void writeQNameSet(final Set<QName> children) throws IOException {
409 // Write each child's qname separately, if list is empty send count as 0
410 if (children != null && !children.isEmpty()) {
411 output.writeInt(children.size());
412 for (QName qName : children) {
416 LOG.debug("augmentation node does not have any child");
421 private void writeObject(final Object value) throws IOException {
423 byte type = ValueTypes.getSerializableType(value);
424 // Write object type first
425 output.writeByte(type);
428 case ValueTypes.BOOL_TYPE:
429 output.writeBoolean((Boolean) value);
431 case ValueTypes.QNAME_TYPE:
432 writeQName((QName) value);
434 case ValueTypes.INT_TYPE:
435 output.writeInt((Integer) value);
437 case ValueTypes.BYTE_TYPE:
438 output.writeByte((Byte) value);
440 case ValueTypes.LONG_TYPE:
441 output.writeLong((Long) value);
443 case ValueTypes.SHORT_TYPE:
444 output.writeShort((Short) value);
446 case ValueTypes.BITS_TYPE:
447 writeObjSet((Set<?>) value);
449 case ValueTypes.BINARY_TYPE:
450 byte[] bytes = (byte[]) value;
451 output.writeInt(bytes.length);
454 case ValueTypes.YANG_IDENTIFIER_TYPE:
455 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
457 case ValueTypes.NULL_TYPE :
459 case ValueTypes.STRING_BYTES_TYPE:
460 final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
461 output.writeInt(valueBytes.length);
462 output.write(valueBytes);
465 output.writeUTF(value.toString());