1
2
3
4
5
6
7
8
9
10
11
12
13
14 package com.addthis.codec.config;
15
16 import javax.annotation.Nullable;
17
18 import java.util.Iterator;
19 import java.util.Map;
20
21 import com.fasterxml.jackson.core.JsonStreamContext;
22 import com.fasterxml.jackson.core.JsonToken;
23 import com.typesafe.config.ConfigList;
24 import com.typesafe.config.ConfigObject;
25 import com.typesafe.config.ConfigValue;
26 import com.typesafe.config.ConfigValueType;
27
28 import static com.typesafe.config.ConfigValueType.LIST;
29 import static com.typesafe.config.ConfigValueType.OBJECT;
30
31 abstract class ConfigNodeCursor extends JsonStreamContext {
32
33
34
35
36 protected final ConfigNodeCursor _parent;
37
38
39
40
41 @Nullable protected String _currentName;
42
43 public ConfigNodeCursor(int contextType, ConfigNodeCursor p) {
44 super();
45 _type = contextType;
46 _index = -1;
47 _parent = p;
48 }
49
50
51
52
53
54
55
56
57 @Override
58 public final ConfigNodeCursor getParent() { return _parent; }
59
60 @Override
61 @Nullable
62 public final String getCurrentName() {
63 return _currentName;
64 }
65
66
67
68
69 public void overrideCurrentName(String name) {
70 _currentName = name;
71 }
72
73
74
75
76
77
78
79 public abstract JsonToken nextToken();
80
81 public abstract JsonToken nextValue();
82
83 public abstract JsonToken endToken();
84
85 public abstract ConfigValue currentNode();
86
87 public abstract boolean currentHasChildren();
88
89
90
91
92
93 public final ConfigNodeCursor iterateChildren() {
94 ConfigValue n = currentNode();
95 if (n == null) throw new IllegalStateException("No current node");
96 if (n.valueType() == LIST) {
97 return new Array((ConfigList) n, this);
98 }
99 if (n.valueType() == OBJECT) {
100 return new Object((ConfigObject) n, this);
101 }
102 throw new IllegalStateException("Current node of type " + n.getClass().getName());
103 }
104
105 static JsonToken forConfigValue(ConfigValue configValue) {
106 ConfigValueType valueType = configValue.valueType();
107 switch (valueType) {
108 case NUMBER:
109 if (configValue.unwrapped() instanceof Double) {
110 return JsonToken.VALUE_NUMBER_FLOAT;
111 } else {
112 return JsonToken.VALUE_NUMBER_INT;
113 }
114 case BOOLEAN:
115 if (configValue.unwrapped().equals(Boolean.TRUE)) {
116 return JsonToken.VALUE_TRUE;
117 } else {
118 return JsonToken.VALUE_FALSE;
119 }
120 case NULL:
121 return JsonToken.VALUE_NULL;
122 case STRING:
123 return JsonToken.VALUE_STRING;
124 case OBJECT:
125 return JsonToken.START_OBJECT;
126 case LIST:
127 return JsonToken.START_ARRAY;
128 default:
129
130 throw new IllegalArgumentException(valueType.name() + " is not a supported ConfigValueType");
131 }
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145 protected static final class RootValue extends ConfigNodeCursor {
146 @Nullable private ConfigValue _node;
147
148 private boolean _done = false;
149
150 public RootValue(ConfigValue n, ConfigNodeCursor p) {
151 super(JsonStreamContext.TYPE_ROOT, p);
152 _node = n;
153 }
154
155 @Override
156 public void overrideCurrentName(String name) {}
157
158 @Override
159 @Nullable
160 public JsonToken nextToken() {
161 if (!_done) {
162 _done = true;
163 return forConfigValue(_node);
164 }
165 _node = null;
166 return null;
167 }
168
169 @Override
170 @Nullable
171 public JsonToken nextValue() { return nextToken(); }
172
173 @Override
174 @Nullable
175 public JsonToken endToken() { return null; }
176
177 @Override
178 public ConfigValue currentNode() { return _node; }
179
180 @Override
181 public boolean currentHasChildren() { return false; }
182 }
183
184
185
186
187 protected static final class Array extends ConfigNodeCursor {
188 private final Iterator<ConfigValue> _contents;
189
190 @Nullable private ConfigValue _currentNode;
191
192 public Array(ConfigList n, ConfigNodeCursor p) {
193 super(JsonStreamContext.TYPE_ARRAY, p);
194 _contents = n.iterator();
195 }
196
197 @Override
198 @Nullable
199 public JsonToken nextToken() {
200 if (!_contents.hasNext()) {
201 _currentNode = null;
202 return null;
203 }
204 _currentNode = _contents.next();
205 return forConfigValue(_currentNode);
206 }
207
208 @Override
209 public JsonToken nextValue() { return nextToken(); }
210
211 @Override
212 public JsonToken endToken() { return JsonToken.END_ARRAY; }
213
214 @Override
215 public ConfigValue currentNode() { return _currentNode; }
216
217 @Override
218 public boolean currentHasChildren() {
219
220 ConfigValue currentValue = currentNode();
221 if (currentValue.valueType() == ConfigValueType.LIST) {
222 return !((ConfigList) currentValue).isEmpty();
223 } else {
224 return !((ConfigObject) currentValue).isEmpty();
225 }
226 }
227 }
228
229
230
231
232 protected static final class Object extends ConfigNodeCursor {
233 private final Iterator<Map.Entry<String, ConfigValue>> _contents;
234
235 @Nullable private Map.Entry<String, ConfigValue> _current;
236 private boolean _needEntry;
237
238 public Object(ConfigObject n, ConfigNodeCursor p) {
239 super(JsonStreamContext.TYPE_OBJECT, p);
240 _contents = n.entrySet().iterator();
241 _needEntry = true;
242 }
243
244 @Override
245 @Nullable
246 public JsonToken nextToken() {
247
248 if (_needEntry) {
249 if (!_contents.hasNext()) {
250 _currentName = null;
251 _current = null;
252 return null;
253 }
254 _needEntry = false;
255 _current = _contents.next();
256 _currentName = (_current == null) ? null : _current.getKey();
257 return JsonToken.FIELD_NAME;
258 }
259 _needEntry = true;
260 return forConfigValue(_current.getValue());
261 }
262
263 @Override
264 public JsonToken nextValue() {
265 JsonToken t = nextToken();
266 if (t == JsonToken.FIELD_NAME) {
267 t = nextToken();
268 }
269 return t;
270 }
271
272 @Override
273 public JsonToken endToken() { return JsonToken.END_OBJECT; }
274
275 @Override
276 public ConfigValue currentNode() {
277 if (_current == null) {
278 return null;
279 } else {
280 return _current.getValue();
281 }
282 }
283
284 @Override
285 public boolean currentHasChildren() {
286
287 ConfigValue currentValue = currentNode();
288 if (currentValue.valueType() == ConfigValueType.LIST) {
289 return !((ConfigList) currentValue).isEmpty();
290 } else {
291 return !((ConfigObject) currentValue).isEmpty();
292 }
293 }
294 }
295 }