blob: a3194c369bf71a8e8923ff38216611231d325405 [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
Cowtowncoderdf521572015-04-16 15:58:41 -0700228 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700229 t = delegate.getCurrentToken();
230 if (t != JsonToken.FIELD_NAME) {
231 _currToken = t;
232 return t;
233 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700234 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700235 break;
236 }
237 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700238 ctxt = _headContext.findChildOf(ctxt);
239
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700240 _exposedContext = ctxt;
241 if (ctxt == null) { // should never occur
242 throw _constructError("Unexpected problem: chain of filtered context broken");
243 }
244 }
245 }
246
Tatu Salorantab46e0372015-04-14 21:43:03 -0700247 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700248 JsonToken t = delegate.nextToken();
249 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700250 // no strict need to close, since we have no state here
251 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700252 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700253
Tatu Salorantab46e0372015-04-14 21:43:03 -0700254 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700255 TokenFilter f;
256
257 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700258 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700259 f = _itemFilter;
260 if (f == TokenFilter.INCLUDE_ALL) {
261 _headContext = _headContext.createChildArrayContext(f, true);
262 return (_currToken = t);
263 }
264 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700265 delegate.skipChildren();
266 break;
267 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700268 // Otherwise still iffy, need to check
269 f = _headContext.checkValue(f);
270 if (f == null) {
271 delegate.skipChildren();
272 break;
273 }
274 if (f != TokenFilter.INCLUDE_ALL) {
275 f = f.filterStartArray();
276 }
277 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700278 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700279 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700280 return (_currToken = t);
281 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700282 _headContext = _headContext.createChildArrayContext(f, false);
283
284 // Also: only need buffering if parent path to be included
285 if (_includePath) {
286 return _nextTokenWithBuffering(_headContext);
287 }
288 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700289
290 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700291 f = _itemFilter;
292 if (f == TokenFilter.INCLUDE_ALL) {
293 _headContext = _headContext.createChildObjectContext(f, true);
294 return (_currToken = t);
295 }
296 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700297 delegate.skipChildren();
298 break;
299 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700300 // Otherwise still iffy, need to check
301 f = _headContext.checkValue(f);
302 if (f == null) {
303 delegate.skipChildren();
304 break;
305 }
306 if (f != TokenFilter.INCLUDE_ALL) {
307 f = f.filterStartObject();
308 }
309 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700310 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700311 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700312 return (_currToken = t);
313 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700314 _headContext = _headContext.createChildObjectContext(f, false);
315 // Also: only need buffering if parent path to be included
316 if (_includePath) {
317 return _nextTokenWithBuffering(_headContext);
318 }
319 // note: inclusion of surrounding Object handled separately via
320 // FIELD_NAME
321 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700322
323 case ID_END_ARRAY:
324 case ID_END_OBJECT:
325 {
326 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700327 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700328 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
329 f.filterFinishArray();
330 }
331 _headContext = _headContext.getParent();
332 _itemFilter = _headContext.getFilter();
333 if (returnEnd) {
334 return (_currToken = t);
335 }
336 }
337 break;
338
339 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700340 {
341 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700342 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700343 f = _headContext.setFieldName(name);
344 if (f == TokenFilter.INCLUDE_ALL) {
345 _itemFilter = f;
Cowtowncoder654acf02015-04-16 15:30:43 -0700346 // Minor twist here: if parent NOT included, may need to induce output of
347 // surrounding START_OBJECT/END_OBJECT
348 if (!_includePath && !_headContext.isStartHandled()) {
349 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
350 _exposedContext = _headContext;
351 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700352 return (_currToken = t);
353 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700354 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700355 delegate.nextToken();
356 delegate.skipChildren();
357 break;
358 }
359 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700360 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700361 delegate.nextToken();
362 delegate.skipChildren();
363 break;
364 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700365 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700366 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700367 return (_currToken = t);
368 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700369 if (_includePath) {
370 return _nextTokenWithBuffering(_headContext);
371 }
372 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700373 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700374
375 default: // scalar value
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700376 if (_itemFilter == TokenFilter.INCLUDE_ALL) {
377 return (_currToken = t);
378 }
379 // Otherwise not included (leaves must be explicitly included)
380 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700381 }
382
383 // We get here if token was not yet found; offlined handling
384 return _nextToken2();
385 }
386
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700387 /**
388 * Offlined handling for cases where there was no buffered token to
389 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700390 * at least not yet, but where we have not yet established that
391 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700392 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700393 protected final JsonToken _nextToken2() throws IOException
394 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700395 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700396 while (true) {
397 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700398 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700399 return (_currToken = t);
400 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700401 TokenFilter f;
402
403 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700404 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700405 f = _itemFilter;
406 if (f == TokenFilter.INCLUDE_ALL) {
407 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700408 return (_currToken = t);
409 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700410 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700411 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700412 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700413 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700414 // Otherwise still iffy, need to check
415 f = _headContext.checkValue(f);
416 if (f == null) {
417 delegate.skipChildren();
418 continue main_loop;
419 }
420 if (f != TokenFilter.INCLUDE_ALL) {
421 f = f.filterStartArray();
422 }
423 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700424 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700425 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700426 return (_currToken = t);
427 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700428 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700429 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700430 if (_includePath) {
431 return _nextTokenWithBuffering(_headContext);
432 }
433 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700434
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700435 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700436 f = _itemFilter;
437 if (f == TokenFilter.INCLUDE_ALL) {
438 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700439 return (_currToken = t);
440 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700441 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700442 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700443 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700444 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700445 // Otherwise still iffy, need to check
446 f = _headContext.checkValue(f);
447 if (f == null) {
448 delegate.skipChildren();
449 continue main_loop;
450 }
451 if (f != TokenFilter.INCLUDE_ALL) {
452 f = f.filterStartObject();
453 }
454 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700455 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700456 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700457 return (_currToken = t);
458 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700459 _headContext = _headContext.createChildObjectContext(f, false);
460 if (_includePath) {
461 return _nextTokenWithBuffering(_headContext);
462 }
463 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700464
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700465 case ID_END_ARRAY:
466 case ID_END_OBJECT:
467 {
468 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700469 f = _headContext.getFilter();
470 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
471 f.filterFinishArray();
472 }
473 _headContext = _headContext.getParent();
474 _itemFilter = _headContext.getFilter();
475 if (returnEnd) {
476 return (_currToken = t);
477 }
478 }
479 continue main_loop;
480
481 case ID_FIELD_NAME:
482 {
483 final String name = delegate.getCurrentName();
484 f = _headContext.setFieldName(name);
485 if (f == TokenFilter.INCLUDE_ALL) {
486 _itemFilter = f;
487 return (_currToken = t);
488 }
489 if (f == null) { // filter out the value
490 delegate.nextToken();
491 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700492 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700493 }
494 f = f.includeProperty(name);
495 if (f == null) { // filter out the value
496 delegate.nextToken();
497 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700498 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700499 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700500 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700501 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700502 return (_currToken = t);
503 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700504 if (_includePath) {
505 return _nextTokenWithBuffering(_headContext);
506 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700507 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700508 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700509
510 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700511 f = _itemFilter;
512 if ((f == TokenFilter.INCLUDE_ALL)
513 || ((f != null) && f.includeValue(delegate))) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700514 return (_currToken = t);
515 }
516 // Otherwise not included (leaves must be explicitly included)
517 continue main_loop;
518 }
519 }
520 }
521
522 /**
523 * Method called when a new potentially included context is found.
524 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700525 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
526 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700527 {
Cowtowncoder654acf02015-04-16 15:30:43 -0700528 _exposedContext = buffRoot;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700529
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700530 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700531 while (true) {
532 JsonToken t = delegate.nextToken();
533
534 if (t == null) { // is this even legal?
535 return (_currToken = t);
536 }
537 TokenFilter f;
538
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700539 // One simplification here: we know for a fact that the item filter is
540 // neither null nor 'include all', for most cases; the only exception
541 // being FIELD_NAME handling
542
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700543 switch (t.id()) {
544 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700545 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700546 if (f == null) {
547 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700548 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700549 }
550 if (f != TokenFilter.INCLUDE_ALL) {
551 f = f.filterStartArray();
552 }
553 _itemFilter = f;
554 _headContext = _headContext.createChildArrayContext(f, true);
555 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700556 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700557 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700558 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700559
560 case ID_START_OBJECT:
561 f = _itemFilter;
562 if (f == TokenFilter.INCLUDE_ALL) {
563 _headContext = _headContext.createChildObjectContext(f, true);
564 return (_currToken = t);
565 }
566 if (f == null) { // does this occur?
567 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700568 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700569 }
570 // Otherwise still iffy, need to check
571 f = _headContext.checkValue(f);
572 if (f == null) {
573 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700574 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700575 }
576 if (f != TokenFilter.INCLUDE_ALL) {
577 f = f.filterStartObject();
578 }
579 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700580 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700581 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700582 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700583 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700584 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700585 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700586
587 case ID_END_ARRAY:
588 case ID_END_OBJECT:
589 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700590 // Unlike with other loops, here we know that content was NOT
591 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700592 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700593 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
594 f.filterFinishArray();
595 }
596 _headContext = _headContext.getParent();
597 _itemFilter = _headContext.getFilter();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700598
599 if (_headContext == buffRoot) {
600 // !!! TBI
601 throw _constructError("Internal error: end of possible inclusion -- TBI");
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700602 }
603 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700604 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700605
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700606 case ID_FIELD_NAME:
607 {
608 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700609 f = _headContext.setFieldName(name);
610 if (f == TokenFilter.INCLUDE_ALL) {
611 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700612 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700613 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700614 if (f == null) { // filter out the value
615 delegate.nextToken();
616 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700617 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700618 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700619 f = f.includeProperty(name);
620 if (f == null) { // filter out the value
621 delegate.nextToken();
622 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700623 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700624 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700625 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700626 if (f == TokenFilter.INCLUDE_ALL) {
627 return _nextBuffered();
628 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700629 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700630 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700631
632 default: // scalar value
633 if (_itemFilter == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700634 return _nextBuffered();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700635 }
636 // Otherwise not included (leaves must be explicitly included)
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700637 }
638 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700639 }
640
641 private JsonToken _nextBuffered() throws IOException
642 {
643 TokenFilterContext ctxt = _exposedContext;
644 JsonToken t = ctxt.nextTokenToRead();
645 if (t != null) {
646 _currToken = t;
647 return t;
648 }
649 while (true) {
650 // all done with buffered stuff?
651 if (ctxt == _headContext) {
652 throw _constructError("Internal error: failed to locate expected buffered tokens");
653 /*
654 _exposedContext = null;
655 break;
656 */
657 }
658 // If not, traverse down the context chain
659 ctxt = _exposedContext.findChildOf(ctxt);
660 _exposedContext = ctxt;
661 if (ctxt == null) { // should never occur
662 throw _constructError("Unexpected problem: chain of filtered context broken");
663 }
664 t = _exposedContext.nextTokenToRead();
665 if (t != null) {
666 _currToken = t;
667 return t;
668 }
669 }
670 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700671
Tatu Salorantadfd69092015-04-09 22:29:34 -0700672 @Override
673 public JsonToken nextValue() throws IOException {
674 // Re-implemented same as ParserMinimalBase:
675 JsonToken t = nextToken();
676 if (t == JsonToken.FIELD_NAME) {
677 t = nextToken();
678 }
679 return t;
680 }
681
682 /**
683 * Need to override, re-implement similar to how method defined in
684 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
685 * state correct here.
686 */
687 @Override
688 public JsonParser skipChildren() throws IOException
689 {
Cowtowncoder00900312015-04-14 16:02:33 -0700690 if ((_currToken != JsonToken.START_OBJECT)
691 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700692 return this;
693 }
694 int open = 1;
695
696 // Since proper matching of start/end markers is handled
697 // by nextToken(), we'll just count nesting levels here
698 while (true) {
699 JsonToken t = nextToken();
700 if (t == null) { // not ideal but for now, just return
701 return this;
702 }
703 if (t.isStructStart()) {
704 ++open;
705 } else if (t.isStructEnd()) {
706 if (--open == 0) {
707 return this;
708 }
709 }
710 }
711 }
712
713 /*
714 /**********************************************************
715 /* Public API, access to token information, text
716 /**********************************************************
717 */
718
719 @Override public String getText() throws IOException { return delegate.getText(); }
720 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
721 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
722 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
723 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
724
725 /*
726 /**********************************************************
727 /* Public API, access to token information, numeric
728 /**********************************************************
729 */
730
731 @Override
732 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
733
734 @Override
735 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
736
737 @Override
738 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
739
740 @Override
741 public short getShortValue() throws IOException { return delegate.getShortValue(); }
742
743 @Override
744 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
745
746 @Override
747 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
748
749 @Override
750 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
751
752 @Override
753 public int getIntValue() throws IOException { return delegate.getIntValue(); }
754
755 @Override
756 public long getLongValue() throws IOException { return delegate.getLongValue(); }
757
758 @Override
759 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
760
761 @Override
762 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
763
764 /*
765 /**********************************************************
766 /* Public API, access to token information, coercion/conversion
767 /**********************************************************
768 */
769
770 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
771 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
772 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
773 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
774 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
775 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
776 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
777 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
778 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
779 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
780
781 /*
782 /**********************************************************
783 /* Public API, access to token values, other
784 /**********************************************************
785 */
786
787 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
788 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
789 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
790 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700791
792 /*
793 /**********************************************************
794 /* Internal helper methods
795 /**********************************************************
796 */
797
798 protected JsonStreamContext _filterContext() {
799 if (_exposedContext != null) {
800 return _exposedContext;
801 }
802 return _headContext;
803 }
804
Tatu Salorantadfd69092015-04-09 22:29:34 -0700805}