Merge branch 'master' of ../controller
[yangtools.git] / yang / 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     public NormalizedNodeStreamVersion getVersion() {
37         return NormalizedNodeStreamVersion.NEON_SR2;
38     }
39
40     @Override
41     public QName readQName() throws IOException {
42         final byte valueType = readByte();
43         switch (valueType) {
44             case NeonSR2Tokens.IS_QNAME_CODE:
45                 return codedQName(readInt());
46             case NeonSR2Tokens.IS_QNAME_VALUE:
47                 return rawQName();
48             default:
49                 throw new IOException("Unhandled QName value type " + valueType);
50         }
51     }
52
53     @Override
54     AugmentationIdentifier readAugmentationIdentifier() throws IOException {
55         final byte valueType = readByte();
56         switch (valueType) {
57             case NeonSR2Tokens.IS_AUGMENT_CODE:
58                 return codedAugmentId(readInt());
59             case NeonSR2Tokens.IS_AUGMENT_VALUE:
60                 return rawAugmentId();
61             default:
62                 throw new IOException("Unhandled QName value type " + valueType);
63         }
64     }
65
66     @Override
67     NodeIdentifier readNodeIdentifier() throws IOException {
68         // NodeIdentifier rides on top of QName, with this method really saying 'interpret next QName as NodeIdentifier'
69         // to do that we inter-mingle with readQName()
70         final byte valueType = readByte();
71         switch (valueType) {
72             case NeonSR2Tokens.IS_QNAME_CODE:
73                 return codedNodeIdentifier(readInt());
74             case NeonSR2Tokens.IS_QNAME_VALUE:
75                 return rawNodeIdentifier();
76             default:
77                 throw new IOException("Unhandled QName value type " + valueType);
78         }
79     }
80
81     private QNameModule readModule() throws IOException {
82         final byte valueType = readByte();
83         switch (valueType) {
84             case NeonSR2Tokens.IS_MODULE_CODE:
85                 return codedModule(readInt());
86             case NeonSR2Tokens.IS_MODULE_VALUE:
87                 return rawModule();
88             default:
89                 throw new IOException("Unhandled QName value type " + valueType);
90         }
91     }
92
93     private NodeIdentifier codedNodeIdentifier(final int code) throws IOException {
94         final NodeIdentifier existing = codedNodeIdentifiers.size() > code ? codedNodeIdentifiers.get(code) : null;
95         return existing != null ? existing : storeNodeIdentifier(code, codedQName(code));
96     }
97
98     private NodeIdentifier rawNodeIdentifier() throws IOException {
99         // Capture size before it incremented
100         final int code = codedQNames.size();
101         return storeNodeIdentifier(code, rawQName());
102     }
103
104     private NodeIdentifier storeNodeIdentifier(final int code, final QName qname) {
105         final NodeIdentifier ret = NodeIdentifier.create(qname);
106         final int size = codedNodeIdentifiers.size();
107
108         if (code >= size) {
109             // Null-fill others
110             codedNodeIdentifiers.ensureCapacity(code + 1);
111             for (int i = size; i < code; ++i) {
112                 codedNodeIdentifiers.add(null);
113             }
114
115             codedNodeIdentifiers.add(ret);
116         } else {
117             final NodeIdentifier check = codedNodeIdentifiers.set(code, ret);
118             verify(check == null);
119         }
120
121         return ret;
122     }
123
124     private QName codedQName(final int code) throws IOException {
125         try {
126             return codedQNames.get(code);
127         } catch (IndexOutOfBoundsException e) {
128             throw new IOException("QName code " + code + " was not found", e);
129         }
130     }
131
132     private QName rawQName() throws IOException {
133         final String localName = readCodedString();
134         final QNameModule module = readModule();
135         final QName qname = QNameFactory.create(module, localName);
136         codedQNames.add(qname);
137         return qname;
138     }
139
140     private AugmentationIdentifier codedAugmentId(final int code) throws IOException {
141         try {
142             return codedAugments.get(code);
143         } catch (IndexOutOfBoundsException e) {
144             throw new IOException("QName set code " + code + " was not found", e);
145         }
146     }
147
148     private AugmentationIdentifier rawAugmentId() throws IOException {
149         final AugmentationIdentifier aid = defaultReadAugmentationIdentifier();
150         codedAugments.add(aid);
151         return aid;
152     }
153
154     private QNameModule codedModule(final int code) throws IOException {
155         try {
156             return codedModules.get(code);
157         } catch (IndexOutOfBoundsException e) {
158             throw new IOException("Module code " + code + " was not found", e);
159         }
160     }
161
162     private QNameModule rawModule() throws IOException {
163         final String namespace = readCodedString();
164         final String revision = readCodedString();
165         final QNameModule mod = QNameFactory.createModule(namespace, revision);
166         codedModules.add(mod);
167         return mod;
168     }
169 }