2 * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.ImmutableList.Builder;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableSet;
18 import com.google.common.util.concurrent.UncheckedExecutionException;
19 import java.io.DataInput;
20 import java.io.IOException;
21 import java.io.StringReader;
22 import java.math.BigDecimal;
23 import java.math.BigInteger;
24 import java.nio.charset.StandardCharsets;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.concurrent.ExecutionException;
28 import javax.xml.transform.dom.DOMSource;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.opendaylight.controller.cluster.datastore.node.utils.QNameFactory;
31 import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
32 import org.opendaylight.yangtools.util.xml.UntrustedXML;
33 import org.opendaylight.yangtools.yang.common.Empty;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.common.QNameModule;
36 import org.opendaylight.yangtools.yang.common.Uint16;
37 import org.opendaylight.yangtools.yang.common.Uint32;
38 import org.opendaylight.yangtools.yang.common.Uint64;
39 import org.opendaylight.yangtools.yang.common.Uint8;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
46 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.xml.sax.InputSource;
50 import org.xml.sax.SAXException;
53 * Abstract base class for NormalizedNodeDataInput based on {@link MagnesiumNode}, {@link MagnesiumPathArgument} and
54 * {@link MagnesiumValue}.
56 abstract class AbstractMagnesiumDataInput extends AbstractNormalizedNodeDataInput {
57 private static final Logger LOG = LoggerFactory.getLogger(AbstractMagnesiumDataInput.class);
59 // Known singleton objects
60 private static final @NonNull Byte INT8_0 = 0;
61 private static final @NonNull Short INT16_0 = 0;
62 private static final @NonNull Integer INT32_0 = 0;
63 private static final @NonNull Long INT64_0 = 0L;
64 private static final byte @NonNull[] BINARY_0 = new byte[0];
65 private static final @NonNull AugmentationIdentifier EMPTY_AID = AugmentationIdentifier.create(ImmutableSet.of());
67 // FIXME: these should be available as constants
68 private static final @NonNull Uint8 UINT8_0 = Uint8.valueOf(0);
69 private static final @NonNull Uint16 UINT16_0 = Uint16.valueOf(0);
70 private static final @NonNull Uint32 UINT32_0 = Uint32.valueOf(0);
71 private static final @NonNull Uint64 UINT64_0 = Uint64.valueOf(0);
73 private final List<AugmentationIdentifier> codedAugments = new ArrayList<>();
74 private final List<NodeIdentifier> codedNodeIdentifiers = new ArrayList<>();
75 private final List<QNameModule> codedModules = new ArrayList<>();
76 private final List<String> codedStrings = new ArrayList<>();
78 AbstractMagnesiumDataInput(final DataInput input) {
83 public final void streamNormalizedNode(final NormalizedNodeStreamWriter writer) throws IOException {
84 streamNormalizedNode(requireNonNull(writer), null, input.readByte());
87 private void streamNormalizedNode(final NormalizedNodeStreamWriter writer, final PathArgument parent,
88 final byte nodeHeader) throws IOException {
89 switch (nodeHeader & MagnesiumNode.TYPE_MASK) {
90 case MagnesiumNode.NODE_LEAF:
91 streamLeaf(writer, parent, nodeHeader);
93 case MagnesiumNode.NODE_CONTAINER:
94 streamContainer(writer, nodeHeader);
96 case MagnesiumNode.NODE_LIST:
97 streamList(writer, nodeHeader);
99 case MagnesiumNode.NODE_MAP:
100 streamMap(writer, nodeHeader);
102 case MagnesiumNode.NODE_MAP_ORDERED:
103 streamMapOrdered(writer, nodeHeader);
105 case MagnesiumNode.NODE_LEAFSET:
106 streamLeafset(writer, nodeHeader);
108 case MagnesiumNode.NODE_LEAFSET_ORDERED:
109 streamLeafsetOrdered(writer, nodeHeader);
111 case MagnesiumNode.NODE_CHOICE:
112 streamChoice(writer, nodeHeader);
114 case MagnesiumNode.NODE_AUGMENTATION:
115 streamAugmentation(writer, nodeHeader);
117 case MagnesiumNode.NODE_ANYXML:
118 streamAnyxml(writer, nodeHeader);
120 case MagnesiumNode.NODE_ANYXML_MODELED:
121 streamAnyxmlModeled(writer, nodeHeader);
123 case MagnesiumNode.NODE_LIST_ENTRY:
124 streamListEntry(writer, parent, nodeHeader);
126 case MagnesiumNode.NODE_LEAFSET_ENTRY:
127 streamLeafsetEntry(writer, parent, nodeHeader);
129 case MagnesiumNode.NODE_MAP_ENTRY:
130 streamMapEntry(writer, parent, nodeHeader);
133 throw new InvalidNormalizedNodeStreamException("Unexpected node header " + nodeHeader);
137 private void streamAnyxml(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
138 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
139 LOG.trace("Streaming anyxml node {}", identifier);
141 final DOMSource value = readDOMSource();
142 if (writer.startAnyxmlNode(identifier, DOMSource.class)) {
143 writer.domSourceValue(value);
148 private void streamAnyxmlModeled(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
150 // TODO: decide how to deal with these
151 throw new UnsupportedOperationException("Reading YANG-modeled anyxml was never supported");
154 private void streamAugmentation(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
155 final AugmentationIdentifier augIdentifier = decodeAugmentationIdentifier(nodeHeader);
156 LOG.trace("Streaming augmentation node {}", augIdentifier);
157 writer.startAugmentationNode(augIdentifier);
158 commonStreamContainer(writer, augIdentifier);
161 private void streamChoice(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
162 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
163 LOG.trace("Streaming choice node {}", identifier);
164 writer.startChoiceNode(identifier, UNKNOWN_SIZE);
165 commonStreamContainer(writer, identifier);
168 private void streamContainer(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
169 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
170 LOG.trace("Streaming container node {}", identifier);
171 writer.startContainerNode(identifier, UNKNOWN_SIZE);
172 commonStreamContainer(writer, identifier);
175 private void streamLeaf(final NormalizedNodeStreamWriter writer, final PathArgument parent, final byte nodeHeader)
177 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
178 LOG.trace("Streaming leaf node {}", identifier);
179 writer.startLeafNode(identifier);
182 if ((nodeHeader & MagnesiumNode.PREDICATE_ONE) == MagnesiumNode.PREDICATE_ONE) {
183 if (!(parent instanceof NodeIdentifierWithPredicates)) {
184 throw new InvalidNormalizedNodeStreamException("Invalid predicate leaf " + identifier + " in parent "
188 value = ((NodeIdentifierWithPredicates) parent).getValue(identifier.getNodeType());
190 throw new InvalidNormalizedNodeStreamException("Failed to find predicate leaf " + identifier
191 + " in parent " + parent);
194 value = readLeafValue();
197 writer.scalarValue(value);
201 private void streamLeafset(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
202 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
203 LOG.trace("Streaming leaf set node {}", identifier);
204 writer.startLeafSet(identifier, UNKNOWN_SIZE);
205 commonStreamContainer(writer, identifier);
208 private void streamLeafsetOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
210 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
211 LOG.trace("Streaming ordered leaf set node {}", identifier);
212 writer.startOrderedLeafSet(identifier, UNKNOWN_SIZE);
214 commonStreamContainer(writer, identifier);
217 private void streamLeafsetEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
218 final byte nodeHeader) throws IOException {
219 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
220 final Object value = readLeafValue();
221 final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(nodeId.getNodeType(), value);
222 LOG.trace("Streaming leaf set entry node {}", leafIdentifier);
223 writer.startLeafSetEntryNode(leafIdentifier);
224 writer.scalarValue(value);
228 private void streamList(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
229 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
230 writer.startUnkeyedList(identifier, UNKNOWN_SIZE);
231 commonStreamContainer(writer, identifier);
234 private void streamListEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
235 final byte nodeHeader) throws IOException {
236 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader, parent);
237 LOG.trace("Streaming unkeyed list item node {}", identifier);
238 writer.startUnkeyedListItem(identifier, UNKNOWN_SIZE);
239 commonStreamContainer(writer, identifier);
242 private void streamMap(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
243 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
244 LOG.trace("Streaming map node {}", identifier);
245 writer.startMapNode(identifier, UNKNOWN_SIZE);
246 commonStreamContainer(writer, identifier);
249 private void streamMapOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
250 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
251 LOG.trace("Streaming ordered map node {}", identifier);
252 writer.startOrderedMapNode(identifier, UNKNOWN_SIZE);
253 commonStreamContainer(writer, identifier);
256 private void streamMapEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
257 final byte nodeHeader) throws IOException {
258 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
261 switch (mask(nodeHeader, MagnesiumNode.PREDICATE_MASK)) {
262 case MagnesiumNode.PREDICATE_ZERO:
265 case MagnesiumNode.PREDICATE_ONE:
268 case MagnesiumNode.PREDICATE_1B:
269 size = input.readUnsignedByte();
271 case MagnesiumNode.PREDICATE_4B:
272 size = input.readInt();
275 // ISE on purpose: this should never ever happen
276 throw new IllegalStateException("Failed to decode NodeIdentifierWithPredicates size from header "
280 final NodeIdentifierWithPredicates identifier = readNodeIdentifierWithPredicates(nodeId.getNodeType(), size);
281 LOG.trace("Streaming map entry node {}", identifier);
282 writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
283 commonStreamContainer(writer, identifier);
286 private void commonStreamContainer(final NormalizedNodeStreamWriter writer, final PathArgument parent)
288 for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
289 streamNormalizedNode(writer, parent, nodeType);
294 private @NonNull NodeIdentifier decodeNodeIdentifier() throws IOException {
295 final QNameModule module = decodeQNameModule();
296 final String localName = readRefString();
297 final NodeIdentifier nodeId;
299 nodeId = QNameFactory.getNodeIdentifier(module, localName);
300 } catch (ExecutionException e) {
301 throw new InvalidNormalizedNodeStreamException("Illegal QName module=" + module + " localName="
305 codedNodeIdentifiers.add(nodeId);
309 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader) throws IOException {
310 return decodeNodeIdentifier(nodeHeader, null);
313 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader, final PathArgument parent) throws IOException {
315 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
316 case MagnesiumNode.ADDR_DEFINE:
317 return readNodeIdentifier();
318 case MagnesiumNode.ADDR_LOOKUP_1B:
319 index = input.readUnsignedByte();
321 case MagnesiumNode.ADDR_LOOKUP_4B:
322 index = input.readInt();
324 case MagnesiumNode.ADDR_PARENT:
325 if (parent instanceof NodeIdentifier) {
326 return (NodeIdentifier) parent;
328 throw new InvalidNormalizedNodeStreamException("Invalid node identifier reference to parent " + parent);
330 throw new InvalidNormalizedNodeStreamException("Unexpected node identifier addressing in header "
335 return codedNodeIdentifiers.get(index);
336 } catch (IndexOutOfBoundsException e) {
337 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
341 private AugmentationIdentifier decodeAugmentationIdentifier(final byte nodeHeader) throws IOException {
343 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
344 case MagnesiumNode.ADDR_DEFINE:
345 return readAugmentationIdentifier();
346 case MagnesiumNode.ADDR_LOOKUP_1B:
347 index = input.readUnsignedByte();
349 case MagnesiumNode.ADDR_LOOKUP_4B:
350 index = input.readInt();
353 throw new InvalidNormalizedNodeStreamException(
354 "Unexpected augmentation identifier addressing in header " + nodeHeader);
358 return codedAugments.get(index);
359 } catch (IndexOutOfBoundsException e) {
360 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier reference " + index, e);
365 public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
366 final byte type = input.readByte();
367 if (type == MagnesiumValue.YIID) {
368 return readYangInstanceIdentifier(input.readInt());
369 } else if (type >= MagnesiumValue.YIID_0) {
370 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
371 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
373 throw new InvalidNormalizedNodeStreamException("Unexpected YangInstanceIdentifier type " + type);
377 private @NonNull YangInstanceIdentifier readYangInstanceIdentifier(final int size) throws IOException {
379 final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
380 for (int i = 0; i < size; ++i) {
381 builder.add(readPathArgument());
383 return YangInstanceIdentifier.create(builder.build());
384 } else if (size == 0) {
385 return YangInstanceIdentifier.empty();
387 throw new InvalidNormalizedNodeStreamException("Invalid YangInstanceIdentifier size " + size);
392 public final QName readQName() throws IOException {
393 final byte type = input.readByte();
395 case MagnesiumValue.QNAME:
396 return decodeQName();
397 case MagnesiumValue.QNAME_REF_1B:
398 return decodeQNameRef1();
399 case MagnesiumValue.QNAME_REF_2B:
400 return decodeQNameRef2();
401 case MagnesiumValue.QNAME_REF_4B:
402 return decodeQNameRef4();
404 throw new InvalidNormalizedNodeStreamException("Unexpected QName type " + type);
409 public final PathArgument readPathArgument() throws IOException {
410 final byte header = input.readByte();
411 switch (header & MagnesiumPathArgument.TYPE_MASK) {
412 case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER:
413 return readAugmentationIdentifier(header);
414 case MagnesiumPathArgument.NODE_IDENTIFIER:
415 verifyPathIdentifierOnly(header);
416 return readNodeIdentifier(header);
417 case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES:
418 return readNodeIdentifierWithPredicates(header);
419 case MagnesiumPathArgument.NODE_WITH_VALUE:
420 verifyPathIdentifierOnly(header);
421 return readNodeWithValue(header);
422 case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER:
423 verifyPathIdentifierOnly(header);
424 return MountPointIdentifier.create(readNodeIdentifier(header).getNodeType());
426 throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
430 private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
431 final AugmentationIdentifier result = readAugmentationIdentifier(input.readInt());
432 codedAugments.add(result);
436 private AugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
437 final byte count = mask(header, MagnesiumPathArgument.AID_COUNT_MASK);
439 case MagnesiumPathArgument.AID_COUNT_1B:
440 return readAugmentationIdentifier(input.readUnsignedByte());
441 case MagnesiumPathArgument.AID_COUNT_2B:
442 return readAugmentationIdentifier(input.readUnsignedShort());
443 case MagnesiumPathArgument.AID_COUNT_4B:
444 return readAugmentationIdentifier(input.readInt());
446 return readAugmentationIdentifier(rshift(count, MagnesiumPathArgument.AID_COUNT_SHIFT));
450 private AugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
452 final List<QName> qnames = new ArrayList<>(size);
453 for (int i = 0; i < size; ++i) {
454 qnames.add(readQName());
456 return AugmentationIdentifier.create(ImmutableSet.copyOf(qnames));
457 } else if (size == 0) {
460 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
464 private NodeIdentifier readNodeIdentifier() throws IOException {
465 return decodeNodeIdentifier();
468 private NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
469 switch (header & MagnesiumPathArgument.QNAME_MASK) {
470 case MagnesiumPathArgument.QNAME_DEF:
471 return decodeNodeIdentifier();
472 case MagnesiumPathArgument.QNAME_REF_1B:
473 return decodeNodeIdentifierRef1();
474 case MagnesiumPathArgument.QNAME_REF_2B:
475 return decodeNodeIdentifierRef2();
476 case MagnesiumPathArgument.QNAME_REF_4B:
477 return decodeNodeIdentifierRef4();
479 throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
483 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header) throws IOException {
484 final QName qname = readNodeIdentifier(header).getNodeType();
485 switch (mask(header, MagnesiumPathArgument.SIZE_MASK)) {
486 case MagnesiumPathArgument.SIZE_1B:
487 return readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
488 case MagnesiumPathArgument.SIZE_2B:
489 return readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
490 case MagnesiumPathArgument.SIZE_4B:
491 return readNodeIdentifierWithPredicates(qname, input.readInt());
493 return readNodeIdentifierWithPredicates(qname, rshift(header, MagnesiumPathArgument.SIZE_SHIFT));
497 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
500 return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
501 } else if (size > 1) {
502 final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
503 for (int i = 0; i < size; ++i) {
504 builder.put(readQName(), readLeafValue());
506 return NodeIdentifierWithPredicates.of(qname, builder.build());
507 } else if (size == 0) {
508 return NodeIdentifierWithPredicates.of(qname);
510 throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
514 private NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
515 final QName qname = readNodeIdentifier(header).getNodeType();
516 return new NodeWithValue<>(qname, readLeafValue());
519 private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
520 if (mask(header, MagnesiumPathArgument.SIZE_MASK) != 0) {
521 throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
525 private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
526 return lookupNodeIdentifier(input.readUnsignedByte());
529 private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
530 return lookupNodeIdentifier(input.readUnsignedShort() + 256);
533 private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
534 return lookupNodeIdentifier(input.readInt());
537 private @NonNull QName decodeQName() throws IOException {
538 return decodeNodeIdentifier().getNodeType();
541 private @NonNull QName decodeQNameRef1() throws IOException {
542 return lookupQName(input.readUnsignedByte());
545 private @NonNull QName decodeQNameRef2() throws IOException {
546 return lookupQName(input.readUnsignedShort() + 256);
549 private @NonNull QName decodeQNameRef4() throws IOException {
550 return lookupQName(input.readInt());
553 private @NonNull QNameModule decodeQNameModule() throws IOException {
554 final byte type = input.readByte();
557 case MagnesiumValue.MODREF_1B:
558 index = input.readUnsignedByte();
560 case MagnesiumValue.MODREF_2B:
561 index = input.readUnsignedShort() + 256;
563 case MagnesiumValue.MODREF_4B:
564 index = input.readInt();
567 return decodeQNameModuleDef(type);
571 return codedModules.get(index);
572 } catch (IndexOutOfBoundsException e) {
573 throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
577 // QNameModule definition, i.e. two encoded strings
578 private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
579 final String namespace = readRefString(type);
581 final byte refType = input.readByte();
582 final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
583 final QNameModule module;
585 module = QNameFactory.createModule(namespace, revision);
586 } catch (UncheckedExecutionException e) {
587 throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
591 codedModules.add(module);
595 private @NonNull String readRefString() throws IOException {
596 return readRefString(input.readByte());
599 private @NonNull String readRefString(final byte type) throws IOException {
602 case MagnesiumValue.STRING_REF_1B:
603 return lookupString(input.readUnsignedByte());
604 case MagnesiumValue.STRING_REF_2B:
605 return lookupString(input.readUnsignedShort() + 256);
606 case MagnesiumValue.STRING_REF_4B:
607 return lookupString(input.readInt());
608 case MagnesiumValue.STRING_EMPTY:
610 case MagnesiumValue.STRING_2B:
613 case MagnesiumValue.STRING_4B:
616 case MagnesiumValue.STRING_CHARS:
617 str = readCharsString();
619 case MagnesiumValue.STRING_UTF:
620 str = input.readUTF();
623 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
626 // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
627 codedStrings.add(verifyNotNull(str));
631 private @NonNull String readString() throws IOException {
632 final byte type = input.readByte();
634 case MagnesiumValue.STRING_EMPTY:
636 case MagnesiumValue.STRING_UTF:
637 return input.readUTF();
638 case MagnesiumValue.STRING_2B:
639 return readString2();
640 case MagnesiumValue.STRING_4B:
641 return readString4();
642 case MagnesiumValue.STRING_CHARS:
643 return readCharsString();
645 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
649 private @NonNull String readString2() throws IOException {
650 return readByteString(input.readUnsignedShort());
653 private @NonNull String readString4() throws IOException {
654 return readByteString(input.readInt());
657 private @NonNull String readByteString(final int size) throws IOException {
659 final byte[] bytes = new byte[size];
660 input.readFully(bytes);
661 return new String(bytes, StandardCharsets.UTF_8);
662 } else if (size == 0) {
665 throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
669 private @NonNull String readCharsString() throws IOException {
670 final int size = input.readInt();
672 final char[] chars = new char[size];
673 for (int i = 0; i < size; ++i) {
674 chars[i] = input.readChar();
676 return String.valueOf(chars);
677 } else if (size == 0) {
680 throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
684 private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
686 return codedNodeIdentifiers.get(index);
687 } catch (IndexOutOfBoundsException e) {
688 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
692 private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
693 return lookupNodeIdentifier(index).getNodeType();
696 private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
698 return codedStrings.get(index);
699 } catch (IndexOutOfBoundsException e) {
700 throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
704 private @NonNull DOMSource readDOMSource() throws IOException {
705 final String str = readString();
707 return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
708 .getDocumentElement());
709 } catch (SAXException e) {
710 throw new IOException("Error parsing XML: " + str, e);
714 private @NonNull Object readLeafValue() throws IOException {
715 final byte type = input.readByte();
717 case MagnesiumValue.BOOLEAN_FALSE:
718 return Boolean.FALSE;
719 case MagnesiumValue.BOOLEAN_TRUE:
721 case MagnesiumValue.EMPTY:
722 return Empty.getInstance();
723 case MagnesiumValue.INT8:
724 return input.readByte();
725 case MagnesiumValue.INT8_0:
727 case MagnesiumValue.INT16:
728 return input.readShort();
729 case MagnesiumValue.INT16_0:
731 case MagnesiumValue.INT32:
732 return input.readInt();
733 case MagnesiumValue.INT32_0:
735 case MagnesiumValue.INT32_2B:
736 return input.readShort() & 0xFFFF;
737 case MagnesiumValue.INT64:
738 return input.readLong();
739 case MagnesiumValue.INT64_0:
741 case MagnesiumValue.INT64_4B:
742 return input.readInt() & 0xFFFFFFFFL;
743 case MagnesiumValue.UINT8:
744 return Uint8.fromByteBits(input.readByte());
745 case MagnesiumValue.UINT8_0:
747 case MagnesiumValue.UINT16:
748 return Uint16.fromShortBits(input.readShort());
749 case MagnesiumValue.UINT16_0:
751 case MagnesiumValue.UINT32:
752 return Uint32.fromIntBits(input.readInt());
753 case MagnesiumValue.UINT32_0:
755 case MagnesiumValue.UINT32_2B:
756 return Uint32.fromIntBits(input.readShort() & 0xFFFF);
757 case MagnesiumValue.UINT64:
758 return Uint64.fromLongBits(input.readLong());
759 case MagnesiumValue.UINT64_0:
761 case MagnesiumValue.UINT64_4B:
762 return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
763 case MagnesiumValue.BIGDECIMAL:
764 // FIXME: use string -> BigDecimal cache
765 return new BigDecimal(input.readUTF());
766 case MagnesiumValue.BIGINTEGER:
767 return readBigInteger();
768 case MagnesiumValue.STRING_EMPTY:
770 case MagnesiumValue.STRING_UTF:
771 return input.readUTF();
772 case MagnesiumValue.STRING_2B:
773 return readString2();
774 case MagnesiumValue.STRING_4B:
775 return readString4();
776 case MagnesiumValue.STRING_CHARS:
777 return readCharsString();
778 case MagnesiumValue.BINARY_0:
780 case MagnesiumValue.BINARY_1B:
781 return readBinary(128 + input.readUnsignedByte());
782 case MagnesiumValue.BINARY_2B:
783 return readBinary(384 + input.readUnsignedShort());
784 case MagnesiumValue.BINARY_4B:
785 return readBinary(input.readInt());
786 case MagnesiumValue.YIID_0:
787 return YangInstanceIdentifier.empty();
788 case MagnesiumValue.YIID:
789 return readYangInstanceIdentifier(input.readInt());
790 case MagnesiumValue.QNAME:
791 return decodeQName();
792 case MagnesiumValue.QNAME_REF_1B:
793 return decodeQNameRef1();
794 case MagnesiumValue.QNAME_REF_2B:
795 return decodeQNameRef2();
796 case MagnesiumValue.QNAME_REF_4B:
797 return decodeQNameRef4();
798 case MagnesiumValue.BITS_0:
799 return ImmutableSet.of();
800 case MagnesiumValue.BITS_1B:
801 return readBits(input.readUnsignedByte() + 29);
802 case MagnesiumValue.BITS_2B:
803 return readBits(input.readUnsignedShort() + 285);
804 case MagnesiumValue.BITS_4B:
805 return readBits(input.readInt());
808 if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
809 return readBinary(type - MagnesiumValue.BINARY_0);
810 } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
811 return readBits(type - MagnesiumValue.BITS_0);
812 } else if (type > MagnesiumValue.YIID_0) {
813 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
814 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
816 throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
821 abstract @NonNull BigInteger readBigInteger() throws IOException;
823 private byte @NonNull [] readBinary(final int size) throws IOException {
825 final byte[] ret = new byte[size];
826 input.readFully(ret);
828 } else if (size == 0) {
831 throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
835 private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
837 final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
838 for (int i = 0; i < size; ++i) {
839 builder.add(readRefString());
841 return builder.build();
842 } else if (size == 0) {
843 return ImmutableSet.of();
845 throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);
849 private static byte mask(final byte header, final byte mask) {
850 return (byte) (header & mask);
853 private static int rshift(final byte header, final byte shift) {
854 return (header & 0xFF) >>> shift;