1
2
3
4
5
6
7
8
9
10
11
12
13
14 package com.addthis.codec.config;
15
16 import java.io.IOException;
17 import java.io.OutputStream;
18
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21
22 import com.fasterxml.jackson.core.Base64Variant;
23 import com.fasterxml.jackson.core.JsonLocation;
24 import com.fasterxml.jackson.core.JsonParseException;
25 import com.fasterxml.jackson.core.JsonParser;
26 import com.fasterxml.jackson.core.JsonStreamContext;
27 import com.fasterxml.jackson.core.JsonToken;
28 import com.fasterxml.jackson.core.ObjectCodec;
29 import com.fasterxml.jackson.core.Version;
30 import com.fasterxml.jackson.core.base.ParserMinimalBase;
31 import com.fasterxml.jackson.databind.JsonNode;
32 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
33 import com.typesafe.config.ConfigList;
34 import com.typesafe.config.ConfigObject;
35 import com.typesafe.config.ConfigOrigin;
36 import com.typesafe.config.ConfigValue;
37 import com.typesafe.config.ConfigValueType;
38
39 import static com.typesafe.config.ConfigValueType.LIST;
40 import static com.typesafe.config.ConfigValueType.OBJECT;
41
42 public class ConfigTraversingParser extends ParserMinimalBase {
43
44
45
46
47
48
49 protected ObjectCodec _objectCodec;
50
51
52
53
54 protected ConfigNodeCursor _nodeCursor;
55
56
57
58
59
60
61
62
63
64
65
66 protected JsonToken _nextToken;
67
68
69
70
71
72 protected boolean _startContainer;
73
74
75
76
77
78
79 protected boolean _closed;
80
81 protected ConfigValue currentConfig;
82
83
84
85
86
87
88
89 public ConfigTraversingParser(ConfigValue n) { this(n, null); }
90
91 public ConfigTraversingParser(ConfigValue n, ObjectCodec codec) {
92 super(0);
93 _objectCodec = codec;
94 currentConfig = n;
95 if (n.valueType() == LIST) {
96 _nextToken = JsonToken.START_ARRAY;
97 _nodeCursor = new ConfigNodeCursor.Array((ConfigList) n, null);
98 } else if (n.valueType() == OBJECT) {
99 _nextToken = JsonToken.START_OBJECT;
100 _nodeCursor = new ConfigNodeCursor.Object((ConfigObject) n, null);
101 } else {
102 _nodeCursor = new ConfigNodeCursor.RootValue(n, null);
103 }
104 }
105
106 @Override
107 public void setCodec(ObjectCodec c) {
108 _objectCodec = c;
109 }
110
111 @Override
112 public ObjectCodec getCodec() {
113 return _objectCodec;
114 }
115
116 @Override
117 public Version version() {
118 return Version.unknownVersion();
119 }
120
121
122
123
124
125
126
127 @Override
128 public void close() {
129 if (!_closed) {
130 _closed = true;
131 _nodeCursor = null;
132 _currToken = null;
133 }
134 }
135
136
137
138
139
140
141
142 public ConfigValue currentConfig() {
143 if (_nextToken != null) {
144
145 return null;
146 }
147 return currentConfig;
148 }
149
150 @Override
151 public JsonToken nextToken() throws IOException, JsonParseException {
152 if (_nextToken != null) {
153 _currToken = _nextToken;
154 _nextToken = null;
155 return _currToken;
156 }
157
158 if (_startContainer) {
159 _startContainer = false;
160
161 if (!_nodeCursor.currentHasChildren()) {
162 _currToken = (_currToken == JsonToken.START_OBJECT) ?
163 JsonToken.END_OBJECT : JsonToken.END_ARRAY;
164 return _currToken;
165 }
166 _nodeCursor = _nodeCursor.iterateChildren();
167 _currToken = _nodeCursor.nextToken();
168 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
169 _startContainer = true;
170 }
171 currentConfig = currentNode();
172 return _currToken;
173 }
174
175 if (_nodeCursor == null) {
176 _closed = true;
177 currentConfig = null;
178 return null;
179 }
180
181 _currToken = _nodeCursor.nextToken();
182 if (_currToken != null) {
183 currentConfig = currentNode();
184 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
185 _startContainer = true;
186 }
187 return _currToken;
188 }
189
190 _currToken = _nodeCursor.endToken();
191 _nodeCursor = _nodeCursor.getParent();
192 currentConfig = currentNode();
193 return _currToken;
194 }
195
196
197
198
199 @Override
200 public JsonParser skipChildren() throws IOException, JsonParseException {
201 if (_currToken == JsonToken.START_OBJECT) {
202 _startContainer = false;
203 _currToken = JsonToken.END_OBJECT;
204 } else if (_currToken == JsonToken.START_ARRAY) {
205 _startContainer = false;
206 _currToken = JsonToken.END_ARRAY;
207 }
208 return this;
209 }
210
211 @Override
212 public boolean isClosed() {
213 return _closed;
214 }
215
216
217
218
219
220
221
222 @Override
223 public String getCurrentName() {
224 if (_nodeCursor == null) {
225 return null;
226 } else {
227 return _nodeCursor.getCurrentName();
228 }
229 }
230
231 @Override
232 public void overrideCurrentName(String name) {
233 if (_nodeCursor != null) {
234 _nodeCursor.overrideCurrentName(name);
235 }
236 }
237
238 @Override
239 public JsonStreamContext getParsingContext() {
240 return _nodeCursor;
241 }
242
243 @Override
244 public JsonLocation getTokenLocation() {
245 ConfigValue current = currentConfig();
246 if (current == null) {
247 return JsonLocation.NA;
248 }
249 ConfigOrigin nodeOrigin = current.origin();
250 return new JsonLocation(current, -1, nodeOrigin.lineNumber(), -1);
251 }
252
253 @Override
254 public JsonLocation getCurrentLocation() {
255 return getTokenLocation();
256 }
257
258
259
260
261
262
263
264 @Override
265 public String getText() {
266 if (_closed) {
267 return null;
268 }
269
270 switch (_currToken) {
271 case FIELD_NAME:
272 return _nodeCursor.getCurrentName();
273 case VALUE_STRING:
274 return (String) currentNode().unwrapped();
275 case VALUE_NUMBER_INT:
276 case VALUE_NUMBER_FLOAT:
277 return String.valueOf(currentNode().unwrapped());
278 case VALUE_EMBEDDED_OBJECT:
279
280 default:
281 if (_currToken == null) {
282 return null;
283 } else {
284 return _currToken.asString();
285 }
286 }
287 }
288
289 @Override
290 public char[] getTextCharacters() throws IOException, JsonParseException {
291 return getText().toCharArray();
292 }
293
294 @Override
295 public int getTextLength() throws IOException, JsonParseException {
296 return getText().length();
297 }
298
299 @Override
300 public int getTextOffset() throws IOException, JsonParseException {
301 return 0;
302 }
303
304 @Override
305 public boolean hasTextCharacters() {
306
307 return false;
308 }
309
310
311
312
313
314
315
316
317
318 @Override
319 public NumberType getNumberType() throws IOException, JsonParseException {
320 JsonNode n = currentNumericNode();
321 return (n == null) ? null : n.numberType();
322 }
323
324 @Override
325 public BigInteger getBigIntegerValue() throws IOException, JsonParseException {
326 return currentNumericNode().bigIntegerValue();
327 }
328
329 @Override
330 public BigDecimal getDecimalValue() throws IOException, JsonParseException {
331 return currentNumericNode().decimalValue();
332 }
333
334 @Override
335 public double getDoubleValue() throws IOException, JsonParseException {
336 return currentNumericNode().doubleValue();
337 }
338
339 @Override
340 public float getFloatValue() throws IOException, JsonParseException {
341 return (float) currentNumericNode().doubleValue();
342 }
343
344 @Override
345 public long getLongValue() throws IOException, JsonParseException {
346 return currentNumericNode().longValue();
347 }
348
349 @Override
350 public int getIntValue() throws IOException, JsonParseException {
351 JsonNode numericNode = currentNumericNode();
352 if (numericNode.canConvertToInt()) {
353 return currentNumericNode().intValue();
354 }
355 throw _constructError("Numeric value ("+getText()+") out of range of Java short");
356 }
357
358 @Override
359 public Number getNumberValue() throws IOException, JsonParseException {
360 return currentNumericNode().numberValue();
361 }
362
363 @Override
364 public Object getEmbeddedObject() {
365 return null;
366 }
367
368
369
370
371
372
373
374 @Override
375 public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException {
376
377 return null;
378 }
379
380
381 @Override
382 public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException, JsonParseException {
383 return 0;
384 }
385
386
387
388
389
390
391
392 protected ConfigValue currentNode() {
393 if (_closed || _nodeCursor == null) {
394 return null;
395 }
396 return _nodeCursor.currentNode();
397 }
398
399 protected JsonNode currentNumericNode() throws JsonParseException {
400 ConfigValue configValue = currentNode();
401 if ((configValue == null) || (configValue.valueType() != ConfigValueType.NUMBER)) {
402 JsonToken t = (configValue == null) ? null : ConfigNodeCursor.forConfigValue(configValue);
403 throw _constructError("Current token ("+t+") not numeric, can not use numeric value accessors");
404 }
405 Number value = (Number) configValue.unwrapped();
406 if (value instanceof Double) {
407 return JsonNodeFactory.instance.numberNode((Double) value);
408 }
409 if (value instanceof Long) {
410 return JsonNodeFactory.instance.numberNode((Long) value);
411 }
412 if (value instanceof Integer) {
413 return JsonNodeFactory.instance.numberNode((Integer) value);
414 }
415
416 throw _constructError(value.getClass() + " is not a supported numeric config type");
417 }
418
419 @Override
420 protected void _handleEOF() throws JsonParseException {
421 _throwInternal();
422 }
423 }