blob: e4f4adb00de0132378f9e010aa95d0de9c04b565 [file] [log] [blame]
Tatu Salorantadfd69092015-04-09 22:29:34 -07001package com.fasterxml.jackson.core.filter;
2
3import java.io.IOException;
4import java.io.OutputStream;
5import java.math.BigDecimal;
6import java.math.BigInteger;
7
8import com.fasterxml.jackson.core.*;
9import com.fasterxml.jackson.core.util.JsonParserDelegate;
10
Tatu Salorantab46e0372015-04-14 21:43:03 -070011import static com.fasterxml.jackson.core.JsonTokenId.*;
12
Tatu Salorantadfd69092015-04-09 22:29:34 -070013/**
14 * Specialized {@link JsonParserDelegate} that allows use of
15 * {@link TokenFilter} for outputting a subset of content that
16 * is visible to caller
17 *
18 * @since 2.6
19 */
20public class FilteringParserDelegate extends JsonParserDelegate
21{
22 /*
23 /**********************************************************
24 /* Configuration
25 /**********************************************************
26 */
27
28 /**
29 * Object consulted to determine whether to write parts of content generator
30 * is asked to write or not.
31 */
32 protected TokenFilter rootFilter;
33
34 /**
35 * Flag that determines whether filtering will continue after the first
36 * match is indicated or not: if `false`, output is based on just the first
37 * full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more
38 * checks are made; if `true` then filtering will be applied as necessary
39 * until end of content.
40 */
41 protected boolean _allowMultipleMatches;
42
43 /**
44 * Flag that determines whether path leading up to included content should
45 * also be automatically included or not. If `false`, no path inclusion is
46 * done and only explicitly included entries are output; if `true` then
47 * path from main level down to match is also included as necessary.
48 */
49 protected boolean _includePath;
50
51 /*
52 /**********************************************************
53 /* State
54 /**********************************************************
55 */
56
57 /**
58 * Last token retrieved via {@link #nextToken}, if any.
59 * Null before the first call to <code>nextToken()</code>,
60 * as well as if token has been explicitly cleared
61 */
62 protected JsonToken _currToken;
63
64 /**
65 * Last cleared token, if any: that is, value that was in
66 * effect when {@link #clearCurrentToken} was called.
67 */
68 protected JsonToken _lastClearedToken;
69
70 /**
Cowtowncoder00900312015-04-14 16:02:33 -070071 * During traversal this is the actual "open" parse tree, which sometimes
72 * is the same as {@link #_exposedContext}, and at other times is ahead
73 * of it. Note that this context is never null.
Tatu Salorantadfd69092015-04-09 22:29:34 -070074 */
Cowtowncoder00900312015-04-14 16:02:33 -070075 protected TokenFilterContext _headContext;
Tatu Salorantadfd69092015-04-09 22:29:34 -070076
77 /**
Cowtowncoder00900312015-04-14 16:02:33 -070078 * In cases where {@link #_headContext} is "ahead" of context exposed to
79 * caller, this context points to what is currently exposed to caller.
80 * When the two are in sync, this context reference will be <code>null</code>.
81 */
82 protected TokenFilterContext _exposedContext;
Cowtowncoder9a797fb2015-04-14 16:12:12 -070083
Cowtowncoder00900312015-04-14 16:02:33 -070084 /**
Tatu Salorantadfd69092015-04-09 22:29:34 -070085 * State that applies to the item within container, used where applicable.
86 * Specifically used to pass inclusion state between property name and
87 * property, and also used for array elements.
88 */
89 protected TokenFilter _itemFilter;
90
91 /**
92 * Number of tokens for which {@link TokenFilter#INCLUDE_ALL}
Tatu Saloranta5a5d1192015-04-13 23:05:07 -070093 * has been returned.
Tatu Salorantadfd69092015-04-09 22:29:34 -070094 */
95 protected int _matchCount;
96
97 /*
98 /**********************************************************
99 /* Construction, initialization
100 /**********************************************************
101 */
102
103 public FilteringParserDelegate(JsonParser p, TokenFilter f,
104 boolean includePath, boolean allowMultipleMatches)
105 {
106 super(p);
107 rootFilter = f;
108 // and this is the currently active filter for root values
109 _itemFilter = f;
Cowtowncoder00900312015-04-14 16:02:33 -0700110 _headContext = TokenFilterContext.createRootContext(f);
Tatu Salorantadfd69092015-04-09 22:29:34 -0700111 _includePath = includePath;
112 _allowMultipleMatches = allowMultipleMatches;
113 }
114
115 /*
116 /**********************************************************
117 /* Extended API
118 /**********************************************************
119 */
120
121 public TokenFilter getFilter() { return rootFilter; }
122
123 /**
124 * Accessor for finding number of matches, where specific token and sub-tree
125 * starting (if structured type) are passed.
126 */
127 public int getMatchCount() {
128 return _matchCount;
129 }
130
131 /*
132 /**********************************************************
133 /* Public API, token accessors
134 /**********************************************************
135 */
136
137 @Override public JsonToken getCurrentToken() { return _currToken; }
138
139 @Override public final int getCurrentTokenId() {
140 final JsonToken t = _currToken;
141 return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
142 }
143
144 @Override public boolean hasCurrentToken() { return _currToken != null; }
145 @Override public boolean hasTokenId(int id) {
146 final JsonToken t = _currToken;
147 if (t == null) {
148 return (JsonTokenId.ID_NO_TOKEN == id);
149 }
150 return t.id() == id;
151 }
152
153 @Override public final boolean hasToken(JsonToken t) {
154 return (_currToken == t);
155 }
156
157 @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
158 @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
159
160 @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700161
Cowtowncoder00900312015-04-14 16:02:33 -0700162 @Override
163 public JsonStreamContext getParsingContext() {
164 return _filterContext();
165 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700166
Cowtowncoder00900312015-04-14 16:02:33 -0700167 // !!! TODO: Verify it works as expected: copied from standard JSON parser impl
168 @Override
169 public String getCurrentName() throws IOException {
170 JsonStreamContext ctxt = _filterContext();
171 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
172 JsonStreamContext parent = ctxt.getParent();
173 return (parent == null) ? null : parent.getCurrentName();
174 }
175 return ctxt.getCurrentName();
176 }
177
Tatu Salorantadfd69092015-04-09 22:29:34 -0700178 /*
179 /**********************************************************
180 /* Public API, token state overrides
181 /**********************************************************
182 */
183
184 @Override
185 public void clearCurrentToken() {
186 if (_currToken != null) {
187 _lastClearedToken = _currToken;
188 _currToken = null;
189 }
190 }
191
192 @Override
193 public JsonToken getLastClearedToken() { return _lastClearedToken; }
194
Tatu Salorantadfd69092015-04-09 22:29:34 -0700195 @Override
Cowtowncoder00900312015-04-14 16:02:33 -0700196 public void overrideCurrentName(String name) {
197 /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so,
198 * what to do with it... Delegation won't work for sure, so let's for
199 * now throw an exception
200 */
201 throw new UnsupportedOperationException("Can not currently override name during filtering read");
202 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700203
204 /*
205 /**********************************************************
206 /* Public API, traversal
207 /**********************************************************
208 */
209
Cowtowncoder00900312015-04-14 16:02:33 -0700210 @Override
211 public JsonToken nextToken() throws IOException
212 {
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700213 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700214 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700215
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700216 if (ctxt != null) {
217 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700218 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700219 if (t != null) {
220 _currToken = t;
221 return t;
222 }
223 // all done with buffered stuff?
224 if (ctxt == _headContext) {
225 _exposedContext = null;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700226 // Almost! Most likely still have the current token;
227 // with the sole exception of
228 t = delegate.getCurrentToken();
229 if (t != JsonToken.FIELD_NAME) {
230 _currToken = t;
231 return t;
232 }
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700233 break;
234 }
235 // If not, traverse down the context chain
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700236 ctxt = _exposedContext.findChildOf(ctxt);
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700237 _exposedContext = ctxt;
238 if (ctxt == null) { // should never occur
239 throw _constructError("Unexpected problem: chain of filtered context broken");
240 }
241 }
242 }
243
Tatu Salorantab46e0372015-04-14 21:43:03 -0700244 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700245 JsonToken t = delegate.nextToken();
246 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700247 // no strict need to close, since we have no state here
248 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700249 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700250
Tatu Salorantab46e0372015-04-14 21:43:03 -0700251 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700252 TokenFilter f;
253
254 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700255 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700256 f = _itemFilter;
257 if (f == TokenFilter.INCLUDE_ALL) {
258 _headContext = _headContext.createChildArrayContext(f, true);
259 return (_currToken = t);
260 }
261 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700262 delegate.skipChildren();
263 break;
264 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700265 // Otherwise still iffy, need to check
266 f = _headContext.checkValue(f);
267 if (f == null) {
268 delegate.skipChildren();
269 break;
270 }
271 if (f != TokenFilter.INCLUDE_ALL) {
272 f = f.filterStartArray();
273 }
274 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700275 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700276 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700277 return (_currToken = t);
278 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700279 _headContext = _headContext.createChildArrayContext(f, false);
280
281 // Also: only need buffering if parent path to be included
282 if (_includePath) {
283 return _nextTokenWithBuffering(_headContext);
284 }
285 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700286
287 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700288 f = _itemFilter;
289 if (f == TokenFilter.INCLUDE_ALL) {
290 _headContext = _headContext.createChildObjectContext(f, true);
291 return (_currToken = t);
292 }
293 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700294 delegate.skipChildren();
295 break;
296 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700297 // Otherwise still iffy, need to check
298 f = _headContext.checkValue(f);
299 if (f == null) {
300 delegate.skipChildren();
301 break;
302 }
303 if (f != TokenFilter.INCLUDE_ALL) {
304 f = f.filterStartObject();
305 }
306 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700307 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700308 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700309 return (_currToken = t);
310 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700311 _headContext = _headContext.createChildObjectContext(f, false);
312 // Also: only need buffering if parent path to be included
313 if (_includePath) {
314 return _nextTokenWithBuffering(_headContext);
315 }
316 // note: inclusion of surrounding Object handled separately via
317 // FIELD_NAME
318 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700319
320 case ID_END_ARRAY:
321 case ID_END_OBJECT:
322 {
323 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700324 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700325 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
326 f.filterFinishArray();
327 }
328 _headContext = _headContext.getParent();
329 _itemFilter = _headContext.getFilter();
330 if (returnEnd) {
331 return (_currToken = t);
332 }
333 }
334 break;
335
336 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700337 {
338 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700339 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700340 f = _headContext.setFieldName(name);
341 if (f == TokenFilter.INCLUDE_ALL) {
342 _itemFilter = f;
Cowtowncoder654acf02015-04-16 15:30:43 -0700343 // Minor twist here: if parent NOT included, may need to induce output of
344 // surrounding START_OBJECT/END_OBJECT
345 if (!_includePath && !_headContext.isStartHandled()) {
346 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
347 _exposedContext = _headContext;
348 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700349 return (_currToken = t);
350 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700351 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700352 delegate.nextToken();
353 delegate.skipChildren();
354 break;
355 }
356 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700357 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700358 delegate.nextToken();
359 delegate.skipChildren();
360 break;
361 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700362 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700363 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700364 return (_currToken = t);
365 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700366 if (_includePath) {
367 return _nextTokenWithBuffering(_headContext);
368 }
369 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700370 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700371
372 default: // scalar value
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700373 if (_itemFilter == TokenFilter.INCLUDE_ALL) {
374 return (_currToken = t);
375 }
376 // Otherwise not included (leaves must be explicitly included)
377 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700378 }
379
380 // We get here if token was not yet found; offlined handling
381 return _nextToken2();
382 }
383
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700384 /**
385 * Offlined handling for cases where there was no buffered token to
386 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700387 * at least not yet, but where we have not yet established that
388 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700389 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700390 protected final JsonToken _nextToken2() throws IOException
391 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700392 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700393 while (true) {
394 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700395 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700396 return (_currToken = t);
397 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700398 TokenFilter f;
399
400 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700401 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700402 f = _itemFilter;
403 if (f == TokenFilter.INCLUDE_ALL) {
404 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700405 return (_currToken = t);
406 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700407 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700408 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700409 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700410 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700411 // Otherwise still iffy, need to check
412 f = _headContext.checkValue(f);
413 if (f == null) {
414 delegate.skipChildren();
415 continue main_loop;
416 }
417 if (f != TokenFilter.INCLUDE_ALL) {
418 f = f.filterStartArray();
419 }
420 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700421 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700422 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700423 return (_currToken = t);
424 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700425 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700426 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700427 if (_includePath) {
428 return _nextTokenWithBuffering(_headContext);
429 }
430 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700431
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700432 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700433 f = _itemFilter;
434 if (f == TokenFilter.INCLUDE_ALL) {
435 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700436 return (_currToken = t);
437 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700438 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700439 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700440 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700441 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700442 // Otherwise still iffy, need to check
443 f = _headContext.checkValue(f);
444 if (f == null) {
445 delegate.skipChildren();
446 continue main_loop;
447 }
448 if (f != TokenFilter.INCLUDE_ALL) {
449 f = f.filterStartObject();
450 }
451 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700452 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700453 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700454 return (_currToken = t);
455 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700456 _headContext = _headContext.createChildObjectContext(f, false);
457 if (_includePath) {
458 return _nextTokenWithBuffering(_headContext);
459 }
460 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700461
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700462 case ID_END_ARRAY:
463 case ID_END_OBJECT:
464 {
465 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700466 f = _headContext.getFilter();
467 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
468 f.filterFinishArray();
469 }
470 _headContext = _headContext.getParent();
471 _itemFilter = _headContext.getFilter();
472 if (returnEnd) {
473 return (_currToken = t);
474 }
475 }
476 continue main_loop;
477
478 case ID_FIELD_NAME:
479 {
480 final String name = delegate.getCurrentName();
481 f = _headContext.setFieldName(name);
482 if (f == TokenFilter.INCLUDE_ALL) {
483 _itemFilter = f;
484 return (_currToken = t);
485 }
486 if (f == null) { // filter out the value
487 delegate.nextToken();
488 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700489 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700490 }
491 f = f.includeProperty(name);
492 if (f == null) { // filter out the value
493 delegate.nextToken();
494 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700495 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700496 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700497 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700498 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700499 return (_currToken = t);
500 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700501 if (_includePath) {
502 return _nextTokenWithBuffering(_headContext);
503 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700504 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700505 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700506
507 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700508 f = _itemFilter;
509 if ((f == TokenFilter.INCLUDE_ALL)
510 || ((f != null) && f.includeValue(delegate))) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700511 return (_currToken = t);
512 }
513 // Otherwise not included (leaves must be explicitly included)
514 continue main_loop;
515 }
516 }
517 }
518
519 /**
520 * Method called when a new potentially included context is found.
521 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700522 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
523 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700524 {
Cowtowncoder654acf02015-04-16 15:30:43 -0700525 _exposedContext = buffRoot;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700526
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700527 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700528 while (true) {
529 JsonToken t = delegate.nextToken();
530
531 if (t == null) { // is this even legal?
532 return (_currToken = t);
533 }
534 TokenFilter f;
535
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700536 // One simplification here: we know for a fact that the item filter is
537 // neither null nor 'include all', for most cases; the only exception
538 // being FIELD_NAME handling
539
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700540 switch (t.id()) {
541 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700542 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700543 if (f == null) {
544 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700545 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700546 }
547 if (f != TokenFilter.INCLUDE_ALL) {
548 f = f.filterStartArray();
549 }
550 _itemFilter = f;
551 _headContext = _headContext.createChildArrayContext(f, true);
552 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700553 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700554 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700555 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700556
557 case ID_START_OBJECT:
558 f = _itemFilter;
559 if (f == TokenFilter.INCLUDE_ALL) {
560 _headContext = _headContext.createChildObjectContext(f, true);
561 return (_currToken = t);
562 }
563 if (f == null) { // does this occur?
564 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700565 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700566 }
567 // Otherwise still iffy, need to check
568 f = _headContext.checkValue(f);
569 if (f == null) {
570 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700571 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700572 }
573 if (f != TokenFilter.INCLUDE_ALL) {
574 f = f.filterStartObject();
575 }
576 _itemFilter = f;
577 _headContext = _headContext.createChildObjectContext(f, true);
578 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700579 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700580 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700581 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700582
583 case ID_END_ARRAY:
584 case ID_END_OBJECT:
585 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700586 // Unlike with other loops, here we know that content was NOT
587 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700588 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700589 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
590 f.filterFinishArray();
591 }
592 _headContext = _headContext.getParent();
593 _itemFilter = _headContext.getFilter();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700594
595 if (_headContext == buffRoot) {
596 // !!! TBI
597 throw _constructError("Internal error: end of possible inclusion -- TBI");
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700598 }
599 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700600 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700601
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700602 case ID_FIELD_NAME:
603 {
604 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700605 f = _headContext.setFieldName(name);
606 if (f == TokenFilter.INCLUDE_ALL) {
607 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700608 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700609 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700610 if (f == null) { // filter out the value
611 delegate.nextToken();
612 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700613 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700614 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700615 f = f.includeProperty(name);
616 if (f == null) { // filter out the value
617 delegate.nextToken();
618 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700619 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700620 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700621 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700622 if (f == TokenFilter.INCLUDE_ALL) {
623 return _nextBuffered();
624 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700625 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700626 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700627
628 default: // scalar value
629 if (_itemFilter == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700630 return _nextBuffered();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700631 }
632 // Otherwise not included (leaves must be explicitly included)
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700633 }
634 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700635 }
636
637 private JsonToken _nextBuffered() throws IOException
638 {
639 TokenFilterContext ctxt = _exposedContext;
640 JsonToken t = ctxt.nextTokenToRead();
641 if (t != null) {
642 _currToken = t;
643 return t;
644 }
645 while (true) {
646 // all done with buffered stuff?
647 if (ctxt == _headContext) {
648 throw _constructError("Internal error: failed to locate expected buffered tokens");
649 /*
650 _exposedContext = null;
651 break;
652 */
653 }
654 // If not, traverse down the context chain
655 ctxt = _exposedContext.findChildOf(ctxt);
656 _exposedContext = ctxt;
657 if (ctxt == null) { // should never occur
658 throw _constructError("Unexpected problem: chain of filtered context broken");
659 }
660 t = _exposedContext.nextTokenToRead();
661 if (t != null) {
662 _currToken = t;
663 return t;
664 }
665 }
666 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700667
Tatu Salorantadfd69092015-04-09 22:29:34 -0700668 @Override
669 public JsonToken nextValue() throws IOException {
670 // Re-implemented same as ParserMinimalBase:
671 JsonToken t = nextToken();
672 if (t == JsonToken.FIELD_NAME) {
673 t = nextToken();
674 }
675 return t;
676 }
677
678 /**
679 * Need to override, re-implement similar to how method defined in
680 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
681 * state correct here.
682 */
683 @Override
684 public JsonParser skipChildren() throws IOException
685 {
Cowtowncoder00900312015-04-14 16:02:33 -0700686 if ((_currToken != JsonToken.START_OBJECT)
687 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700688 return this;
689 }
690 int open = 1;
691
692 // Since proper matching of start/end markers is handled
693 // by nextToken(), we'll just count nesting levels here
694 while (true) {
695 JsonToken t = nextToken();
696 if (t == null) { // not ideal but for now, just return
697 return this;
698 }
699 if (t.isStructStart()) {
700 ++open;
701 } else if (t.isStructEnd()) {
702 if (--open == 0) {
703 return this;
704 }
705 }
706 }
707 }
708
709 /*
710 /**********************************************************
711 /* Public API, access to token information, text
712 /**********************************************************
713 */
714
715 @Override public String getText() throws IOException { return delegate.getText(); }
716 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
717 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
718 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
719 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
720
721 /*
722 /**********************************************************
723 /* Public API, access to token information, numeric
724 /**********************************************************
725 */
726
727 @Override
728 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
729
730 @Override
731 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
732
733 @Override
734 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
735
736 @Override
737 public short getShortValue() throws IOException { return delegate.getShortValue(); }
738
739 @Override
740 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
741
742 @Override
743 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
744
745 @Override
746 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
747
748 @Override
749 public int getIntValue() throws IOException { return delegate.getIntValue(); }
750
751 @Override
752 public long getLongValue() throws IOException { return delegate.getLongValue(); }
753
754 @Override
755 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
756
757 @Override
758 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
759
760 /*
761 /**********************************************************
762 /* Public API, access to token information, coercion/conversion
763 /**********************************************************
764 */
765
766 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
767 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
768 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
769 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
770 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
771 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
772 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
773 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
774 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
775 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
776
777 /*
778 /**********************************************************
779 /* Public API, access to token values, other
780 /**********************************************************
781 */
782
783 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
784 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
785 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
786 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700787
788 /*
789 /**********************************************************
790 /* Internal helper methods
791 /**********************************************************
792 */
793
794 protected JsonStreamContext _filterContext() {
795 if (_exposedContext != null) {
796 return _exposedContext;
797 }
798 return _headContext;
799 }
800
Tatu Salorantadfd69092015-04-09 22:29:34 -0700801}