4e7216df88eac82e5ea4b4d875c29fc9f4b66a14
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / stream / NormalizedNodeInputStreamReader.java
1 /*
2  * Copyright (c) 2014, 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 java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Strings;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableList.Builder;
15 import java.io.DataInput;
16 import java.io.IOException;
17 import java.io.StringReader;
18 import java.math.BigDecimal;
19 import java.math.BigInteger;
20 import java.nio.charset.StandardCharsets;
21 import java.util.ArrayList;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Set;
25 import javax.xml.parsers.DocumentBuilderFactory;
26 import javax.xml.parsers.ParserConfigurationException;
27 import javax.xml.transform.dom.DOMSource;
28 import org.opendaylight.controller.cluster.datastore.node.utils.QNameFactory;
29 import org.opendaylight.yangtools.util.ImmutableOffsetMapTemplate;
30 import org.opendaylight.yangtools.yang.common.Empty;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
38 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
41 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
45 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.w3c.dom.Element;
49 import org.xml.sax.InputSource;
50 import org.xml.sax.SAXException;
51
52 /**
53  * NormalizedNodeInputStreamReader reads the byte stream and constructs the normalized node including its children
54  * nodes. This process goes in recursive manner, where each NodeTypes object signifies the start of the object, except
55  * END_NODE. If a node can have children, then that node's end is calculated based on appearance of END_NODE.
56  */
57 public class NormalizedNodeInputStreamReader implements NormalizedNodeDataInput {
58
59     private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeInputStreamReader.class);
60
61     private static final String REVISION_ARG = "?revision=";
62
63     private final DataInput input;
64
65     private final List<String> codedStringMap = new ArrayList<>();
66
67     private QName lastLeafSetQName;
68
69     private NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder;
70
71     @SuppressWarnings("rawtypes")
72     private NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder;
73
74     private final StringBuilder reusableStringBuilder = new StringBuilder(50);
75
76     private boolean readSignatureMarker = true;
77
78     NormalizedNodeInputStreamReader(final DataInput input, final boolean versionChecked) {
79         this.input = requireNonNull(input);
80         readSignatureMarker = !versionChecked;
81     }
82
83     @Override
84     public NormalizedNode<?, ?> readNormalizedNode() throws IOException {
85         readSignatureMarkerAndVersionIfNeeded();
86         return readNormalizedNodeInternal();
87     }
88
89     private void readSignatureMarkerAndVersionIfNeeded() throws IOException {
90         if (readSignatureMarker) {
91             readSignatureMarker = false;
92
93             final byte marker = input.readByte();
94             if (marker != TokenTypes.SIGNATURE_MARKER) {
95                 throw new InvalidNormalizedNodeStreamException(String.format(
96                         "Invalid signature marker: %d", marker));
97             }
98
99             final short version = input.readShort();
100             if (version != TokenTypes.LITHIUM_VERSION) {
101                 throw new InvalidNormalizedNodeStreamException(String.format("Unhandled stream version %s", version));
102             }
103         }
104     }
105
106     private NormalizedNode<?, ?> readNormalizedNodeInternal() throws IOException {
107         // each node should start with a byte
108         byte nodeType = input.readByte();
109
110         if (nodeType == NodeTypes.END_NODE) {
111             LOG.trace("End node reached. return");
112             lastLeafSetQName = null;
113             return null;
114         }
115
116         switch (nodeType) {
117             case NodeTypes.AUGMENTATION_NODE:
118                 AugmentationIdentifier augIdentifier = readAugmentationIdentifier();
119                 LOG.trace("Reading augmentation node {} ", augIdentifier);
120                 return addDataContainerChildren(Builders.augmentationBuilder().withNodeIdentifier(augIdentifier))
121                         .build();
122
123             case NodeTypes.LEAF_SET_ENTRY_NODE:
124                 final QName name = lastLeafSetQName != null ? lastLeafSetQName : readQName();
125                 final Object value = readObject();
126                 final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(name, value);
127                 LOG.trace("Reading leaf set entry node {}, value {}", leafIdentifier, value);
128                 return leafSetEntryBuilder().withNodeIdentifier(leafIdentifier).withValue(value).build();
129
130             case NodeTypes.MAP_ENTRY_NODE:
131                 final NodeIdentifierWithPredicates entryIdentifier = readNormalizedNodeWithPredicates();
132                 LOG.trace("Reading map entry node {} ", entryIdentifier);
133                 return addDataContainerChildren(Builders.mapEntryBuilder().withNodeIdentifier(entryIdentifier))
134                         .build();
135
136             default:
137                 return readNodeIdentifierDependentNode(nodeType, readNodeIdentifier());
138         }
139     }
140
141     private NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder() {
142         if (leafBuilder == null) {
143             leafBuilder = Builders.leafBuilder();
144         }
145
146         return leafBuilder;
147     }
148
149     @SuppressWarnings("rawtypes")
150     private NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder() {
151         if (leafSetEntryBuilder == null) {
152             leafSetEntryBuilder = Builders.leafSetEntryBuilder();
153         }
154
155         return leafSetEntryBuilder;
156     }
157
158     private NormalizedNode<?, ?> readNodeIdentifierDependentNode(final byte nodeType, final NodeIdentifier identifier)
159         throws IOException {
160
161         switch (nodeType) {
162             case NodeTypes.LEAF_NODE:
163                 LOG.trace("Read leaf node {}", identifier);
164                 // Read the object value
165                 return leafBuilder().withNodeIdentifier(identifier).withValue(readObject()).build();
166
167             case NodeTypes.ANY_XML_NODE:
168                 LOG.trace("Read xml node");
169                 return Builders.anyXmlBuilder().withNodeIdentifier(identifier).withValue(readDOMSource()).build();
170
171             case NodeTypes.MAP_NODE:
172                 LOG.trace("Read map node {}", identifier);
173                 return addDataContainerChildren(Builders.mapBuilder().withNodeIdentifier(identifier)).build();
174
175             case NodeTypes.CHOICE_NODE:
176                 LOG.trace("Read choice node {}", identifier);
177                 return addDataContainerChildren(Builders.choiceBuilder().withNodeIdentifier(identifier)).build();
178
179             case NodeTypes.ORDERED_MAP_NODE:
180                 LOG.trace("Reading ordered map node {}", identifier);
181                 return addDataContainerChildren(Builders.orderedMapBuilder().withNodeIdentifier(identifier)).build();
182
183             case NodeTypes.UNKEYED_LIST:
184                 LOG.trace("Read unkeyed list node {}", identifier);
185                 return addDataContainerChildren(Builders.unkeyedListBuilder().withNodeIdentifier(identifier)).build();
186
187             case NodeTypes.UNKEYED_LIST_ITEM:
188                 LOG.trace("Read unkeyed list item node {}", identifier);
189                 return addDataContainerChildren(Builders.unkeyedListEntryBuilder()
190                         .withNodeIdentifier(identifier)).build();
191
192             case NodeTypes.CONTAINER_NODE:
193                 LOG.trace("Read container node {}", identifier);
194                 return addDataContainerChildren(Builders.containerBuilder().withNodeIdentifier(identifier)).build();
195
196             case NodeTypes.LEAF_SET:
197                 LOG.trace("Read leaf set node {}", identifier);
198                 return addLeafSetChildren(identifier.getNodeType(),
199                         Builders.leafSetBuilder().withNodeIdentifier(identifier)).build();
200
201             case NodeTypes.ORDERED_LEAF_SET:
202                 LOG.trace("Read ordered leaf set node {}", identifier);
203                 return addLeafSetChildren(identifier.getNodeType(),
204                         Builders.orderedLeafSetBuilder().withNodeIdentifier(identifier)).build();
205
206             default:
207                 return null;
208         }
209     }
210
211     private DOMSource readDOMSource() throws IOException {
212         String xml = readObject().toString();
213         try {
214             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
215             factory.setNamespaceAware(true);
216             Element node = factory.newDocumentBuilder().parse(
217                     new InputSource(new StringReader(xml))).getDocumentElement();
218             return new DOMSource(node);
219         } catch (SAXException | ParserConfigurationException e) {
220             throw new IOException("Error parsing XML: " + xml, e);
221         }
222     }
223
224     private QName readQName() throws IOException {
225         // Read in the same sequence of writing
226         String localName = readCodedString();
227         String namespace = readCodedString();
228         String revision = readCodedString();
229
230         String qname;
231         if (!Strings.isNullOrEmpty(revision)) {
232             qname = reusableStringBuilder.append('(').append(namespace).append(REVISION_ARG).append(revision)
233                     .append(')').append(localName).toString();
234         } else {
235             qname = reusableStringBuilder.append('(').append(namespace).append(')').append(localName).toString();
236         }
237
238         reusableStringBuilder.delete(0, reusableStringBuilder.length());
239         return QNameFactory.create(qname);
240     }
241
242
243     private String readCodedString() throws IOException {
244         final byte valueType = input.readByte();
245         switch (valueType) {
246             case TokenTypes.IS_NULL_VALUE:
247                 return null;
248             case TokenTypes.IS_CODE_VALUE:
249                 final int code = input.readInt();
250                 try {
251                     return codedStringMap.get(code);
252                 } catch (IndexOutOfBoundsException e) {
253                     throw new IOException("String code " + code + " was not found", e);
254                 }
255             case TokenTypes.IS_STRING_VALUE:
256                 final String value = input.readUTF().intern();
257                 codedStringMap.add(value);
258                 return value;
259             default:
260                 throw new IOException("Unhandled string value type " + valueType);
261         }
262     }
263
264     private Set<QName> readQNameSet() throws IOException {
265         // Read the children count
266         int count = input.readInt();
267         Set<QName> children = new HashSet<>(count);
268         for (int i = 0; i < count; i++) {
269             children.add(readQName());
270         }
271         return children;
272     }
273
274     private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
275         // FIXME: we should have a cache for these, too
276         return new AugmentationIdentifier(readQNameSet());
277     }
278
279     private NodeIdentifier readNodeIdentifier() throws IOException {
280         // FIXME: we should have a cache for these, too
281         return new NodeIdentifier(readQName());
282     }
283
284     private NodeIdentifierWithPredicates readNormalizedNodeWithPredicates() throws IOException {
285         final QName qname = readQName();
286         final int count = input.readInt();
287         switch (count) {
288             case 0:
289                 return new NodeIdentifierWithPredicates(qname);
290             case 1:
291                 return new NodeIdentifierWithPredicates(qname, readQName(), readObject());
292             default:
293                 // ImmutableList is used by ImmutableOffsetMapTemplate for lookups, hence we use that.
294                 final Builder<QName> keys = ImmutableList.builderWithExpectedSize(count);
295                 final Object[] values = new Object[count];
296                 for (int i = 0; i < count; i++) {
297                     keys.add(readQName());
298                     values[i] = readObject();
299                 }
300
301                 return new NodeIdentifierWithPredicates(qname, ImmutableOffsetMapTemplate.ordered(keys.build())
302                     .instantiateWithValues(values));
303         }
304     }
305
306     private Object readObject() throws IOException {
307         byte objectType = input.readByte();
308         switch (objectType) {
309             case ValueTypes.BITS_TYPE:
310                 return readObjSet();
311
312             case ValueTypes.BOOL_TYPE:
313                 return input.readBoolean();
314
315             case ValueTypes.BYTE_TYPE:
316                 return input.readByte();
317
318             case ValueTypes.INT_TYPE:
319                 return input.readInt();
320
321             case ValueTypes.LONG_TYPE:
322                 return input.readLong();
323
324             case ValueTypes.QNAME_TYPE:
325                 return readQName();
326
327             case ValueTypes.SHORT_TYPE:
328                 return input.readShort();
329
330             case ValueTypes.STRING_TYPE:
331                 return input.readUTF();
332
333             case ValueTypes.STRING_BYTES_TYPE:
334                 return readStringBytes();
335
336             case ValueTypes.BIG_DECIMAL_TYPE:
337                 return new BigDecimal(input.readUTF());
338
339             case ValueTypes.BIG_INTEGER_TYPE:
340                 return new BigInteger(input.readUTF());
341
342             case ValueTypes.BINARY_TYPE:
343                 byte[] bytes = new byte[input.readInt()];
344                 input.readFully(bytes);
345                 return bytes;
346
347             case ValueTypes.YANG_IDENTIFIER_TYPE:
348                 return readYangInstanceIdentifierInternal();
349
350             case ValueTypes.EMPTY_TYPE:
351             // Leaf nodes no longer allow null values and thus we no longer emit null values. Previously, the "empty"
352             // yang type was represented as null so we translate an incoming null value to Empty. It was possible for
353             // a BI user to set a string leaf to null and we're rolling the dice here but the chances for that are
354             // very low. We'd have to know the yang type but, even if we did, we can't let a null value pass upstream
355             // so we'd have to drop the leaf which might cause other issues.
356             case ValueTypes.NULL_TYPE:
357                 return Empty.getInstance();
358
359             default:
360                 return null;
361         }
362     }
363
364     private String readStringBytes() throws IOException {
365         byte[] bytes = new byte[input.readInt()];
366         input.readFully(bytes);
367         return new String(bytes, StandardCharsets.UTF_8);
368     }
369
370     @Override
371     public SchemaPath readSchemaPath() throws IOException {
372         readSignatureMarkerAndVersionIfNeeded();
373
374         final boolean absolute = input.readBoolean();
375         final int size = input.readInt();
376
377         final Builder<QName> qnames = ImmutableList.builderWithExpectedSize(size);
378         for (int i = 0; i < size; ++i) {
379             qnames.add(readQName());
380         }
381         return SchemaPath.create(qnames.build(), absolute);
382     }
383
384     @Override
385     public YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
386         readSignatureMarkerAndVersionIfNeeded();
387         return readYangInstanceIdentifierInternal();
388     }
389
390     private YangInstanceIdentifier readYangInstanceIdentifierInternal() throws IOException {
391         int size = input.readInt();
392         final Builder<PathArgument> pathArguments = ImmutableList.builderWithExpectedSize(size);
393         for (int i = 0; i < size; i++) {
394             pathArguments.add(readPathArgument());
395         }
396         return YangInstanceIdentifier.create(pathArguments.build());
397     }
398
399     private Set<String> readObjSet() throws IOException {
400         int count = input.readInt();
401         Set<String> children = new HashSet<>(count);
402         for (int i = 0; i < count; i++) {
403             children.add(readCodedString());
404         }
405         return children;
406     }
407
408     @Override
409     public PathArgument readPathArgument() throws IOException {
410         // read Type
411         int type = input.readByte();
412
413         switch (type) {
414             case PathArgumentTypes.AUGMENTATION_IDENTIFIER:
415                 return readAugmentationIdentifier();
416             case PathArgumentTypes.NODE_IDENTIFIER:
417                 return readNodeIdentifier();
418             case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
419                 return readNormalizedNodeWithPredicates();
420             case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE:
421                 return new NodeWithValue<>(readQName(), readObject());
422             default:
423                 // FIXME: throw hard error
424                 return null;
425         }
426     }
427
428     @SuppressWarnings("unchecked")
429     private ListNodeBuilder<Object, LeafSetEntryNode<Object>> addLeafSetChildren(final QName nodeType,
430             final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder) throws IOException {
431
432         LOG.trace("Reading children of leaf set");
433
434         lastLeafSetQName = nodeType;
435
436         LeafSetEntryNode<Object> child = (LeafSetEntryNode<Object>)readNormalizedNodeInternal();
437
438         while (child != null) {
439             builder.withChild(child);
440             child = (LeafSetEntryNode<Object>)readNormalizedNodeInternal();
441         }
442         return builder;
443     }
444
445     @SuppressWarnings({ "unchecked", "rawtypes" })
446     private NormalizedNodeContainerBuilder addDataContainerChildren(
447             final NormalizedNodeContainerBuilder builder) throws IOException {
448         LOG.trace("Reading data container (leaf nodes) nodes");
449
450         NormalizedNode<?, ?> child = readNormalizedNodeInternal();
451
452         while (child != null) {
453             builder.addChild(child);
454             child = readNormalizedNodeInternal();
455         }
456         return builder;
457     }
458
459     @Override
460     public void readFully(final byte[] value) throws IOException {
461         readSignatureMarkerAndVersionIfNeeded();
462         input.readFully(value);
463     }
464
465     @Override
466     public void readFully(final byte[] str, final int off, final int len) throws IOException {
467         readSignatureMarkerAndVersionIfNeeded();
468         input.readFully(str, off, len);
469     }
470
471     @Override
472     public int skipBytes(final int num) throws IOException {
473         readSignatureMarkerAndVersionIfNeeded();
474         return input.skipBytes(num);
475     }
476
477     @Override
478     public boolean readBoolean() throws IOException {
479         readSignatureMarkerAndVersionIfNeeded();
480         return input.readBoolean();
481     }
482
483     @Override
484     public byte readByte() throws IOException {
485         readSignatureMarkerAndVersionIfNeeded();
486         return input.readByte();
487     }
488
489     @Override
490     public int readUnsignedByte() throws IOException {
491         readSignatureMarkerAndVersionIfNeeded();
492         return input.readUnsignedByte();
493     }
494
495     @Override
496     public short readShort() throws IOException {
497         readSignatureMarkerAndVersionIfNeeded();
498         return input.readShort();
499     }
500
501     @Override
502     public int readUnsignedShort() throws IOException {
503         readSignatureMarkerAndVersionIfNeeded();
504         return input.readUnsignedShort();
505     }
506
507     @Override
508     public char readChar() throws IOException {
509         readSignatureMarkerAndVersionIfNeeded();
510         return input.readChar();
511     }
512
513     @Override
514     public int readInt() throws IOException {
515         readSignatureMarkerAndVersionIfNeeded();
516         return input.readInt();
517     }
518
519     @Override
520     public long readLong() throws IOException {
521         readSignatureMarkerAndVersionIfNeeded();
522         return input.readLong();
523     }
524
525     @Override
526     public float readFloat() throws IOException {
527         readSignatureMarkerAndVersionIfNeeded();
528         return input.readFloat();
529     }
530
531     @Override
532     public double readDouble() throws IOException {
533         readSignatureMarkerAndVersionIfNeeded();
534         return input.readDouble();
535     }
536
537     @Override
538     public String readLine() throws IOException {
539         readSignatureMarkerAndVersionIfNeeded();
540         return input.readLine();
541     }
542
543     @Override
544     public String readUTF() throws IOException {
545         readSignatureMarkerAndVersionIfNeeded();
546         return input.readUTF();
547     }
548 }