blob: bfbc4ec9142855877e80d94df91ef2edada7ac80 [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;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700237 // Almost! Most likely still have the current token;
238 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700239 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700240 t = delegate.getCurrentToken();
241 if (t != JsonToken.FIELD_NAME) {
242 _currToken = t;
243 return t;
244 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700245 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700246 break;
247 }
248 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700249 ctxt = _headContext.findChildOf(ctxt);
250
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700251 _exposedContext = ctxt;
252 if (ctxt == null) { // should never occur
253 throw _constructError("Unexpected problem: chain of filtered context broken");
254 }
255 }
256 }
257
Tatu Salorantab46e0372015-04-14 21:43:03 -0700258 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700259 JsonToken t = delegate.nextToken();
260 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700261 // no strict need to close, since we have no state here
262 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700263 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700264
Tatu Salorantab46e0372015-04-14 21:43:03 -0700265 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700266 TokenFilter f;
267
268 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700269 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700270 f = _itemFilter;
271 if (f == TokenFilter.INCLUDE_ALL) {
272 _headContext = _headContext.createChildArrayContext(f, true);
273 return (_currToken = t);
274 }
275 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700276 delegate.skipChildren();
277 break;
278 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700279 // Otherwise still iffy, need to check
280 f = _headContext.checkValue(f);
281 if (f == null) {
282 delegate.skipChildren();
283 break;
284 }
285 if (f != TokenFilter.INCLUDE_ALL) {
286 f = f.filterStartArray();
287 }
288 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700289 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700290 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700291 return (_currToken = t);
292 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700293 _headContext = _headContext.createChildArrayContext(f, false);
294
295 // Also: only need buffering if parent path to be included
296 if (_includePath) {
297 return _nextTokenWithBuffering(_headContext);
298 }
299 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700300
301 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700302 f = _itemFilter;
303 if (f == TokenFilter.INCLUDE_ALL) {
304 _headContext = _headContext.createChildObjectContext(f, true);
305 return (_currToken = t);
306 }
307 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700308 delegate.skipChildren();
309 break;
310 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700311 // Otherwise still iffy, need to check
312 f = _headContext.checkValue(f);
313 if (f == null) {
314 delegate.skipChildren();
315 break;
316 }
317 if (f != TokenFilter.INCLUDE_ALL) {
318 f = f.filterStartObject();
319 }
320 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700321 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700322 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700323 return (_currToken = t);
324 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700325 _headContext = _headContext.createChildObjectContext(f, false);
326 // Also: only need buffering if parent path to be included
327 if (_includePath) {
328 return _nextTokenWithBuffering(_headContext);
329 }
330 // note: inclusion of surrounding Object handled separately via
331 // FIELD_NAME
332 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700333
334 case ID_END_ARRAY:
335 case ID_END_OBJECT:
336 {
337 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700338 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700339 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
340 f.filterFinishArray();
341 }
342 _headContext = _headContext.getParent();
343 _itemFilter = _headContext.getFilter();
344 if (returnEnd) {
345 return (_currToken = t);
346 }
347 }
348 break;
349
350 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700351 {
352 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700353 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700354 f = _headContext.setFieldName(name);
355 if (f == TokenFilter.INCLUDE_ALL) {
356 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700357 if (!_includePath) {
358 // Minor twist here: if parent NOT included, may need to induce output of
359 // surrounding START_OBJECT/END_OBJECT
360 if (_includeImmediateParent && !_headContext.isStartHandled()) {
361 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
362 _exposedContext = _headContext;
363 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700364 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700365 return (_currToken = t);
366 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700367 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700368 delegate.nextToken();
369 delegate.skipChildren();
370 break;
371 }
372 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700373 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700374 delegate.nextToken();
375 delegate.skipChildren();
376 break;
377 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700378 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700379 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700380 if (_includePath) {
381 return (_currToken = t);
382 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700383 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700384 if (_includePath) {
385 return _nextTokenWithBuffering(_headContext);
386 }
387 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700388 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700389
390 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700391 f = _itemFilter;
392 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700393 return (_currToken = t);
394 }
Cowtowncoder32224282015-04-16 16:39:22 -0700395 if (f != null) {
396 f = _headContext.checkValue(f);
397 if ((f == TokenFilter.INCLUDE_ALL)
398 || ((f != null) && f.includeValue(delegate))) {
399 return (_currToken = t);
400 }
401 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700402 // Otherwise not included (leaves must be explicitly included)
403 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700404 }
405
406 // We get here if token was not yet found; offlined handling
407 return _nextToken2();
408 }
409
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700410 /**
411 * Offlined handling for cases where there was no buffered token to
412 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700413 * at least not yet, but where we have not yet established that
414 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700415 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700416 protected final JsonToken _nextToken2() throws IOException
417 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700418 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700419 while (true) {
420 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700421 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700422 return (_currToken = t);
423 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700424 TokenFilter f;
425
426 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700427 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700428 f = _itemFilter;
429 if (f == TokenFilter.INCLUDE_ALL) {
430 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700431 return (_currToken = t);
432 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700433 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700434 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700435 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700436 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700437 // Otherwise still iffy, need to check
438 f = _headContext.checkValue(f);
439 if (f == null) {
440 delegate.skipChildren();
441 continue main_loop;
442 }
443 if (f != TokenFilter.INCLUDE_ALL) {
444 f = f.filterStartArray();
445 }
446 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700447 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700448 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700449 return (_currToken = t);
450 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700451 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700452 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700453 if (_includePath) {
454 return _nextTokenWithBuffering(_headContext);
455 }
456 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700457
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700458 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700459 f = _itemFilter;
460 if (f == TokenFilter.INCLUDE_ALL) {
461 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700462 return (_currToken = t);
463 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700464 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700465 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700466 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700467 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700468 // Otherwise still iffy, need to check
469 f = _headContext.checkValue(f);
470 if (f == null) {
471 delegate.skipChildren();
472 continue main_loop;
473 }
474 if (f != TokenFilter.INCLUDE_ALL) {
475 f = f.filterStartObject();
476 }
477 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700478 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700479 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700480 return (_currToken = t);
481 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700482 _headContext = _headContext.createChildObjectContext(f, false);
483 if (_includePath) {
484 return _nextTokenWithBuffering(_headContext);
485 }
486 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700487
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700488 case ID_END_ARRAY:
489 case ID_END_OBJECT:
490 {
491 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700492 f = _headContext.getFilter();
493 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
494 f.filterFinishArray();
495 }
496 _headContext = _headContext.getParent();
497 _itemFilter = _headContext.getFilter();
498 if (returnEnd) {
499 return (_currToken = t);
500 }
501 }
502 continue main_loop;
503
504 case ID_FIELD_NAME:
505 {
506 final String name = delegate.getCurrentName();
507 f = _headContext.setFieldName(name);
508 if (f == TokenFilter.INCLUDE_ALL) {
509 _itemFilter = f;
510 return (_currToken = t);
511 }
512 if (f == null) { // filter out the value
513 delegate.nextToken();
514 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700515 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700516 }
517 f = f.includeProperty(name);
518 if (f == null) { // filter out the value
519 delegate.nextToken();
520 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700521 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700522 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700523 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700524 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700525 if (_includePath) {
526 return (_currToken = t);
527 }
528// if (_includeImmediateParent) { ...
529 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700530 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700531 if (_includePath) {
532 return _nextTokenWithBuffering(_headContext);
533 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700534 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700535 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700536
537 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700538 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700539 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700540 return (_currToken = t);
541 }
Cowtowncoder32224282015-04-16 16:39:22 -0700542 if (f != null) {
543 f = _headContext.checkValue(f);
544 if ((f == TokenFilter.INCLUDE_ALL)
545 || ((f != null) && f.includeValue(delegate))) {
546 return (_currToken = t);
547 }
548 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700549 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700550 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700551 }
552 }
553 }
554
555 /**
556 * Method called when a new potentially included context is found.
557 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700558 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
559 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700560 {
Cowtowncoder654acf02015-04-16 15:30:43 -0700561 _exposedContext = buffRoot;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700562
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700563 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700564 while (true) {
565 JsonToken t = delegate.nextToken();
566
567 if (t == null) { // is this even legal?
568 return (_currToken = t);
569 }
570 TokenFilter f;
571
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700572 // One simplification here: we know for a fact that the item filter is
573 // neither null nor 'include all', for most cases; the only exception
574 // being FIELD_NAME handling
575
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700576 switch (t.id()) {
577 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700578 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700579 if (f == null) {
580 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700581 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700582 }
583 if (f != TokenFilter.INCLUDE_ALL) {
584 f = f.filterStartArray();
585 }
586 _itemFilter = f;
587 _headContext = _headContext.createChildArrayContext(f, true);
588 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700589 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700590 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700591 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700592
593 case ID_START_OBJECT:
594 f = _itemFilter;
595 if (f == TokenFilter.INCLUDE_ALL) {
596 _headContext = _headContext.createChildObjectContext(f, true);
597 return (_currToken = t);
598 }
599 if (f == null) { // does this occur?
600 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700601 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700602 }
603 // Otherwise still iffy, need to check
604 f = _headContext.checkValue(f);
605 if (f == null) {
606 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700607 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700608 }
609 if (f != TokenFilter.INCLUDE_ALL) {
610 f = f.filterStartObject();
611 }
612 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700613 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700614 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700615 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700616 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700617 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700618 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700619
620 case ID_END_ARRAY:
621 case ID_END_OBJECT:
622 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700623 // Unlike with other loops, here we know that content was NOT
624 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700625 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700626 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
627 f.filterFinishArray();
628 }
629 _headContext = _headContext.getParent();
630 _itemFilter = _headContext.getFilter();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700631
632 if (_headContext == buffRoot) {
633 // !!! TBI
634 throw _constructError("Internal error: end of possible inclusion -- TBI");
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700635 }
636 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700637 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700638
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700639 case ID_FIELD_NAME:
640 {
641 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700642 f = _headContext.setFieldName(name);
643 if (f == TokenFilter.INCLUDE_ALL) {
644 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700645 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700646 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700647 if (f == null) { // filter out the value
648 delegate.nextToken();
649 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700650 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700651 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700652 f = f.includeProperty(name);
653 if (f == null) { // filter out the value
654 delegate.nextToken();
655 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700656 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700657 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700658 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700659 if (f == TokenFilter.INCLUDE_ALL) {
660 return _nextBuffered();
661 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700662 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700663 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700664
665 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700666 f = _itemFilter;
667 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700668 return _nextBuffered();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700669 }
Cowtowncoder32224282015-04-16 16:39:22 -0700670 if (f != null) {
671 f = _headContext.checkValue(f);
672 if ((f == TokenFilter.INCLUDE_ALL)
673 || ((f != null) && f.includeValue(delegate))) {
674 return _nextBuffered();
675 }
676 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700677 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700678 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700679 }
680 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700681 }
682
683 private JsonToken _nextBuffered() throws IOException
684 {
685 TokenFilterContext ctxt = _exposedContext;
686 JsonToken t = ctxt.nextTokenToRead();
687 if (t != null) {
688 _currToken = t;
689 return t;
690 }
691 while (true) {
692 // all done with buffered stuff?
693 if (ctxt == _headContext) {
694 throw _constructError("Internal error: failed to locate expected buffered tokens");
695 /*
696 _exposedContext = null;
697 break;
698 */
699 }
700 // If not, traverse down the context chain
701 ctxt = _exposedContext.findChildOf(ctxt);
702 _exposedContext = ctxt;
703 if (ctxt == null) { // should never occur
704 throw _constructError("Unexpected problem: chain of filtered context broken");
705 }
706 t = _exposedContext.nextTokenToRead();
707 if (t != null) {
708 _currToken = t;
709 return t;
710 }
711 }
712 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700713
Tatu Salorantadfd69092015-04-09 22:29:34 -0700714 @Override
715 public JsonToken nextValue() throws IOException {
716 // Re-implemented same as ParserMinimalBase:
717 JsonToken t = nextToken();
718 if (t == JsonToken.FIELD_NAME) {
719 t = nextToken();
720 }
721 return t;
722 }
723
724 /**
725 * Need to override, re-implement similar to how method defined in
726 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
727 * state correct here.
728 */
729 @Override
730 public JsonParser skipChildren() throws IOException
731 {
Cowtowncoder00900312015-04-14 16:02:33 -0700732 if ((_currToken != JsonToken.START_OBJECT)
733 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700734 return this;
735 }
736 int open = 1;
737
738 // Since proper matching of start/end markers is handled
739 // by nextToken(), we'll just count nesting levels here
740 while (true) {
741 JsonToken t = nextToken();
742 if (t == null) { // not ideal but for now, just return
743 return this;
744 }
745 if (t.isStructStart()) {
746 ++open;
747 } else if (t.isStructEnd()) {
748 if (--open == 0) {
749 return this;
750 }
751 }
752 }
753 }
754
755 /*
756 /**********************************************************
757 /* Public API, access to token information, text
758 /**********************************************************
759 */
760
761 @Override public String getText() throws IOException { return delegate.getText(); }
762 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
763 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
764 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
765 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
766
767 /*
768 /**********************************************************
769 /* Public API, access to token information, numeric
770 /**********************************************************
771 */
772
773 @Override
774 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
775
776 @Override
777 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
778
779 @Override
780 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
781
782 @Override
783 public short getShortValue() throws IOException { return delegate.getShortValue(); }
784
785 @Override
786 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
787
788 @Override
789 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
790
791 @Override
792 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
793
794 @Override
795 public int getIntValue() throws IOException { return delegate.getIntValue(); }
796
797 @Override
798 public long getLongValue() throws IOException { return delegate.getLongValue(); }
799
800 @Override
801 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
802
803 @Override
804 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
805
806 /*
807 /**********************************************************
808 /* Public API, access to token information, coercion/conversion
809 /**********************************************************
810 */
811
812 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
813 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
814 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
815 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
816 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
817 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
818 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
819 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
820 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
821 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
822
823 /*
824 /**********************************************************
825 /* Public API, access to token values, other
826 /**********************************************************
827 */
828
829 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
830 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
831 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
832 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700833
834 /*
835 /**********************************************************
836 /* Internal helper methods
837 /**********************************************************
838 */
839
840 protected JsonStreamContext _filterContext() {
841 if (_exposedContext != null) {
842 return _exposedContext;
843 }
844 return _headContext;
845 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700846}