Skip to content

Commit d80283e

Browse files
tgregglinlin-s
authored andcommitted
Adds a system-level IonReader wrapper over the continuable reader.
1 parent 46824d7 commit d80283e

File tree

1 file changed

+352
-0
lines changed

1 file changed

+352
-0
lines changed
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package com.amazon.ion.impl;
5+
6+
import com.amazon.ion.Decimal;
7+
import com.amazon.ion.IntegerSize;
8+
import com.amazon.ion.IonException;
9+
import com.amazon.ion.IonReader;
10+
import com.amazon.ion.IonCursor;
11+
import com.amazon.ion.IonType;
12+
import com.amazon.ion.SymbolTable;
13+
import com.amazon.ion.SymbolToken;
14+
import com.amazon.ion.SystemSymbols;
15+
import com.amazon.ion.Timestamp;
16+
import com.amazon.ion.UnknownSymbolException;
17+
18+
import java.io.IOException;
19+
import java.math.BigDecimal;
20+
import java.math.BigInteger;
21+
import java.util.ArrayDeque;
22+
import java.util.Date;
23+
import java.util.Iterator;
24+
import java.util.Queue;
25+
26+
import static com.amazon.ion.IonCursor.Event.NEEDS_DATA;
27+
28+
/**
29+
* Provides a system-level view of an Ion stream. This differs from the application-level view in that system values
30+
* (IVMs and symbol tables) are surfaced as if they were user values and symbols are not mapped, except for symbols
31+
* that occur within the system symbol table. Because this is primarily intended to be used for debugging and is not
32+
* exposed via public interfaces, it is not currently considered performance-critical, and no continuable equivalent
33+
* is provided.
34+
*/
35+
class IonReaderNonContinuableSystem implements IonReader {
36+
37+
protected final IonReaderContinuableCore reader;
38+
private IonType type = null;
39+
private IonType typeAfterIvm = null;
40+
private final Queue<Integer> pendingIvmSids = new ArrayDeque<>(1);
41+
private int pendingIvmSid = -1;
42+
43+
/**
44+
* Constructs a new non-continuable system-level reader over the given continuable reader.
45+
* @param reader the reader to wrap.
46+
*/
47+
IonReaderNonContinuableSystem(IonReaderContinuableCore reader) {
48+
this.reader = reader;
49+
reader.registerIvmNotificationConsumer((x, y) -> {
50+
pendingIvmSids.add(SystemSymbols.ION_1_0_SID); // TODO generalize for Ion 1.1
51+
});
52+
}
53+
54+
@Override
55+
public boolean hasNext() {
56+
throw new UnsupportedOperationException("Not implemented -- use `next() != null`");
57+
}
58+
59+
/**
60+
* Consumes an IVM that was encountered before the next value, presenting the IVM as a symbol value. If an IVM
61+
* was emitted on the last invocation, restores the next value to be presented to the user.
62+
* @return true if a value is ready to be presented to the user; otherwise, false.
63+
*/
64+
private boolean handleIvm() {
65+
Integer ivmSid = pendingIvmSids.poll();
66+
if (ivmSid != null) {
67+
// An IVM has been found between values.
68+
if (typeAfterIvm == null) {
69+
// Only save the type of the next user value the first time an IVM is encountered before that value.
70+
typeAfterIvm = type;
71+
}
72+
// For consistency with the legacy implementation, the system reader surfaces IVMs as symbol values.
73+
type = IonType.SYMBOL;
74+
pendingIvmSid = ivmSid;
75+
return true;
76+
} else if (pendingIvmSid != -1) {
77+
// All preceding IVMs have been surfaced. Restore the value that follows.
78+
pendingIvmSid = -1;
79+
type = typeAfterIvm;
80+
typeAfterIvm = null;
81+
return true;
82+
}
83+
return false;
84+
}
85+
86+
@Override
87+
public IonType next() {
88+
if (handleIvm()) {
89+
// This happens if there were multiple IVMs between top-level values. They will be drained from the queue
90+
// one-by-one on each call to next().
91+
return type;
92+
}
93+
if (reader.nextValue() == NEEDS_DATA) {
94+
if (handleIvm()) {
95+
// This happens if one or more IVMs occurs before the end of the stream.
96+
return type;
97+
}
98+
reader.endStream();
99+
type = null;
100+
} else {
101+
type = reader.getType();
102+
handleIvm(); // Handles the case where one or more IVMs occurred before the value.
103+
}
104+
return type;
105+
}
106+
107+
@Override
108+
public void stepIn() {
109+
reader.stepIntoContainer();
110+
type = null;
111+
}
112+
113+
@Override
114+
public void stepOut() {
115+
reader.stepOutOfContainer();
116+
type = null;
117+
}
118+
119+
@Override
120+
public int getDepth() {
121+
return reader.getDepth();
122+
}
123+
124+
@Override
125+
public IonType getType() {
126+
return type;
127+
}
128+
129+
/**
130+
* Prepares a scalar value to be parsed by ensuring it is present in the buffer.
131+
*/
132+
protected void prepareScalar() {
133+
IonCursor.Event event = reader.getCurrentEvent();
134+
if (event == IonCursor.Event.VALUE_READY) {
135+
return;
136+
}
137+
if (event != IonCursor.Event.START_SCALAR) {
138+
// Note: existing tests expect IllegalStateException in this case.
139+
throw new IllegalStateException("Reader is not positioned on a scalar value.");
140+
}
141+
if (reader.fillValue() != IonCursor.Event.VALUE_READY) {
142+
throw new IonException("Unexpected EOF.");
143+
}
144+
}
145+
146+
@Override
147+
public IntegerSize getIntegerSize() {
148+
if (getType() != IonType.INT) {
149+
return null;
150+
}
151+
prepareScalar();
152+
return reader.getIntegerSize();
153+
}
154+
155+
@Override
156+
public boolean isNullValue() {
157+
return pendingIvmSid == -1 && reader.isNullValue();
158+
}
159+
160+
@Override
161+
public boolean isInStruct() {
162+
return reader.isInStruct();
163+
}
164+
165+
@Override
166+
public boolean booleanValue() {
167+
prepareScalar();
168+
return reader.booleanValue();
169+
}
170+
171+
@Override
172+
public int intValue() {
173+
prepareScalar();
174+
return reader.intValue();
175+
}
176+
177+
@Override
178+
public long longValue() {
179+
prepareScalar();
180+
return reader.longValue();
181+
}
182+
183+
@Override
184+
public BigInteger bigIntegerValue() {
185+
prepareScalar();
186+
return reader.bigIntegerValue();
187+
}
188+
189+
@Override
190+
public double doubleValue() {
191+
prepareScalar();
192+
return reader.doubleValue();
193+
}
194+
195+
@Override
196+
public BigDecimal bigDecimalValue() {
197+
prepareScalar();
198+
return reader.bigDecimalValue();
199+
}
200+
201+
@Override
202+
public Decimal decimalValue() {
203+
prepareScalar();
204+
return reader.decimalValue();
205+
}
206+
207+
@Override
208+
public Date dateValue() {
209+
prepareScalar();
210+
return reader.dateValue();
211+
}
212+
213+
@Override
214+
public Timestamp timestampValue() {
215+
prepareScalar();
216+
return reader.timestampValue();
217+
}
218+
219+
@Override
220+
public String stringValue() {
221+
if (pendingIvmSid != -1) {
222+
return getSymbolTable().findKnownSymbol(pendingIvmSid);
223+
}
224+
prepareScalar();
225+
String value;
226+
if (type == IonType.SYMBOL) {
227+
int sid = reader.symbolValueId();
228+
value = getSymbolTable().findKnownSymbol(sid);
229+
if (value == null) {
230+
throw new UnknownSymbolException(sid);
231+
}
232+
} else {
233+
value = reader.stringValue();
234+
}
235+
return value;
236+
}
237+
238+
@Override
239+
public int byteSize() {
240+
prepareScalar();
241+
return reader.byteSize();
242+
}
243+
244+
@Override
245+
public byte[] newBytes() {
246+
prepareScalar();
247+
return reader.newBytes();
248+
}
249+
250+
@Override
251+
public int getBytes(byte[] buffer, int offset, int len) {
252+
prepareScalar();
253+
return reader.getBytes(buffer, offset, len);
254+
}
255+
256+
@Override
257+
public <T> T asFacet(Class<T> facetType) {
258+
return null; // The system-level reader has no facets.
259+
}
260+
261+
@Override
262+
public SymbolTable getSymbolTable() {
263+
// TODO generalize for Ion 1.1
264+
return SharedSymbolTable.getSystemSymbolTable(reader.getIonMajorVersion());
265+
}
266+
267+
@Override
268+
public String[] getTypeAnnotations() {
269+
if (pendingIvmSid != -1 || !reader.hasAnnotations()) {
270+
return _Private_Utils.EMPTY_STRING_ARRAY;
271+
}
272+
int[] annotationIds = reader.getAnnotationIds();
273+
String[] annotations = new String[annotationIds.length];
274+
SymbolTable symbolTable = getSymbolTable();
275+
for (int i = 0; i < annotationIds.length; i++) {
276+
int sid = annotationIds[i];
277+
String annotation = symbolTable.findKnownSymbol(sid);
278+
if (annotation == null) {
279+
throw new UnknownSymbolException(sid);
280+
}
281+
annotations[i] = annotation;
282+
}
283+
return annotations;
284+
}
285+
286+
@Override
287+
public SymbolToken[] getTypeAnnotationSymbols() {
288+
if (pendingIvmSid != -1 || !reader.hasAnnotations()) {
289+
return SymbolToken.EMPTY_ARRAY;
290+
}
291+
int[] annotationIds = reader.getAnnotationIds();
292+
SymbolToken[] annotationSymbolTokens = new SymbolToken[annotationIds.length];
293+
SymbolTable symbolTable = getSymbolTable();
294+
for (int i = 0; i < annotationIds.length; i++) {
295+
int sid = annotationIds[i];
296+
annotationSymbolTokens[i] = new SymbolTokenWithImportLocation(symbolTable.findKnownSymbol(sid), sid, null);
297+
}
298+
return annotationSymbolTokens;
299+
}
300+
301+
@Override
302+
public Iterator<String> iterateTypeAnnotations() {
303+
if (pendingIvmSid != -1 || !reader.hasAnnotations()) {
304+
return _Private_Utils.emptyIterator();
305+
}
306+
return _Private_Utils.stringIterator(getTypeAnnotations());
307+
}
308+
309+
@Override
310+
public int getFieldId() {
311+
return reader.getFieldId();
312+
}
313+
314+
@Override
315+
public String getFieldName() {
316+
int sid = reader.getFieldId();
317+
if (sid < 0) {
318+
return null;
319+
}
320+
String name = getSymbolTable().findKnownSymbol(sid);
321+
if (name == null) {
322+
throw new UnknownSymbolException(sid);
323+
}
324+
return name;
325+
}
326+
327+
@Override
328+
public SymbolToken getFieldNameSymbol() {
329+
int sid = reader.getFieldId();
330+
if (sid < 0) {
331+
return null;
332+
}
333+
return new SymbolTokenWithImportLocation(getSymbolTable().findKnownSymbol(sid), sid, null);
334+
}
335+
336+
@Override
337+
public SymbolToken symbolValue() {
338+
int sid;
339+
if (pendingIvmSid != -1) {
340+
sid = pendingIvmSid;
341+
} else {
342+
prepareScalar();
343+
sid = reader.symbolValueId();
344+
}
345+
return new SymbolTokenWithImportLocation(getSymbolTable().findKnownSymbol(sid), sid, null);
346+
}
347+
348+
@Override
349+
public void close() throws IOException {
350+
reader.close();
351+
}
352+
}

0 commit comments

Comments
 (0)