blob: ce80dd8c3294d44779cd4125e91c3e675edecbcf [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;
Tatu Salorantacfe32d02015-04-21 23:17:22 -070050
51 /* NOTE: this feature is included in the first version (2.6), but
52 * there is no public API to enable it, yet, since there isn't an
53 * actual use case. But it seemed possible need could arise, which
54 * is feature has not yet been removed. If no use is found within
55 * first version or two, just remove.
56 *
57 * Marked as deprecated since its status is uncertain.
58 */
59 @Deprecated
60 protected boolean _includeImmediateParent = false;
Tatu Salorantadfd69092015-04-09 22:29:34 -070061
62 /*
63 /**********************************************************
64 /* State
65 /**********************************************************
66 */
67
68 /**
69 * Last token retrieved via {@link #nextToken}, if any.
70 * Null before the first call to <code>nextToken()</code>,
71 * as well as if token has been explicitly cleared
72 */
73 protected JsonToken _currToken;
74
75 /**
76 * Last cleared token, if any: that is, value that was in
77 * effect when {@link #clearCurrentToken} was called.
78 */
79 protected JsonToken _lastClearedToken;
80
81 /**
Cowtowncoder00900312015-04-14 16:02:33 -070082 * During traversal this is the actual "open" parse tree, which sometimes
83 * is the same as {@link #_exposedContext}, and at other times is ahead
84 * of it. Note that this context is never null.
Tatu Salorantadfd69092015-04-09 22:29:34 -070085 */
Cowtowncoder00900312015-04-14 16:02:33 -070086 protected TokenFilterContext _headContext;
Tatu Salorantadfd69092015-04-09 22:29:34 -070087
88 /**
Cowtowncoder00900312015-04-14 16:02:33 -070089 * In cases where {@link #_headContext} is "ahead" of context exposed to
90 * caller, this context points to what is currently exposed to caller.
91 * When the two are in sync, this context reference will be <code>null</code>.
92 */
93 protected TokenFilterContext _exposedContext;
Cowtowncoder9a797fb2015-04-14 16:12:12 -070094
Cowtowncoder00900312015-04-14 16:02:33 -070095 /**
Tatu Salorantadfd69092015-04-09 22:29:34 -070096 * State that applies to the item within container, used where applicable.
97 * Specifically used to pass inclusion state between property name and
98 * property, and also used for array elements.
99 */
100 protected TokenFilter _itemFilter;
101
102 /**
103 * Number of tokens for which {@link TokenFilter#INCLUDE_ALL}
Tatu Saloranta5a5d1192015-04-13 23:05:07 -0700104 * has been returned.
Tatu Salorantadfd69092015-04-09 22:29:34 -0700105 */
106 protected int _matchCount;
107
108 /*
109 /**********************************************************
110 /* Construction, initialization
111 /**********************************************************
112 */
113
114 public FilteringParserDelegate(JsonParser p, TokenFilter f,
115 boolean includePath, boolean allowMultipleMatches)
116 {
117 super(p);
118 rootFilter = f;
119 // and this is the currently active filter for root values
120 _itemFilter = f;
Cowtowncoder00900312015-04-14 16:02:33 -0700121 _headContext = TokenFilterContext.createRootContext(f);
Tatu Salorantadfd69092015-04-09 22:29:34 -0700122 _includePath = includePath;
123 _allowMultipleMatches = allowMultipleMatches;
124 }
125
126 /*
127 /**********************************************************
128 /* Extended API
129 /**********************************************************
130 */
131
132 public TokenFilter getFilter() { return rootFilter; }
133
134 /**
135 * Accessor for finding number of matches, where specific token and sub-tree
136 * starting (if structured type) are passed.
137 */
138 public int getMatchCount() {
139 return _matchCount;
140 }
141
142 /*
143 /**********************************************************
144 /* Public API, token accessors
145 /**********************************************************
146 */
147
148 @Override public JsonToken getCurrentToken() { return _currToken; }
149
150 @Override public final int getCurrentTokenId() {
151 final JsonToken t = _currToken;
152 return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
153 }
154
155 @Override public boolean hasCurrentToken() { return _currToken != null; }
156 @Override public boolean hasTokenId(int id) {
157 final JsonToken t = _currToken;
158 if (t == null) {
159 return (JsonTokenId.ID_NO_TOKEN == id);
160 }
161 return t.id() == id;
162 }
163
164 @Override public final boolean hasToken(JsonToken t) {
165 return (_currToken == t);
166 }
167
168 @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
169 @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
170
171 @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700172
Cowtowncoder00900312015-04-14 16:02:33 -0700173 @Override
174 public JsonStreamContext getParsingContext() {
175 return _filterContext();
176 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700177
Cowtowncoder00900312015-04-14 16:02:33 -0700178 // !!! TODO: Verify it works as expected: copied from standard JSON parser impl
179 @Override
180 public String getCurrentName() throws IOException {
181 JsonStreamContext ctxt = _filterContext();
182 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
183 JsonStreamContext parent = ctxt.getParent();
184 return (parent == null) ? null : parent.getCurrentName();
185 }
186 return ctxt.getCurrentName();
187 }
188
Tatu Salorantadfd69092015-04-09 22:29:34 -0700189 /*
190 /**********************************************************
191 /* Public API, token state overrides
192 /**********************************************************
193 */
194
195 @Override
196 public void clearCurrentToken() {
197 if (_currToken != null) {
198 _lastClearedToken = _currToken;
199 _currToken = null;
200 }
201 }
202
203 @Override
204 public JsonToken getLastClearedToken() { return _lastClearedToken; }
205
Tatu Salorantadfd69092015-04-09 22:29:34 -0700206 @Override
Cowtowncoder00900312015-04-14 16:02:33 -0700207 public void overrideCurrentName(String name) {
208 /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so,
209 * what to do with it... Delegation won't work for sure, so let's for
210 * now throw an exception
211 */
212 throw new UnsupportedOperationException("Can not currently override name during filtering read");
213 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700214
215 /*
216 /**********************************************************
217 /* Public API, traversal
218 /**********************************************************
219 */
220
Cowtowncoder00900312015-04-14 16:02:33 -0700221 @Override
222 public JsonToken nextToken() throws IOException
223 {
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700224 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700225 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700226
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700227 if (ctxt != null) {
228 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700229 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700230 if (t != null) {
231 _currToken = t;
232 return t;
233 }
234 // all done with buffered stuff?
235 if (ctxt == _headContext) {
236 _exposedContext = null;
Cowtowncoderac4b4492015-04-23 15:36:50 -0700237 if (ctxt.inArray()) {
238 t = delegate.getCurrentToken();
239// Is this guaranteed to work without further checks?
240// if (t != JsonToken.START_ARRAY) {
241 _currToken = t;
242 return t;
243 }
244
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700245 // Almost! Most likely still have the current token;
246 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700247 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700248 t = delegate.getCurrentToken();
249 if (t != JsonToken.FIELD_NAME) {
250 _currToken = t;
251 return t;
252 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700253 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700254 break;
255 }
256 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700257 ctxt = _headContext.findChildOf(ctxt);
258
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700259 _exposedContext = ctxt;
260 if (ctxt == null) { // should never occur
261 throw _constructError("Unexpected problem: chain of filtered context broken");
262 }
263 }
264 }
265
Tatu Salorantab46e0372015-04-14 21:43:03 -0700266 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700267 JsonToken t = delegate.nextToken();
268 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700269 // no strict need to close, since we have no state here
270 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700271 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700272
Tatu Salorantab46e0372015-04-14 21:43:03 -0700273 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700274 TokenFilter f;
275
276 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700277 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700278 f = _itemFilter;
279 if (f == TokenFilter.INCLUDE_ALL) {
280 _headContext = _headContext.createChildArrayContext(f, true);
281 return (_currToken = t);
282 }
283 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700284 delegate.skipChildren();
285 break;
286 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700287 // Otherwise still iffy, need to check
288 f = _headContext.checkValue(f);
289 if (f == null) {
290 delegate.skipChildren();
291 break;
292 }
293 if (f != TokenFilter.INCLUDE_ALL) {
294 f = f.filterStartArray();
295 }
296 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700297 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700298 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700299 return (_currToken = t);
300 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700301 _headContext = _headContext.createChildArrayContext(f, false);
302
303 // Also: only need buffering if parent path to be included
304 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700305 t = _nextTokenWithBuffering(_headContext);
306 if (t != null) {
307 _currToken = t;
308 return t;
309 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700310 }
311 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700312
313 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700314 f = _itemFilter;
315 if (f == TokenFilter.INCLUDE_ALL) {
316 _headContext = _headContext.createChildObjectContext(f, true);
317 return (_currToken = t);
318 }
319 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700320 delegate.skipChildren();
321 break;
322 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700323 // Otherwise still iffy, need to check
324 f = _headContext.checkValue(f);
325 if (f == null) {
326 delegate.skipChildren();
327 break;
328 }
329 if (f != TokenFilter.INCLUDE_ALL) {
330 f = f.filterStartObject();
331 }
332 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700333 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700334 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700335 return (_currToken = t);
336 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700337 _headContext = _headContext.createChildObjectContext(f, false);
338 // Also: only need buffering if parent path to be included
339 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700340 t = _nextTokenWithBuffering(_headContext);
341 if (t != null) {
342 _currToken = t;
343 return t;
344 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700345 }
346 // note: inclusion of surrounding Object handled separately via
347 // FIELD_NAME
348 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700349
350 case ID_END_ARRAY:
351 case ID_END_OBJECT:
352 {
353 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700354 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700355 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
356 f.filterFinishArray();
357 }
358 _headContext = _headContext.getParent();
359 _itemFilter = _headContext.getFilter();
360 if (returnEnd) {
361 return (_currToken = t);
362 }
363 }
364 break;
365
366 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700367 {
368 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700369 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700370 f = _headContext.setFieldName(name);
371 if (f == TokenFilter.INCLUDE_ALL) {
372 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700373 if (!_includePath) {
374 // Minor twist here: if parent NOT included, may need to induce output of
375 // surrounding START_OBJECT/END_OBJECT
376 if (_includeImmediateParent && !_headContext.isStartHandled()) {
377 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
378 _exposedContext = _headContext;
379 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700380 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700381 return (_currToken = t);
382 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700383 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700384 delegate.nextToken();
385 delegate.skipChildren();
386 break;
387 }
388 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700389 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700390 delegate.nextToken();
391 delegate.skipChildren();
392 break;
393 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700394 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700395 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700396 if (_includePath) {
397 return (_currToken = t);
398 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700399 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700400 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700401 t = _nextTokenWithBuffering(_headContext);
402 if (t != null) {
403 _currToken = t;
404 return t;
405 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700406 }
407 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700408 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700409
410 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700411 f = _itemFilter;
412 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700413 return (_currToken = t);
414 }
Cowtowncoder32224282015-04-16 16:39:22 -0700415 if (f != null) {
416 f = _headContext.checkValue(f);
417 if ((f == TokenFilter.INCLUDE_ALL)
418 || ((f != null) && f.includeValue(delegate))) {
419 return (_currToken = t);
420 }
421 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700422 // Otherwise not included (leaves must be explicitly included)
423 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700424 }
425
426 // We get here if token was not yet found; offlined handling
427 return _nextToken2();
428 }
429
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700430 /**
431 * Offlined handling for cases where there was no buffered token to
432 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700433 * at least not yet, but where we have not yet established that
434 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700435 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700436 protected final JsonToken _nextToken2() throws IOException
437 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700438 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700439 while (true) {
440 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700441 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700442 return (_currToken = t);
443 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700444 TokenFilter f;
445
446 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700447 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700448 f = _itemFilter;
449 if (f == TokenFilter.INCLUDE_ALL) {
450 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700451 return (_currToken = t);
452 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700453 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700454 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700455 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700456 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700457 // Otherwise still iffy, need to check
458 f = _headContext.checkValue(f);
459 if (f == null) {
460 delegate.skipChildren();
461 continue main_loop;
462 }
463 if (f != TokenFilter.INCLUDE_ALL) {
464 f = f.filterStartArray();
465 }
466 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700467 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700468 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700469 return (_currToken = t);
470 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700471 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700472 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700473 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700474 t = _nextTokenWithBuffering(_headContext);
475 if (t != null) {
476 _currToken = t;
477 return t;
478 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700479 }
480 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700481
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700482 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700483 f = _itemFilter;
484 if (f == TokenFilter.INCLUDE_ALL) {
485 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700486 return (_currToken = t);
487 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700488 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700489 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700490 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700491 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700492 // Otherwise still iffy, need to check
493 f = _headContext.checkValue(f);
494 if (f == null) {
495 delegate.skipChildren();
496 continue main_loop;
497 }
498 if (f != TokenFilter.INCLUDE_ALL) {
499 f = f.filterStartObject();
500 }
501 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700502 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700503 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700504 return (_currToken = t);
505 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700506 _headContext = _headContext.createChildObjectContext(f, false);
507 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700508 t = _nextTokenWithBuffering(_headContext);
509 if (t != null) {
510 _currToken = t;
511 return t;
512 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700513 }
514 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700515
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700516 case ID_END_ARRAY:
517 case ID_END_OBJECT:
518 {
519 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700520 f = _headContext.getFilter();
521 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
522 f.filterFinishArray();
523 }
524 _headContext = _headContext.getParent();
525 _itemFilter = _headContext.getFilter();
526 if (returnEnd) {
527 return (_currToken = t);
528 }
529 }
530 continue main_loop;
531
532 case ID_FIELD_NAME:
533 {
534 final String name = delegate.getCurrentName();
535 f = _headContext.setFieldName(name);
536 if (f == TokenFilter.INCLUDE_ALL) {
537 _itemFilter = f;
538 return (_currToken = t);
539 }
540 if (f == null) { // filter out the value
541 delegate.nextToken();
542 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700543 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700544 }
545 f = f.includeProperty(name);
546 if (f == null) { // filter out the value
547 delegate.nextToken();
548 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700549 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700550 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700551 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700552 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700553 if (_includePath) {
554 return (_currToken = t);
555 }
556// if (_includeImmediateParent) { ...
557 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700558 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700559 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700560 t = _nextTokenWithBuffering(_headContext);
561 if (t != null) {
562 _currToken = t;
563 return t;
564 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700565 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700566 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700567 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700568
569 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700570 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700571 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700572 return (_currToken = t);
573 }
Cowtowncoder32224282015-04-16 16:39:22 -0700574 if (f != null) {
575 f = _headContext.checkValue(f);
576 if ((f == TokenFilter.INCLUDE_ALL)
577 || ((f != null) && f.includeValue(delegate))) {
578 return (_currToken = t);
579 }
580 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700581 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700582 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700583 }
584 }
585 }
586
587 /**
588 * Method called when a new potentially included context is found.
589 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700590 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
591 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700592 {
Cowtowncoder654acf02015-04-16 15:30:43 -0700593 _exposedContext = buffRoot;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700594
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700595 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700596 while (true) {
597 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700598 if (t == null) { // is this even legal?
Cowtowncoderb808f262015-04-23 15:47:39 -0700599 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700600 }
601 TokenFilter f;
602
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700603 // One simplification here: we know for a fact that the item filter is
604 // neither null nor 'include all', for most cases; the only exception
605 // being FIELD_NAME handling
606
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700607 switch (t.id()) {
608 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700609 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700610 if (f == null) {
611 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700612 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700613 }
614 if (f != TokenFilter.INCLUDE_ALL) {
615 f = f.filterStartArray();
616 }
617 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700618 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700619 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700620 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700621 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700622 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700623 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700624
625 case ID_START_OBJECT:
626 f = _itemFilter;
627 if (f == TokenFilter.INCLUDE_ALL) {
628 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoderb808f262015-04-23 15:47:39 -0700629 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700630 }
631 if (f == null) { // does this occur?
632 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700633 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700634 }
635 // Otherwise still iffy, need to check
636 f = _headContext.checkValue(f);
637 if (f == null) {
638 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700639 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700640 }
641 if (f != TokenFilter.INCLUDE_ALL) {
642 f = f.filterStartObject();
643 }
644 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700645 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700646 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700647 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700648 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700649 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700650 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700651
652 case ID_END_ARRAY:
653 case ID_END_OBJECT:
654 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700655 // Unlike with other loops, here we know that content was NOT
656 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700657 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700658 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
659 f.filterFinishArray();
660 }
661 _headContext = _headContext.getParent();
662 _itemFilter = _headContext.getFilter();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700663
664 if (_headContext == buffRoot) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700665 return null;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700666 }
667 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700668 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700669
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700670 case ID_FIELD_NAME:
671 {
672 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700673 f = _headContext.setFieldName(name);
674 if (f == TokenFilter.INCLUDE_ALL) {
675 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700676 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700677 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700678 if (f == null) { // filter out the value
679 delegate.nextToken();
680 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700681 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700682 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700683 f = f.includeProperty(name);
684 if (f == null) { // filter out the value
685 delegate.nextToken();
686 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700687 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700688 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700689 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700690 if (f == TokenFilter.INCLUDE_ALL) {
691 return _nextBuffered();
692 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700693 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700694 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700695
696 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700697 f = _itemFilter;
698 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700699 return _nextBuffered();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700700 }
Cowtowncoder32224282015-04-16 16:39:22 -0700701 if (f != null) {
702 f = _headContext.checkValue(f);
703 if ((f == TokenFilter.INCLUDE_ALL)
704 || ((f != null) && f.includeValue(delegate))) {
705 return _nextBuffered();
706 }
707 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700708 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700709 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700710 }
711 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700712 }
713
714 private JsonToken _nextBuffered() throws IOException
715 {
716 TokenFilterContext ctxt = _exposedContext;
717 JsonToken t = ctxt.nextTokenToRead();
718 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700719 return t;
720 }
721 while (true) {
722 // all done with buffered stuff?
723 if (ctxt == _headContext) {
724 throw _constructError("Internal error: failed to locate expected buffered tokens");
725 /*
726 _exposedContext = null;
727 break;
728 */
729 }
730 // If not, traverse down the context chain
731 ctxt = _exposedContext.findChildOf(ctxt);
732 _exposedContext = ctxt;
733 if (ctxt == null) { // should never occur
734 throw _constructError("Unexpected problem: chain of filtered context broken");
735 }
736 t = _exposedContext.nextTokenToRead();
737 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700738 return t;
739 }
740 }
741 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700742
Tatu Salorantadfd69092015-04-09 22:29:34 -0700743 @Override
744 public JsonToken nextValue() throws IOException {
745 // Re-implemented same as ParserMinimalBase:
746 JsonToken t = nextToken();
747 if (t == JsonToken.FIELD_NAME) {
748 t = nextToken();
749 }
750 return t;
751 }
752
753 /**
754 * Need to override, re-implement similar to how method defined in
755 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
756 * state correct here.
757 */
758 @Override
759 public JsonParser skipChildren() throws IOException
760 {
Cowtowncoder00900312015-04-14 16:02:33 -0700761 if ((_currToken != JsonToken.START_OBJECT)
762 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700763 return this;
764 }
765 int open = 1;
766
767 // Since proper matching of start/end markers is handled
768 // by nextToken(), we'll just count nesting levels here
769 while (true) {
770 JsonToken t = nextToken();
771 if (t == null) { // not ideal but for now, just return
772 return this;
773 }
774 if (t.isStructStart()) {
775 ++open;
776 } else if (t.isStructEnd()) {
777 if (--open == 0) {
778 return this;
779 }
780 }
781 }
782 }
783
784 /*
785 /**********************************************************
786 /* Public API, access to token information, text
787 /**********************************************************
788 */
789
790 @Override public String getText() throws IOException { return delegate.getText(); }
791 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
792 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
793 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
794 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
795
796 /*
797 /**********************************************************
798 /* Public API, access to token information, numeric
799 /**********************************************************
800 */
801
802 @Override
803 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
804
805 @Override
806 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
807
808 @Override
809 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
810
811 @Override
812 public short getShortValue() throws IOException { return delegate.getShortValue(); }
813
814 @Override
815 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
816
817 @Override
818 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
819
820 @Override
821 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
822
823 @Override
824 public int getIntValue() throws IOException { return delegate.getIntValue(); }
825
826 @Override
827 public long getLongValue() throws IOException { return delegate.getLongValue(); }
828
829 @Override
830 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
831
832 @Override
833 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
834
835 /*
836 /**********************************************************
837 /* Public API, access to token information, coercion/conversion
838 /**********************************************************
839 */
840
841 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
842 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
843 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
844 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
845 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
846 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
847 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
848 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
849 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
850 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
851
852 /*
853 /**********************************************************
854 /* Public API, access to token values, other
855 /**********************************************************
856 */
857
858 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
859 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
860 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
861 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700862
863 /*
864 /**********************************************************
865 /* Internal helper methods
866 /**********************************************************
867 */
868
869 protected JsonStreamContext _filterContext() {
870 if (_exposedContext != null) {
871 return _exposedContext;
872 }
873 return _headContext;
874 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700875}