Bump yangtools to 3.0.0
[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
9 package org.opendaylight.controller.cluster.datastore.node.utils.stream;
10
11 import com.google.common.base.Preconditions;
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.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
44 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.w3c.dom.Element;
48 import org.xml.sax.InputSource;
49 import org.xml.sax.SAXException;
50
51 /**
52  * NormalizedNodeInputStreamReader reads the byte stream and constructs the normalized node including its children
53  * nodes. This process goes in recursive manner, where each NodeTypes object signifies the start of the object, except
54  * END_NODE. If a node can have children, then that node's end is calculated based on appearance of END_NODE.
55  */
56 public class NormalizedNodeInputStreamReader implements NormalizedNodeDataInput {
57
58     private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeInputStreamReader.class);
59
60     private static final String REVISION_ARG = "?revision=";
61
62     private final DataInput input;
63
64     private final List<String> codedStringMap = new ArrayList<>();
65
66     private QName lastLeafSetQName;
67
68     private NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> leafBuilder;
69
70     @SuppressWarnings("rawtypes")
71     private NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder;
72
73     private final StringBuilder reusableStringBuilder = new StringBuilder(50);
74
75     private boolean readSignatureMarker = true;
76
77     NormalizedNodeInputStreamReader(final DataInput input, final boolean versionChecked) {
78         this.input = Preconditions.checkNotNull(input);
79         readSignatureMarker = !versionChecked;
80     }
81
82     @Override
83     public NormalizedNode<?, ?> readNormalizedNode() throws IOException {
84         readSignatureMarkerAndVersionIfNeeded();
85         return readNormalizedNodeInternal();
86     }
87
88     private void readSignatureMarkerAndVersionIfNeeded() throws IOException {
89         if (readSignatureMarker) {
90             readSignatureMarker = false;
91
92             final byte marker = input.readByte();
93             if (marker != TokenTypes.SIGNATURE_MARKER) {
94                 throw new InvalidNormalizedNodeStreamException(String.format(
95                         "Invalid signature marker: %d", marker));
96             }
97
98             final short version = input.readShort();
99             if (version != TokenTypes.LITHIUM_VERSION) {
100                 throw new InvalidNormalizedNodeStreamException(String.format("Unhandled stream version %s", version));
101             }
102         }
103     }
104
105     private NormalizedNode<?, ?> readNormalizedNodeInternal() throws IOException {
106         // each node should start with a byte
107         byte nodeType = input.readByte();
108
109         if (nodeType == NodeTypes.END_NODE) {
110             LOG.trace("End node reached. return");
111             lastLeafSetQName = null;
112             return null;
113         }
114
115         switch (nodeType) {
116             case NodeTypes.AUGMENTATION_NODE :
117                 YangInstanceIdentifier.AugmentationIdentifier augIdentifier =
118                     new YangInstanceIdentifier.AugmentationIdentifier(readQNameSet());
119
120                 LOG.trace("Reading augmentation node {} ", augIdentifier);
121
122                 return addDataContainerChildren(Builders.augmentationBuilder()
123                         .withNodeIdentifier(augIdentifier)).build();
124
125             case NodeTypes.LEAF_SET_ENTRY_NODE :
126                 QName name = lastLeafSetQName;
127                 if (name == null) {
128                     name = readQName();
129                 }
130
131                 Object value = readObject();
132                 NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(name, value);
133
134                 LOG.trace("Reading leaf set entry node {}, value {}", leafIdentifier, value);
135
136                 return leafSetEntryBuilder().withNodeIdentifier(leafIdentifier).withValue(value).build();
137
138             case NodeTypes.MAP_ENTRY_NODE :
139                 NodeIdentifierWithPredicates entryIdentifier = readNormalizedNodeWithPredicates();
140
141                 LOG.trace("Reading map entry node {} ", entryIdentifier);
142
143                 return addDataContainerChildren(Builders.mapEntryBuilder()
144                         .withNodeIdentifier(entryIdentifier)).build();
145
146             default :
147                 return readNodeIdentifierDependentNode(nodeType, new NodeIdentifier(readQName()));
148         }
149     }
150
151     private NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> leafBuilder() {
152         if (leafBuilder == null) {
153             leafBuilder = Builders.leafBuilder();
154         }
155
156         return leafBuilder;
157     }
158
159     @SuppressWarnings("rawtypes")
160     private NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder() {
161         if (leafSetEntryBuilder == null) {
162             leafSetEntryBuilder = Builders.leafSetEntryBuilder();
163         }
164
165         return leafSetEntryBuilder;
166     }
167
168     private NormalizedNode<?, ?> readNodeIdentifierDependentNode(final byte nodeType, final NodeIdentifier identifier)
169         throws IOException {
170
171         switch (nodeType) {
172             case NodeTypes.LEAF_NODE :
173                 LOG.trace("Read leaf node {}", identifier);
174                 // Read the object value
175                 return leafBuilder().withNodeIdentifier(identifier).withValue(readObject()).build();
176
177             case NodeTypes.ANY_XML_NODE :
178                 LOG.trace("Read xml node");
179                 return Builders.anyXmlBuilder().withNodeIdentifier(identifier).withValue(readDOMSource()).build();
180
181             case NodeTypes.MAP_NODE :
182                 LOG.trace("Read map node {}", identifier);
183                 return addDataContainerChildren(Builders.mapBuilder().withNodeIdentifier(identifier)).build();
184
185             case NodeTypes.CHOICE_NODE:
186                 LOG.trace("Read choice node {}", identifier);
187                 return addDataContainerChildren(Builders.choiceBuilder().withNodeIdentifier(identifier)).build();
188
189             case NodeTypes.ORDERED_MAP_NODE:
190                 LOG.trace("Reading ordered map node {}", identifier);
191                 return addDataContainerChildren(Builders.orderedMapBuilder().withNodeIdentifier(identifier)).build();
192
193             case NodeTypes.UNKEYED_LIST:
194                 LOG.trace("Read unkeyed list node {}", identifier);
195                 return addDataContainerChildren(Builders.unkeyedListBuilder().withNodeIdentifier(identifier)).build();
196
197             case NodeTypes.UNKEYED_LIST_ITEM:
198                 LOG.trace("Read unkeyed list item node {}", identifier);
199                 return addDataContainerChildren(Builders.unkeyedListEntryBuilder()
200                         .withNodeIdentifier(identifier)).build();
201
202             case NodeTypes.CONTAINER_NODE:
203                 LOG.trace("Read container node {}", identifier);
204                 return addDataContainerChildren(Builders.containerBuilder().withNodeIdentifier(identifier)).build();
205
206             case NodeTypes.LEAF_SET :
207                 LOG.trace("Read leaf set node {}", identifier);
208                 return addLeafSetChildren(identifier.getNodeType(),
209                         Builders.leafSetBuilder().withNodeIdentifier(identifier)).build();
210
211             case NodeTypes.ORDERED_LEAF_SET:
212                 LOG.trace("Read ordered leaf set node {}", identifier);
213                 return addLeafSetChildren(identifier.getNodeType(),
214                         Builders.orderedLeafSetBuilder().withNodeIdentifier(identifier)).build();
215
216             default:
217                 return null;
218         }
219     }
220
221     private DOMSource readDOMSource() throws IOException {
222         String xml = readObject().toString();
223         try {
224             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
225             factory.setNamespaceAware(true);
226             Element node = factory.newDocumentBuilder().parse(
227                     new InputSource(new StringReader(xml))).getDocumentElement();
228             return new DOMSource(node);
229         } catch (SAXException | ParserConfigurationException e) {
230             throw new IOException("Error parsing XML: " + xml, e);
231         }
232     }
233
234     private QName readQName() throws IOException {
235         // Read in the same sequence of writing
236         String localName = readCodedString();
237         String namespace = readCodedString();
238         String revision = readCodedString();
239
240         String qname;
241         if (!Strings.isNullOrEmpty(revision)) {
242             qname = reusableStringBuilder.append('(').append(namespace).append(REVISION_ARG).append(revision)
243                     .append(')').append(localName).toString();
244         } else {
245             qname = reusableStringBuilder.append('(').append(namespace).append(')').append(localName).toString();
246         }
247
248         reusableStringBuilder.delete(0, reusableStringBuilder.length());
249         return QNameFactory.create(qname);
250     }
251
252
253     private String readCodedString() throws IOException {
254         final byte valueType = input.readByte();
255         switch (valueType) {
256             case TokenTypes.IS_NULL_VALUE:
257                 return null;
258             case TokenTypes.IS_CODE_VALUE:
259                 final int code = input.readInt();
260                 try {
261                     return codedStringMap.get(code);
262                 } catch (IndexOutOfBoundsException e) {
263                     throw new IOException("String code " + code + " was not found", e);
264                 }
265             case TokenTypes.IS_STRING_VALUE:
266                 final String value = input.readUTF().intern();
267                 codedStringMap.add(value);
268                 return value;
269             default:
270                 throw new IOException("Unhandled string value type " + valueType);
271         }
272     }
273
274     private Set<QName> readQNameSet() throws IOException {
275         // Read the children count
276         int count = input.readInt();
277         Set<QName> children = new HashSet<>(count);
278         for (int i = 0; i < count; i++) {
279             children.add(readQName());
280         }
281         return children;
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
415             case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
416                 return new YangInstanceIdentifier.AugmentationIdentifier(readQNameSet());
417
418             case PathArgumentTypes.NODE_IDENTIFIER :
419                 return new NodeIdentifier(readQName());
420
421             case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES :
422                 return readNormalizedNodeWithPredicates();
423
424             case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
425                 return new NodeWithValue<>(readQName(), readObject());
426
427             default :
428                 return null;
429         }
430     }
431
432     @SuppressWarnings("unchecked")
433     private ListNodeBuilder<Object, LeafSetEntryNode<Object>> addLeafSetChildren(final QName nodeType,
434             final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder) throws IOException {
435
436         LOG.trace("Reading children of leaf set");
437
438         lastLeafSetQName = nodeType;
439
440         LeafSetEntryNode<Object> child = (LeafSetEntryNode<Object>)readNormalizedNodeInternal();
441
442         while (child != null) {
443             builder.withChild(child);
444             child = (LeafSetEntryNode<Object>)readNormalizedNodeInternal();
445         }
446         return builder;
447     }
448
449     @SuppressWarnings({ "unchecked", "rawtypes" })
450     private NormalizedNodeContainerBuilder addDataContainerChildren(
451             final NormalizedNodeContainerBuilder builder) throws IOException {
452         LOG.trace("Reading data container (leaf nodes) nodes");
453
454         NormalizedNode<?, ?> child = readNormalizedNodeInternal();
455
456         while (child != null) {
457             builder.addChild(child);
458             child = readNormalizedNodeInternal();
459         }
460         return builder;
461     }
462
463     @Override
464     public void readFully(final byte[] value) throws IOException {
465         readSignatureMarkerAndVersionIfNeeded();
466         input.readFully(value);
467     }
468
469     @Override
470     public void readFully(final byte[] str, final int off, final int len) throws IOException {
471         readSignatureMarkerAndVersionIfNeeded();
472         input.readFully(str, off, len);
473     }
474
475     @Override
476     public int skipBytes(final int num) throws IOException {
477         readSignatureMarkerAndVersionIfNeeded();
478         return input.skipBytes(num);
479     }
480
481     @Override
482     public boolean readBoolean() throws IOException {
483         readSignatureMarkerAndVersionIfNeeded();
484         return input.readBoolean();
485     }
486
487     @Override
488     public byte readByte() throws IOException {
489         readSignatureMarkerAndVersionIfNeeded();
490         return input.readByte();
491     }
492
493     @Override
494     public int readUnsignedByte() throws IOException {
495         readSignatureMarkerAndVersionIfNeeded();
496         return input.readUnsignedByte();
497     }
498
499     @Override
500     public short readShort() throws IOException {
501         readSignatureMarkerAndVersionIfNeeded();
502         return input.readShort();
503     }
504
505     @Override
506     public int readUnsignedShort() throws IOException {
507         readSignatureMarkerAndVersionIfNeeded();
508         return input.readUnsignedShort();
509     }
510
511     @Override
512     public char readChar() throws IOException {
513         readSignatureMarkerAndVersionIfNeeded();
514         return input.readChar();
515     }
516
517     @Override
518     public int readInt() throws IOException {
519         readSignatureMarkerAndVersionIfNeeded();
520         return input.readInt();
521     }
522
523     @Override
524     public long readLong() throws IOException {
525         readSignatureMarkerAndVersionIfNeeded();
526         return input.readLong();
527     }
528
529     @Override
530     public float readFloat() throws IOException {
531         readSignatureMarkerAndVersionIfNeeded();
532         return input.readFloat();
533     }
534
535     @Override
536     public double readDouble() throws IOException {
537         readSignatureMarkerAndVersionIfNeeded();
538         return input.readDouble();
539     }
540
541     @Override
542     public String readLine() throws IOException {
543         readSignatureMarkerAndVersionIfNeeded();
544         return input.readLine();
545     }
546
547     @Override
548     public String readUTF() throws IOException {
549         readSignatureMarkerAndVersionIfNeeded();
550         return input.readUTF();
551     }
552 }