blob: c3554467582ad03e5f9d15b1afe97dc1db84424b [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
Faisal Hameedb18e3882015-12-09 00:34:01 +050060 protected boolean _includeImmediateParent;
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; }
Tatu Salorantae052c312016-07-01 21:11:25 -0700149 @Override public JsonToken currentToken() { return _currToken; }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700150
151 @Override public final int getCurrentTokenId() {
152 final JsonToken t = _currToken;
153 return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
154 }
Tatu Salorantae052c312016-07-01 21:11:25 -0700155 @Override public final int currentTokenId() {
156 final JsonToken t = _currToken;
157 return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
158 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700159
160 @Override public boolean hasCurrentToken() { return _currToken != null; }
161 @Override public boolean hasTokenId(int id) {
162 final JsonToken t = _currToken;
163 if (t == null) {
164 return (JsonTokenId.ID_NO_TOKEN == id);
165 }
166 return t.id() == id;
167 }
168
169 @Override public final boolean hasToken(JsonToken t) {
170 return (_currToken == t);
171 }
172
173 @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
174 @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
175
176 @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700177
Cowtowncoder00900312015-04-14 16:02:33 -0700178 @Override
179 public JsonStreamContext getParsingContext() {
180 return _filterContext();
181 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700182
Cowtowncoder00900312015-04-14 16:02:33 -0700183 // !!! TODO: Verify it works as expected: copied from standard JSON parser impl
184 @Override
185 public String getCurrentName() throws IOException {
186 JsonStreamContext ctxt = _filterContext();
187 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
188 JsonStreamContext parent = ctxt.getParent();
189 return (parent == null) ? null : parent.getCurrentName();
190 }
191 return ctxt.getCurrentName();
192 }
193
Tatu Salorantadfd69092015-04-09 22:29:34 -0700194 /*
195 /**********************************************************
196 /* Public API, token state overrides
197 /**********************************************************
198 */
199
200 @Override
201 public void clearCurrentToken() {
202 if (_currToken != null) {
203 _lastClearedToken = _currToken;
204 _currToken = null;
205 }
206 }
207
208 @Override
209 public JsonToken getLastClearedToken() { return _lastClearedToken; }
210
Tatu Salorantadfd69092015-04-09 22:29:34 -0700211 @Override
Cowtowncoder00900312015-04-14 16:02:33 -0700212 public void overrideCurrentName(String name) {
213 /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so,
214 * what to do with it... Delegation won't work for sure, so let's for
215 * now throw an exception
216 */
217 throw new UnsupportedOperationException("Can not currently override name during filtering read");
218 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700219
220 /*
221 /**********************************************************
222 /* Public API, traversal
223 /**********************************************************
224 */
225
Cowtowncoder00900312015-04-14 16:02:33 -0700226 @Override
227 public JsonToken nextToken() throws IOException
228 {
Tatu Saloranta96faf2e2017-05-23 16:08:31 -0700229 // 23-May-2017, tatu: To be honest, code here is rather hairy and I don't like all
230 // conditionals; and it seems odd to return `null` but NOT considering input
231 // as closed... would love a rewrite to simplify/clear up logic here.
232
233 // Check for _allowMultipleMatches - false and at least there is one token - which is _currToken
234 // check for no buffered context _exposedContext - null
235 // If all the conditions matches then check for scalar / non-scalar property
Rafal Foltynski1415e9d2017-06-03 02:43:18 +0200236
Tatu Saloranta96faf2e2017-05-23 16:08:31 -0700237 if (!_allowMultipleMatches && (_currToken != null) && (_exposedContext == null)) {
Rafal Foltynski1415e9d2017-06-03 02:43:18 +0200238 // if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL
Tatu Saloranta4ed6f922017-05-29 19:33:04 -0700239 // matched once, return null
240 if (_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath
241 && (_itemFilter == TokenFilter.INCLUDE_ALL)) {
242 return (_currToken = null);
Tatu Saloranta96faf2e2017-05-23 16:08:31 -0700243 }
244 }
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700245 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700246 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700247
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700248 if (ctxt != null) {
249 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700250 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700251 if (t != null) {
252 _currToken = t;
253 return t;
254 }
255 // all done with buffered stuff?
256 if (ctxt == _headContext) {
257 _exposedContext = null;
Cowtowncoderac4b4492015-04-23 15:36:50 -0700258 if (ctxt.inArray()) {
259 t = delegate.getCurrentToken();
260// Is this guaranteed to work without further checks?
261// if (t != JsonToken.START_ARRAY) {
262 _currToken = t;
263 return t;
264 }
265
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700266 // Almost! Most likely still have the current token;
267 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700268 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700269 t = delegate.getCurrentToken();
270 if (t != JsonToken.FIELD_NAME) {
271 _currToken = t;
272 return t;
273 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700274 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700275 break;
276 }
277 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700278 ctxt = _headContext.findChildOf(ctxt);
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700279 _exposedContext = ctxt;
280 if (ctxt == null) { // should never occur
281 throw _constructError("Unexpected problem: chain of filtered context broken");
282 }
283 }
284 }
285
Tatu Salorantab46e0372015-04-14 21:43:03 -0700286 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700287 JsonToken t = delegate.nextToken();
288 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700289 // no strict need to close, since we have no state here
Tatu Salorantae962fb92016-07-21 14:14:26 -0700290 _currToken = t;
291 return t;
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700292 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700293
Tatu Salorantab46e0372015-04-14 21:43:03 -0700294 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700295 TokenFilter f;
296
297 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700298 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700299 f = _itemFilter;
300 if (f == TokenFilter.INCLUDE_ALL) {
301 _headContext = _headContext.createChildArrayContext(f, true);
302 return (_currToken = t);
303 }
304 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700305 delegate.skipChildren();
306 break;
307 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700308 // Otherwise still iffy, need to check
309 f = _headContext.checkValue(f);
310 if (f == null) {
311 delegate.skipChildren();
312 break;
313 }
314 if (f != TokenFilter.INCLUDE_ALL) {
315 f = f.filterStartArray();
316 }
317 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700318 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700319 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700320 return (_currToken = t);
321 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700322 _headContext = _headContext.createChildArrayContext(f, false);
323
324 // Also: only need buffering if parent path to be included
325 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700326 t = _nextTokenWithBuffering(_headContext);
327 if (t != null) {
328 _currToken = t;
329 return t;
330 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700331 }
332 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700333
334 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700335 f = _itemFilter;
336 if (f == TokenFilter.INCLUDE_ALL) {
337 _headContext = _headContext.createChildObjectContext(f, true);
338 return (_currToken = t);
339 }
340 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700341 delegate.skipChildren();
342 break;
343 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700344 // Otherwise still iffy, need to check
345 f = _headContext.checkValue(f);
346 if (f == null) {
347 delegate.skipChildren();
348 break;
349 }
350 if (f != TokenFilter.INCLUDE_ALL) {
351 f = f.filterStartObject();
352 }
353 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700354 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700355 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700356 return (_currToken = t);
357 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700358 _headContext = _headContext.createChildObjectContext(f, false);
359 // Also: only need buffering if parent path to be included
360 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700361 t = _nextTokenWithBuffering(_headContext);
362 if (t != null) {
363 _currToken = t;
364 return t;
365 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700366 }
367 // note: inclusion of surrounding Object handled separately via
368 // FIELD_NAME
369 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700370
371 case ID_END_ARRAY:
372 case ID_END_OBJECT:
373 {
374 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700375 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700376 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
377 f.filterFinishArray();
378 }
379 _headContext = _headContext.getParent();
380 _itemFilter = _headContext.getFilter();
381 if (returnEnd) {
382 return (_currToken = t);
383 }
384 }
385 break;
386
387 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700388 {
389 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700390 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700391 f = _headContext.setFieldName(name);
392 if (f == TokenFilter.INCLUDE_ALL) {
393 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700394 if (!_includePath) {
395 // Minor twist here: if parent NOT included, may need to induce output of
396 // surrounding START_OBJECT/END_OBJECT
397 if (_includeImmediateParent && !_headContext.isStartHandled()) {
398 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
399 _exposedContext = _headContext;
400 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700401 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700402 return (_currToken = t);
403 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700404 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700405 delegate.nextToken();
406 delegate.skipChildren();
407 break;
408 }
409 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700410 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700411 delegate.nextToken();
412 delegate.skipChildren();
413 break;
414 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700415 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700416 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700417 if (_includePath) {
418 return (_currToken = t);
419 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700420 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700421 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700422 t = _nextTokenWithBuffering(_headContext);
423 if (t != null) {
424 _currToken = t;
425 return t;
426 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700427 }
428 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700429 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700430
431 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700432 f = _itemFilter;
433 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700434 return (_currToken = t);
435 }
Cowtowncoder32224282015-04-16 16:39:22 -0700436 if (f != null) {
437 f = _headContext.checkValue(f);
438 if ((f == TokenFilter.INCLUDE_ALL)
439 || ((f != null) && f.includeValue(delegate))) {
440 return (_currToken = t);
441 }
442 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700443 // Otherwise not included (leaves must be explicitly included)
444 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700445 }
446
447 // We get here if token was not yet found; offlined handling
448 return _nextToken2();
449 }
450
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700451 /**
452 * Offlined handling for cases where there was no buffered token to
453 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700454 * at least not yet, but where we have not yet established that
455 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700456 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700457 protected final JsonToken _nextToken2() throws IOException
458 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700459 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700460 while (true) {
461 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700462 if (t == null) { // is this even legal?
Tatu Salorantae962fb92016-07-21 14:14:26 -0700463 _currToken = t;
464 return t;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700465 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700466 TokenFilter f;
467
468 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700469 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700470 f = _itemFilter;
471 if (f == TokenFilter.INCLUDE_ALL) {
472 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700473 return (_currToken = t);
474 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700475 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700476 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700477 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700478 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700479 // Otherwise still iffy, need to check
480 f = _headContext.checkValue(f);
481 if (f == null) {
482 delegate.skipChildren();
483 continue main_loop;
484 }
485 if (f != TokenFilter.INCLUDE_ALL) {
486 f = f.filterStartArray();
487 }
488 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700489 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700490 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700491 return (_currToken = t);
492 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700493 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700494 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700495 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700496 t = _nextTokenWithBuffering(_headContext);
497 if (t != null) {
498 _currToken = t;
499 return t;
500 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700501 }
502 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700503
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700504 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700505 f = _itemFilter;
506 if (f == TokenFilter.INCLUDE_ALL) {
507 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700508 return (_currToken = t);
509 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700510 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700511 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700512 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700513 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700514 // Otherwise still iffy, need to check
515 f = _headContext.checkValue(f);
516 if (f == null) {
517 delegate.skipChildren();
518 continue main_loop;
519 }
520 if (f != TokenFilter.INCLUDE_ALL) {
521 f = f.filterStartObject();
522 }
523 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700524 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700525 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700526 return (_currToken = t);
527 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700528 _headContext = _headContext.createChildObjectContext(f, false);
529 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700530 t = _nextTokenWithBuffering(_headContext);
531 if (t != null) {
532 _currToken = t;
533 return t;
534 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700535 }
536 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700537
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700538 case ID_END_ARRAY:
539 case ID_END_OBJECT:
540 {
541 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700542 f = _headContext.getFilter();
543 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
544 f.filterFinishArray();
545 }
546 _headContext = _headContext.getParent();
547 _itemFilter = _headContext.getFilter();
548 if (returnEnd) {
549 return (_currToken = t);
550 }
551 }
552 continue main_loop;
553
554 case ID_FIELD_NAME:
555 {
556 final String name = delegate.getCurrentName();
557 f = _headContext.setFieldName(name);
558 if (f == TokenFilter.INCLUDE_ALL) {
559 _itemFilter = f;
560 return (_currToken = t);
561 }
562 if (f == null) { // filter out the value
563 delegate.nextToken();
564 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700565 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700566 }
567 f = f.includeProperty(name);
568 if (f == null) { // filter out the value
569 delegate.nextToken();
570 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700571 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700572 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700573 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700574 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700575 if (_includePath) {
576 return (_currToken = t);
577 }
578// if (_includeImmediateParent) { ...
579 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700580 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700581 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700582 t = _nextTokenWithBuffering(_headContext);
583 if (t != null) {
584 _currToken = t;
585 return t;
586 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700587 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700588 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700589 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700590
591 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700592 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700593 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700594 return (_currToken = t);
595 }
Cowtowncoder32224282015-04-16 16:39:22 -0700596 if (f != null) {
597 f = _headContext.checkValue(f);
598 if ((f == TokenFilter.INCLUDE_ALL)
599 || ((f != null) && f.includeValue(delegate))) {
600 return (_currToken = t);
601 }
602 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700603 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700604 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700605 }
606 }
607 }
608
609 /**
610 * Method called when a new potentially included context is found.
611 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700612 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
613 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700614 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700615 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700616 while (true) {
617 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700618 if (t == null) { // is this even legal?
Cowtowncoderb808f262015-04-23 15:47:39 -0700619 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700620 }
621 TokenFilter f;
622
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700623 // One simplification here: we know for a fact that the item filter is
624 // neither null nor 'include all', for most cases; the only exception
625 // being FIELD_NAME handling
626
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700627 switch (t.id()) {
628 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700629 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700630 if (f == null) {
631 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700632 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700633 }
634 if (f != TokenFilter.INCLUDE_ALL) {
635 f = f.filterStartArray();
636 }
637 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700638 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700639 _headContext = _headContext.createChildArrayContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700640 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700641 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700642 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700643 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700644
645 case ID_START_OBJECT:
646 f = _itemFilter;
647 if (f == TokenFilter.INCLUDE_ALL) {
648 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoderb808f262015-04-23 15:47:39 -0700649 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700650 }
651 if (f == null) { // does this occur?
652 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700653 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700654 }
655 // Otherwise still iffy, need to check
656 f = _headContext.checkValue(f);
657 if (f == null) {
658 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700659 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700660 }
661 if (f != TokenFilter.INCLUDE_ALL) {
662 f = f.filterStartObject();
663 }
664 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700665 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700666 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700667 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700668 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700669 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700670 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700671
672 case ID_END_ARRAY:
673 case ID_END_OBJECT:
674 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700675 // Unlike with other loops, here we know that content was NOT
676 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700677 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700678 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
679 f.filterFinishArray();
680 }
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700681 boolean gotEnd = (_headContext == buffRoot);
682 boolean returnEnd = gotEnd && _headContext.isStartHandled();
683
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700684 _headContext = _headContext.getParent();
685 _itemFilter = _headContext.getFilter();
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700686
687 if (returnEnd) {
688 return t;
689 }
690 // Hmmh. Do we need both checks, or should above suffice?
691 if (gotEnd || (_headContext == buffRoot)) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700692 return null;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700693 }
694 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700695 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700696
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700697 case ID_FIELD_NAME:
698 {
699 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700700 f = _headContext.setFieldName(name);
701 if (f == TokenFilter.INCLUDE_ALL) {
702 _itemFilter = f;
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700703 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700704 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700705 if (f == null) { // filter out the value
706 delegate.nextToken();
707 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700708 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700709 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700710 f = f.includeProperty(name);
711 if (f == null) { // filter out the value
712 delegate.nextToken();
713 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700714 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700715 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700716 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700717 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700718 return _nextBuffered(buffRoot);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700719 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700720 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700721 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700722
723 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700724 f = _itemFilter;
725 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700726 return _nextBuffered(buffRoot);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700727 }
Cowtowncoder32224282015-04-16 16:39:22 -0700728 if (f != null) {
729 f = _headContext.checkValue(f);
730 if ((f == TokenFilter.INCLUDE_ALL)
731 || ((f != null) && f.includeValue(delegate))) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700732 return _nextBuffered(buffRoot);
Cowtowncoder32224282015-04-16 16:39:22 -0700733 }
734 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700735 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700736 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700737 }
738 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700739 }
740
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700741 private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700742 {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700743 _exposedContext = buffRoot;
744 TokenFilterContext ctxt = buffRoot;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700745 JsonToken t = ctxt.nextTokenToRead();
746 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700747 return t;
748 }
749 while (true) {
750 // all done with buffered stuff?
751 if (ctxt == _headContext) {
752 throw _constructError("Internal error: failed to locate expected buffered tokens");
753 /*
754 _exposedContext = null;
755 break;
756 */
757 }
758 // If not, traverse down the context chain
759 ctxt = _exposedContext.findChildOf(ctxt);
760 _exposedContext = ctxt;
761 if (ctxt == null) { // should never occur
762 throw _constructError("Unexpected problem: chain of filtered context broken");
763 }
764 t = _exposedContext.nextTokenToRead();
765 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700766 return t;
767 }
768 }
769 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700770
Tatu Salorantadfd69092015-04-09 22:29:34 -0700771 @Override
772 public JsonToken nextValue() throws IOException {
773 // Re-implemented same as ParserMinimalBase:
774 JsonToken t = nextToken();
775 if (t == JsonToken.FIELD_NAME) {
776 t = nextToken();
777 }
778 return t;
779 }
780
781 /**
782 * Need to override, re-implement similar to how method defined in
783 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
784 * state correct here.
785 */
786 @Override
787 public JsonParser skipChildren() throws IOException
788 {
Cowtowncoder00900312015-04-14 16:02:33 -0700789 if ((_currToken != JsonToken.START_OBJECT)
790 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700791 return this;
792 }
793 int open = 1;
794
795 // Since proper matching of start/end markers is handled
796 // by nextToken(), we'll just count nesting levels here
797 while (true) {
798 JsonToken t = nextToken();
799 if (t == null) { // not ideal but for now, just return
800 return this;
801 }
802 if (t.isStructStart()) {
803 ++open;
804 } else if (t.isStructEnd()) {
805 if (--open == 0) {
806 return this;
807 }
808 }
809 }
810 }
811
812 /*
813 /**********************************************************
814 /* Public API, access to token information, text
815 /**********************************************************
816 */
817
818 @Override public String getText() throws IOException { return delegate.getText(); }
819 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
820 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
821 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
822 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
823
824 /*
825 /**********************************************************
826 /* Public API, access to token information, numeric
827 /**********************************************************
828 */
829
830 @Override
831 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
832
833 @Override
834 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
835
836 @Override
837 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
838
839 @Override
840 public short getShortValue() throws IOException { return delegate.getShortValue(); }
841
842 @Override
843 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
844
845 @Override
846 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
847
848 @Override
849 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
850
851 @Override
852 public int getIntValue() throws IOException { return delegate.getIntValue(); }
853
854 @Override
855 public long getLongValue() throws IOException { return delegate.getLongValue(); }
856
857 @Override
858 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
859
860 @Override
861 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
862
863 /*
864 /**********************************************************
865 /* Public API, access to token information, coercion/conversion
866 /**********************************************************
867 */
868
869 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
870 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
871 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
872 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
873 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
874 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
875 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
876 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
877 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
878 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
879
880 /*
881 /**********************************************************
882 /* Public API, access to token values, other
883 /**********************************************************
884 */
885
886 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
887 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
888 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
889 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700890
891 /*
892 /**********************************************************
893 /* Internal helper methods
894 /**********************************************************
895 */
896
897 protected JsonStreamContext _filterContext() {
898 if (_exposedContext != null) {
899 return _exposedContext;
900 }
901 return _headContext;
902 }
LokeshNfd327722016-03-20 17:28:25 +0530903
Tatu Salorantadfd69092015-04-09 22:29:34 -0700904}