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);
140 writer.startAnyxmlNode(identifier);
141 writer.domSourceValue(readDOMSource());
145 private void streamAnyxmlModeled(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
147 // TODO: decide how to deal with these
148 throw new UnsupportedOperationException("Reading YANG-modeled anyxml was never supported");
151 private void streamAugmentation(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
152 final AugmentationIdentifier augIdentifier = decodeAugmentationIdentifier(nodeHeader);
153 LOG.trace("Streaming augmentation node {}", augIdentifier);
154 writer.startAugmentationNode(augIdentifier);
155 commonStreamContainer(writer, augIdentifier);
158 private void streamChoice(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
159 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
160 LOG.trace("Streaming choice node {}", identifier);
161 writer.startChoiceNode(identifier, UNKNOWN_SIZE);
162 commonStreamContainer(writer, identifier);
165 private void streamContainer(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
166 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
167 LOG.trace("Streaming container node {}", identifier);
168 writer.startContainerNode(identifier, UNKNOWN_SIZE);
169 commonStreamContainer(writer, identifier);
172 private void streamLeaf(final NormalizedNodeStreamWriter writer, final PathArgument parent, final byte nodeHeader)
174 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
175 LOG.trace("Streaming leaf node {}", identifier);
176 writer.startLeafNode(identifier);
179 if ((nodeHeader & MagnesiumNode.PREDICATE_ONE) == MagnesiumNode.PREDICATE_ONE) {
180 if (!(parent instanceof NodeIdentifierWithPredicates)) {
181 throw new InvalidNormalizedNodeStreamException("Invalid predicate leaf " + identifier + " in parent "
185 value = ((NodeIdentifierWithPredicates) parent).getValue(identifier.getNodeType());
187 throw new InvalidNormalizedNodeStreamException("Failed to find predicate leaf " + identifier
188 + " in parent " + parent);
191 value = readLeafValue();
194 writer.scalarValue(value);
198 private void streamLeafset(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
199 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
200 LOG.trace("Streaming leaf set node {}", identifier);
201 writer.startLeafSet(identifier, UNKNOWN_SIZE);
202 commonStreamContainer(writer, identifier);
205 private void streamLeafsetOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
207 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
208 LOG.trace("Streaming ordered leaf set node {}", identifier);
209 writer.startOrderedLeafSet(identifier, UNKNOWN_SIZE);
211 commonStreamContainer(writer, identifier);
214 private void streamLeafsetEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
215 final byte nodeHeader) throws IOException {
216 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
217 final Object value = readLeafValue();
218 final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(nodeId.getNodeType(), value);
219 LOG.trace("Streaming leaf set entry node {}", leafIdentifier);
220 writer.startLeafSetEntryNode(leafIdentifier);
221 writer.scalarValue(value);
225 private void streamList(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
226 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
227 writer.startUnkeyedList(identifier, UNKNOWN_SIZE);
228 commonStreamContainer(writer, identifier);
231 private void streamListEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
232 final byte nodeHeader) throws IOException {
233 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader, parent);
234 LOG.trace("Streaming unkeyed list item node {}", identifier);
235 writer.startUnkeyedListItem(identifier, UNKNOWN_SIZE);
236 commonStreamContainer(writer, identifier);
239 private void streamMap(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
240 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
241 LOG.trace("Streaming map node {}", identifier);
242 writer.startMapNode(identifier, UNKNOWN_SIZE);
243 commonStreamContainer(writer, identifier);
246 private void streamMapOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
247 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
248 LOG.trace("Streaming ordered map node {}", identifier);
249 writer.startOrderedMapNode(identifier, UNKNOWN_SIZE);
250 commonStreamContainer(writer, identifier);
253 private void streamMapEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
254 final byte nodeHeader) throws IOException {
255 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
258 switch (nodeHeader & MagnesiumNode.PREDICATE_MASK) {
259 case MagnesiumNode.PREDICATE_ZERO:
262 case MagnesiumNode.PREDICATE_ONE:
265 case MagnesiumNode.PREDICATE_1B:
266 size = input.readUnsignedByte();
268 case MagnesiumNode.PREDICATE_4B:
269 size = input.readInt();
272 // ISE on purpose: this should never ever happen
273 throw new IllegalStateException("Failed to decode NodeIdentifierWithPredicates size from header "
277 final NodeIdentifierWithPredicates identifier = readNodeIdentifierWithPredicates(nodeId.getNodeType(), size);
278 LOG.trace("Streaming map entry node {}", identifier);
279 writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
280 commonStreamContainer(writer, identifier);
283 private void commonStreamContainer(final NormalizedNodeStreamWriter writer, final PathArgument parent)
285 for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
286 streamNormalizedNode(writer, parent, nodeType);
291 private @NonNull NodeIdentifier decodeNodeIdentifier() throws IOException {
292 final QNameModule module = decodeQNameModule();
293 final String localName = readRefString();
294 final NodeIdentifier nodeId;
296 nodeId = QNameFactory.getNodeIdentifier(module, localName);
297 } catch (ExecutionException e) {
298 throw new InvalidNormalizedNodeStreamException("Illegal QName module=" + module + " localName="
302 codedNodeIdentifiers.add(nodeId);
306 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader) throws IOException {
307 return decodeNodeIdentifier(nodeHeader, null);
310 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader, final PathArgument parent) throws IOException {
312 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
313 case MagnesiumNode.ADDR_DEFINE:
314 return readNodeIdentifier();
315 case MagnesiumNode.ADDR_LOOKUP_1B:
316 index = input.readUnsignedByte();
318 case MagnesiumNode.ADDR_LOOKUP_4B:
319 index = input.readInt();
321 case MagnesiumNode.ADDR_PARENT:
322 if (parent instanceof NodeIdentifier) {
323 return (NodeIdentifier) parent;
325 throw new InvalidNormalizedNodeStreamException("Invalid node identifier reference to parent " + parent);
327 throw new InvalidNormalizedNodeStreamException("Unexpected node identifier addressing in header "
332 return codedNodeIdentifiers.get(index);
333 } catch (IndexOutOfBoundsException e) {
334 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
338 private AugmentationIdentifier decodeAugmentationIdentifier(final byte nodeHeader) throws IOException {
340 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
341 case MagnesiumNode.ADDR_DEFINE:
342 return readAugmentationIdentifier();
343 case MagnesiumNode.ADDR_LOOKUP_1B:
344 index = input.readUnsignedByte();
346 case MagnesiumNode.ADDR_LOOKUP_4B:
347 index = input.readInt();
350 throw new InvalidNormalizedNodeStreamException(
351 "Unexpected augmentation identifier addressing in header " + nodeHeader);
355 return codedAugments.get(index);
356 } catch (IndexOutOfBoundsException e) {
357 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier reference " + index, e);
362 public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
363 final byte type = input.readByte();
364 if (type == MagnesiumValue.YIID) {
365 return readYangInstanceIdentifier(input.readInt());
366 } else if (type >= MagnesiumValue.YIID_0) {
367 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
368 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
370 throw new InvalidNormalizedNodeStreamException("Unexpected YangInstanceIdentifier type " + type);
374 private @NonNull YangInstanceIdentifier readYangInstanceIdentifier(final int size) throws IOException {
376 final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
377 for (int i = 0; i < size; ++i) {
378 builder.add(readPathArgument());
380 return YangInstanceIdentifier.create(builder.build());
381 } else if (size == 0) {
382 return YangInstanceIdentifier.empty();
384 throw new InvalidNormalizedNodeStreamException("Invalid YangInstanceIdentifier size " + size);
389 public final QName readQName() throws IOException {
390 final byte type = input.readByte();
392 case MagnesiumValue.QNAME:
393 return decodeQName();
394 case MagnesiumValue.QNAME_REF_1B:
395 return decodeQNameRef1();
396 case MagnesiumValue.QNAME_REF_2B:
397 return decodeQNameRef2();
398 case MagnesiumValue.QNAME_REF_4B:
399 return decodeQNameRef4();
401 throw new InvalidNormalizedNodeStreamException("Unexpected QName type " + type);
406 public final PathArgument readPathArgument() throws IOException {
407 final byte header = input.readByte();
408 switch (header & MagnesiumPathArgument.TYPE_MASK) {
409 case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER:
410 return readAugmentationIdentifier(header);
411 case MagnesiumPathArgument.NODE_IDENTIFIER:
412 verifyPathIdentifierOnly(header);
413 return readNodeIdentifier(header);
414 case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES:
415 return readNodeIdentifierWithPredicates(header);
416 case MagnesiumPathArgument.NODE_WITH_VALUE:
417 verifyPathIdentifierOnly(header);
418 return readNodeWithValue(header);
419 case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER:
420 verifyPathIdentifierOnly(header);
421 return MountPointIdentifier.create(readNodeIdentifier(header).getNodeType());
423 throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
427 private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
428 final AugmentationIdentifier result = readAugmentationIdentifier(input.readInt());
429 codedAugments.add(result);
433 private AugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
434 final int count = header & MagnesiumPathArgument.AID_COUNT_MASK;
436 case MagnesiumPathArgument.AID_COUNT_1B:
437 return readAugmentationIdentifier(input.readUnsignedByte());
438 case MagnesiumPathArgument.AID_COUNT_2B:
439 return readAugmentationIdentifier(input.readUnsignedShort());
440 case MagnesiumPathArgument.AID_COUNT_4B:
441 return readAugmentationIdentifier(input.readInt());
443 return readAugmentationIdentifier(count >>> MagnesiumPathArgument.AID_COUNT_SHIFT);
447 private AugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
449 final List<QName> qnames = new ArrayList<>(size);
450 for (int i = 0; i < size; ++i) {
451 qnames.add(readQName());
453 return AugmentationIdentifier.create(ImmutableSet.copyOf(qnames));
454 } else if (size == 0) {
457 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
461 private NodeIdentifier readNodeIdentifier() throws IOException {
462 return decodeNodeIdentifier();
465 private NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
466 switch (header & MagnesiumPathArgument.QNAME_MASK) {
467 case MagnesiumPathArgument.QNAME_DEF:
468 return decodeNodeIdentifier();
469 case MagnesiumPathArgument.QNAME_REF_1B:
470 return decodeNodeIdentifierRef1();
471 case MagnesiumPathArgument.QNAME_REF_2B:
472 return decodeNodeIdentifierRef2();
473 case MagnesiumPathArgument.QNAME_REF_4B:
474 return decodeNodeIdentifierRef4();
476 throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
480 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header) throws IOException {
481 final QName qname = readNodeIdentifier(header).getNodeType();
482 switch (header & MagnesiumPathArgument.SIZE_MASK) {
483 case MagnesiumPathArgument.SIZE_1B:
484 return readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
485 case MagnesiumPathArgument.SIZE_2B:
486 return readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
487 case MagnesiumPathArgument.SIZE_4B:
488 return readNodeIdentifierWithPredicates(qname, input.readInt());
490 return readNodeIdentifierWithPredicates(qname, header >>> MagnesiumPathArgument.SIZE_SHIFT);
494 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
497 return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
498 } else if (size > 1) {
499 final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
500 for (int i = 0; i < size; ++i) {
501 builder.put(readQName(), readLeafValue());
503 return NodeIdentifierWithPredicates.of(qname, builder.build());
504 } else if (size == 0) {
505 return NodeIdentifierWithPredicates.of(qname);
507 throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
511 private NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
512 final QName qname = readNodeIdentifier(header).getNodeType();
513 return new NodeWithValue<>(qname, readLeafValue());
516 private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
517 if ((header & MagnesiumPathArgument.SIZE_MASK) != 0) {
518 throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
522 private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
523 return lookupNodeIdentifier(input.readUnsignedByte());
526 private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
527 return lookupNodeIdentifier(input.readUnsignedShort() + 256);
530 private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
531 return lookupNodeIdentifier(input.readInt());
534 private @NonNull QName decodeQName() throws IOException {
535 return decodeNodeIdentifier().getNodeType();
538 private @NonNull QName decodeQNameRef1() throws IOException {
539 return lookupQName(input.readUnsignedByte());
542 private @NonNull QName decodeQNameRef2() throws IOException {
543 return lookupQName(input.readUnsignedShort() + 256);
546 private @NonNull QName decodeQNameRef4() throws IOException {
547 return lookupQName(input.readInt());
550 private @NonNull QNameModule decodeQNameModule() throws IOException {
551 final byte type = input.readByte();
554 case MagnesiumValue.MODREF_1B:
555 index = input.readUnsignedByte();
557 case MagnesiumValue.MODREF_2B:
558 index = input.readUnsignedShort() + 256;
560 case MagnesiumValue.MODREF_4B:
561 index = input.readInt();
564 return decodeQNameModuleDef(type);
568 return codedModules.get(index);
569 } catch (IndexOutOfBoundsException e) {
570 throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
574 // QNameModule definition, i.e. two encoded strings
575 private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
576 final String namespace = readRefString(type);
578 final byte refType = input.readByte();
579 final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
580 final QNameModule module;
582 module = QNameFactory.createModule(namespace, revision);
583 } catch (UncheckedExecutionException e) {
584 throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
588 codedModules.add(module);
592 private @NonNull String readRefString() throws IOException {
593 return readRefString(input.readByte());
596 private @NonNull String readRefString(final byte type) throws IOException {
599 case MagnesiumValue.STRING_REF_1B:
600 return lookupString(input.readUnsignedByte());
601 case MagnesiumValue.STRING_REF_2B:
602 return lookupString(input.readUnsignedShort() + 256);
603 case MagnesiumValue.STRING_REF_4B:
604 return lookupString(input.readInt());
605 case MagnesiumValue.STRING_EMPTY:
607 case MagnesiumValue.STRING_2B:
610 case MagnesiumValue.STRING_4B:
613 case MagnesiumValue.STRING_CHARS:
614 str = readCharsString();
616 case MagnesiumValue.STRING_UTF:
617 str = input.readUTF();
620 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
623 // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
624 codedStrings.add(verifyNotNull(str));
628 private @NonNull String readString() throws IOException {
629 final byte type = input.readByte();
631 case MagnesiumValue.STRING_EMPTY:
633 case MagnesiumValue.STRING_UTF:
634 return input.readUTF();
635 case MagnesiumValue.STRING_2B:
636 return readString2();
637 case MagnesiumValue.STRING_4B:
638 return readString4();
639 case MagnesiumValue.STRING_CHARS:
640 return readCharsString();
642 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
646 private @NonNull String readString2() throws IOException {
647 return readByteString(input.readUnsignedShort());
650 private @NonNull String readString4() throws IOException {
651 return readByteString(input.readInt());
654 private @NonNull String readByteString(final int size) throws IOException {
656 final byte[] bytes = new byte[size];
657 input.readFully(bytes);
658 return new String(bytes, StandardCharsets.UTF_8);
659 } else if (size == 0) {
662 throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
666 private @NonNull String readCharsString() throws IOException {
667 final int size = input.readInt();
669 final char[] chars = new char[size];
670 for (int i = 0; i < size; ++i) {
671 chars[i] = input.readChar();
673 return String.valueOf(chars);
674 } else if (size == 0) {
677 throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
681 private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
683 return codedNodeIdentifiers.get(index);
684 } catch (IndexOutOfBoundsException e) {
685 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
689 private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
690 return lookupNodeIdentifier(index).getNodeType();
693 private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
695 return codedStrings.get(index);
696 } catch (IndexOutOfBoundsException e) {
697 throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
701 private @NonNull DOMSource readDOMSource() throws IOException {
702 final String str = readString();
704 return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
705 .getDocumentElement());
706 } catch (SAXException e) {
707 throw new IOException("Error parsing XML: " + str, e);
711 private @NonNull Object readLeafValue() throws IOException {
712 final byte type = input.readByte();
714 case MagnesiumValue.BOOLEAN_FALSE:
715 return Boolean.FALSE;
716 case MagnesiumValue.BOOLEAN_TRUE:
718 case MagnesiumValue.EMPTY:
719 return Empty.getInstance();
720 case MagnesiumValue.INT8:
721 return input.readByte();
722 case MagnesiumValue.INT8_0:
724 case MagnesiumValue.INT16:
725 return input.readShort();
726 case MagnesiumValue.INT16_0:
728 case MagnesiumValue.INT32:
729 return input.readInt();
730 case MagnesiumValue.INT32_0:
732 case MagnesiumValue.INT32_2B:
733 return input.readShort() & 0xFFFF;
734 case MagnesiumValue.INT64:
735 return input.readLong();
736 case MagnesiumValue.INT64_0:
738 case MagnesiumValue.INT64_4B:
739 return input.readInt() & 0xFFFFFFFFL;
740 case MagnesiumValue.UINT8:
741 return Uint8.fromByteBits(input.readByte());
742 case MagnesiumValue.UINT8_0:
744 case MagnesiumValue.UINT16:
745 return Uint16.fromShortBits(input.readShort());
746 case MagnesiumValue.UINT16_0:
748 case MagnesiumValue.UINT32:
749 return Uint32.fromIntBits(input.readInt());
750 case MagnesiumValue.UINT32_0:
752 case MagnesiumValue.UINT32_2B:
753 return Uint32.fromIntBits(input.readShort() & 0xFFFF);
754 case MagnesiumValue.UINT64:
755 return Uint64.fromLongBits(input.readLong());
756 case MagnesiumValue.UINT64_0:
758 case MagnesiumValue.UINT64_4B:
759 return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
760 case MagnesiumValue.BIGDECIMAL:
761 // FIXME: use string -> BigDecimal cache
762 return new BigDecimal(input.readUTF());
763 case MagnesiumValue.BIGINTEGER:
764 return readBigInteger();
765 case MagnesiumValue.STRING_EMPTY:
767 case MagnesiumValue.STRING_UTF:
768 return input.readUTF();
769 case MagnesiumValue.STRING_2B:
770 return readString2();
771 case MagnesiumValue.STRING_4B:
772 return readString4();
773 case MagnesiumValue.STRING_CHARS:
774 return readCharsString();
775 case MagnesiumValue.BINARY_0:
777 case MagnesiumValue.BINARY_1B:
778 return readBinary(128 + input.readUnsignedByte());
779 case MagnesiumValue.BINARY_2B:
780 return readBinary(384 + input.readUnsignedShort());
781 case MagnesiumValue.BINARY_4B:
782 return readBinary(input.readInt());
783 case MagnesiumValue.YIID_0:
784 return YangInstanceIdentifier.empty();
785 case MagnesiumValue.YIID:
786 return readYangInstanceIdentifier(input.readInt());
787 case MagnesiumValue.QNAME:
788 return decodeQName();
789 case MagnesiumValue.QNAME_REF_1B:
790 return decodeQNameRef1();
791 case MagnesiumValue.QNAME_REF_2B:
792 return decodeQNameRef2();
793 case MagnesiumValue.QNAME_REF_4B:
794 return decodeQNameRef4();
795 case MagnesiumValue.BITS_0:
796 return ImmutableSet.of();
797 case MagnesiumValue.BITS_1B:
798 return readBits(input.readUnsignedByte() + 29);
799 case MagnesiumValue.BITS_2B:
800 return readBits(input.readUnsignedShort() + 285);
801 case MagnesiumValue.BITS_4B:
802 return readBits(input.readInt());
805 if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
806 return readBinary(type - MagnesiumValue.BINARY_0);
807 } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
808 return readBits(type - MagnesiumValue.BITS_0);
809 } else if (type > MagnesiumValue.YIID_0) {
810 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
811 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
813 throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
818 abstract @NonNull BigInteger readBigInteger() throws IOException;
820 private byte @NonNull [] readBinary(final int size) throws IOException {
822 final byte[] ret = new byte[size];
823 input.readFully(ret);
825 } else if (size == 0) {
828 throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
832 private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
834 final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
835 for (int i = 0; i < size; ++i) {
836 builder.add(readRefString());
838 return builder.build();
839 } else if (size == 0) {
840 return ImmutableSet.of();
842 throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);