c46e7f36ba9cca30a8874646bf7eefe9ff81dffe
[yangtools.git] / codec / yang-data-codec-binfmt / src / main / java / org / opendaylight / yangtools / yang / data / codec / binfmt / NeonSR2NormalizedNodeInputStreamReader.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.yangtools.yang.data.codec.binfmt;
9
10 import static com.google.common.base.Verify.verify;
11
12 import java.io.DataInput;
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.common.QNameModule;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
20
21 /**
22  * Neon SR2 specialization of AbstractLithiumDataInput. Unlike its Lithium counterpart, this format uses coding for
23  * QNameModules, QNames, NodeIdentifiers and AugmentationIdentifiers, thus reducing stream duplication.
24  */
25 final class NeonSR2NormalizedNodeInputStreamReader extends AbstractLithiumDataInput {
26     private final ArrayList<NodeIdentifier> codedNodeIdentifiers = new ArrayList<>();
27     private final List<AugmentationIdentifier> codedAugments = new ArrayList<>();
28     private final List<QNameModule> codedModules = new ArrayList<>();
29     private final List<QName> codedQNames = new ArrayList<>();
30
31     NeonSR2NormalizedNodeInputStreamReader(final DataInput input) {
32         super(input);
33     }
34
35     @Override
36     @Deprecated(since = "11.0.0", forRemoval = true)
37     public NormalizedNodeStreamVersion getVersion() {
38         return NormalizedNodeStreamVersion.NEON_SR2;
39     }
40
41     @Override
42     public QName readQName() throws IOException {
43         final byte valueType = readByte();
44         return switch (valueType) {
45             case NeonSR2Tokens.IS_QNAME_CODE -> codedQName(readInt());
46             case NeonSR2Tokens.IS_QNAME_VALUE -> rawQName();
47             default -> throw new IOException("Unhandled QName value type " + valueType);
48         };
49     }
50
51     @Override
52     AugmentationIdentifier readAugmentationIdentifier() throws IOException {
53         final byte valueType = readByte();
54         return switch (valueType) {
55             case NeonSR2Tokens.IS_AUGMENT_CODE -> codedAugmentId(readInt());
56             case NeonSR2Tokens.IS_AUGMENT_VALUE -> rawAugmentId();
57             default -> throw new IOException("Unhandled AugmentationIdentifier value type " + valueType);
58         };
59     }
60
61     @Override
62     NodeIdentifier readNodeIdentifier() throws IOException {
63         // NodeIdentifier rides on top of QName, with this method really saying 'interpret next QName as NodeIdentifier'
64         // to do that we inter-mingle with readQName()
65         final byte valueType = readByte();
66         return switch (valueType) {
67             case NeonSR2Tokens.IS_QNAME_CODE -> codedNodeIdentifier(readInt());
68             case NeonSR2Tokens.IS_QNAME_VALUE -> rawNodeIdentifier();
69             default -> throw new IOException("Unhandled NodeIdentifier value type " + valueType);
70         };
71     }
72
73     private QNameModule readModule() throws IOException {
74         final byte valueType = readByte();
75         return switch (valueType) {
76             case NeonSR2Tokens.IS_MODULE_CODE -> codedModule(readInt());
77             case NeonSR2Tokens.IS_MODULE_VALUE -> rawModule();
78             default -> throw new IOException("Unhandled QNameModule value type " + valueType);
79         };
80     }
81
82     private NodeIdentifier codedNodeIdentifier(final int code) throws IOException {
83         final NodeIdentifier existing = codedNodeIdentifiers.size() > code ? codedNodeIdentifiers.get(code) : null;
84         return existing != null ? existing : storeNodeIdentifier(code, codedQName(code));
85     }
86
87     private NodeIdentifier rawNodeIdentifier() throws IOException {
88         // Capture size before it incremented
89         final int code = codedQNames.size();
90         return storeNodeIdentifier(code, rawQName());
91     }
92
93     private NodeIdentifier storeNodeIdentifier(final int code, final QName qname) {
94         final NodeIdentifier ret = NodeIdentifier.create(qname);
95         final int size = codedNodeIdentifiers.size();
96
97         if (code >= size) {
98             // Null-fill others
99             codedNodeIdentifiers.ensureCapacity(code + 1);
100             for (int i = size; i < code; ++i) {
101                 codedNodeIdentifiers.add(null);
102             }
103
104             codedNodeIdentifiers.add(ret);
105         } else {
106             final NodeIdentifier check = codedNodeIdentifiers.set(code, ret);
107             verify(check == null);
108         }
109
110         return ret;
111     }
112
113     private QName codedQName(final int code) throws IOException {
114         return getCode("QName", codedQNames, code);
115     }
116
117     private QName rawQName() throws IOException {
118         final String localName = readCodedString();
119         final QNameModule module = readModule();
120         final QName qname = QNameFactory.create(module, localName);
121         codedQNames.add(qname);
122         return qname;
123     }
124
125     private AugmentationIdentifier codedAugmentId(final int code) throws IOException {
126         return getCode("QName set", codedAugments, code);
127     }
128
129     private AugmentationIdentifier rawAugmentId() throws IOException {
130         final AugmentationIdentifier aid = defaultReadAugmentationIdentifier();
131         codedAugments.add(aid);
132         return aid;
133     }
134
135     private QNameModule codedModule(final int code) throws IOException {
136         return getCode("Module", codedModules, code);
137     }
138
139     private QNameModule rawModule() throws IOException {
140         final String namespace = readCodedString();
141         final String revision = readCodedString();
142         final QNameModule mod = QNameFactory.createModule(namespace, revision);
143         codedModules.add(mod);
144         return mod;
145     }
146
147     private static <T> T getCode(final String name, final List<T> list, final int code) throws IOException {
148         try {
149             return list.get(code);
150         } catch (IndexOutOfBoundsException e) {
151             throw new IOException(name + " code " + code + " was not found", e);
152         }
153     }
154 }