blob: 2bb0165ce893b718b821341b39de0fe56bed3593 [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 {
LokeshNfd327722016-03-20 17:28:25 +0530229 //Check for _allowMultipleMatches - false and atleast there is one token - which is _currToken
230 // check for no buffered context _exposedContext - null
231 //If all the conditions matches then check for scalar / non-scalar property
232 if(!_allowMultipleMatches && _currToken != null && _exposedContext == null){
233 //if not scalar and ended successfully, then return null
234 if((_currToken.isStructEnd() && _headContext.isStartHandled()) ){
235 return (_currToken = null);
236 }
237 //else if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL matched once
238 // then return null
239 else if(_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath
240 && _itemFilter == TokenFilter.INCLUDE_ALL) {
241 return (_currToken = null);
242 }
243 }
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700244 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700245 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700246
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700247 if (ctxt != null) {
248 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700249 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700250 if (t != null) {
251 _currToken = t;
252 return t;
253 }
254 // all done with buffered stuff?
255 if (ctxt == _headContext) {
256 _exposedContext = null;
Cowtowncoderac4b4492015-04-23 15:36:50 -0700257 if (ctxt.inArray()) {
258 t = delegate.getCurrentToken();
259// Is this guaranteed to work without further checks?
260// if (t != JsonToken.START_ARRAY) {
261 _currToken = t;
262 return t;
263 }
264
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700265 // Almost! Most likely still have the current token;
266 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700267 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700268 t = delegate.getCurrentToken();
269 if (t != JsonToken.FIELD_NAME) {
270 _currToken = t;
271 return t;
272 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700273 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700274 break;
275 }
276 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700277 ctxt = _headContext.findChildOf(ctxt);
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700278 _exposedContext = ctxt;
279 if (ctxt == null) { // should never occur
280 throw _constructError("Unexpected problem: chain of filtered context broken");
281 }
282 }
283 }
284
Tatu Salorantab46e0372015-04-14 21:43:03 -0700285 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700286 JsonToken t = delegate.nextToken();
287 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700288 // no strict need to close, since we have no state here
289 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700290 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700291
Tatu Salorantab46e0372015-04-14 21:43:03 -0700292 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700293 TokenFilter f;
294
295 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700296 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700297 f = _itemFilter;
298 if (f == TokenFilter.INCLUDE_ALL) {
299 _headContext = _headContext.createChildArrayContext(f, true);
300 return (_currToken = t);
301 }
302 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700303 delegate.skipChildren();
304 break;
305 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700306 // Otherwise still iffy, need to check
307 f = _headContext.checkValue(f);
308 if (f == null) {
309 delegate.skipChildren();
310 break;
311 }
312 if (f != TokenFilter.INCLUDE_ALL) {
313 f = f.filterStartArray();
314 }
315 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700316 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700317 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700318 return (_currToken = t);
319 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700320 _headContext = _headContext.createChildArrayContext(f, false);
321
322 // Also: only need buffering if parent path to be included
323 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700324 t = _nextTokenWithBuffering(_headContext);
325 if (t != null) {
326 _currToken = t;
327 return t;
328 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700329 }
330 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700331
332 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700333 f = _itemFilter;
334 if (f == TokenFilter.INCLUDE_ALL) {
335 _headContext = _headContext.createChildObjectContext(f, true);
336 return (_currToken = t);
337 }
338 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700339 delegate.skipChildren();
340 break;
341 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700342 // Otherwise still iffy, need to check
343 f = _headContext.checkValue(f);
344 if (f == null) {
345 delegate.skipChildren();
346 break;
347 }
348 if (f != TokenFilter.INCLUDE_ALL) {
349 f = f.filterStartObject();
350 }
351 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700352 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700353 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700354 return (_currToken = t);
355 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700356 _headContext = _headContext.createChildObjectContext(f, false);
357 // Also: only need buffering if parent path to be included
358 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700359 t = _nextTokenWithBuffering(_headContext);
360 if (t != null) {
361 _currToken = t;
362 return t;
363 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700364 }
365 // note: inclusion of surrounding Object handled separately via
366 // FIELD_NAME
367 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700368
369 case ID_END_ARRAY:
370 case ID_END_OBJECT:
371 {
372 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700373 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700374 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
375 f.filterFinishArray();
376 }
377 _headContext = _headContext.getParent();
378 _itemFilter = _headContext.getFilter();
379 if (returnEnd) {
380 return (_currToken = t);
381 }
382 }
383 break;
384
385 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700386 {
387 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700388 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700389 f = _headContext.setFieldName(name);
390 if (f == TokenFilter.INCLUDE_ALL) {
391 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700392 if (!_includePath) {
393 // Minor twist here: if parent NOT included, may need to induce output of
394 // surrounding START_OBJECT/END_OBJECT
395 if (_includeImmediateParent && !_headContext.isStartHandled()) {
396 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
397 _exposedContext = _headContext;
398 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700399 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700400 return (_currToken = t);
401 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700402 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700403 delegate.nextToken();
404 delegate.skipChildren();
405 break;
406 }
407 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700408 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700409 delegate.nextToken();
410 delegate.skipChildren();
411 break;
412 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700413 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700414 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700415 if (_includePath) {
416 return (_currToken = t);
417 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700418 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700419 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700420 t = _nextTokenWithBuffering(_headContext);
421 if (t != null) {
422 _currToken = t;
423 return t;
424 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700425 }
426 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700427 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700428
429 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700430 f = _itemFilter;
431 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700432 return (_currToken = t);
433 }
Cowtowncoder32224282015-04-16 16:39:22 -0700434 if (f != null) {
435 f = _headContext.checkValue(f);
436 if ((f == TokenFilter.INCLUDE_ALL)
437 || ((f != null) && f.includeValue(delegate))) {
438 return (_currToken = t);
439 }
440 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700441 // Otherwise not included (leaves must be explicitly included)
442 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700443 }
444
445 // We get here if token was not yet found; offlined handling
446 return _nextToken2();
447 }
448
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700449 /**
450 * Offlined handling for cases where there was no buffered token to
451 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700452 * at least not yet, but where we have not yet established that
453 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700454 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700455 protected final JsonToken _nextToken2() throws IOException
456 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700457 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700458 while (true) {
459 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700460 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700461 return (_currToken = t);
462 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700463 TokenFilter f;
464
465 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700466 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700467 f = _itemFilter;
468 if (f == TokenFilter.INCLUDE_ALL) {
469 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700470 return (_currToken = t);
471 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700472 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700473 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700474 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700475 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700476 // Otherwise still iffy, need to check
477 f = _headContext.checkValue(f);
478 if (f == null) {
479 delegate.skipChildren();
480 continue main_loop;
481 }
482 if (f != TokenFilter.INCLUDE_ALL) {
483 f = f.filterStartArray();
484 }
485 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700486 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700487 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700488 return (_currToken = t);
489 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700490 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700491 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700492 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700493 t = _nextTokenWithBuffering(_headContext);
494 if (t != null) {
495 _currToken = t;
496 return t;
497 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700498 }
499 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700500
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700501 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700502 f = _itemFilter;
503 if (f == TokenFilter.INCLUDE_ALL) {
504 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700505 return (_currToken = t);
506 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700507 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700508 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700509 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700510 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700511 // Otherwise still iffy, need to check
512 f = _headContext.checkValue(f);
513 if (f == null) {
514 delegate.skipChildren();
515 continue main_loop;
516 }
517 if (f != TokenFilter.INCLUDE_ALL) {
518 f = f.filterStartObject();
519 }
520 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700521 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700522 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700523 return (_currToken = t);
524 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700525 _headContext = _headContext.createChildObjectContext(f, false);
526 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700527 t = _nextTokenWithBuffering(_headContext);
528 if (t != null) {
529 _currToken = t;
530 return t;
531 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700532 }
533 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700534
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700535 case ID_END_ARRAY:
536 case ID_END_OBJECT:
537 {
538 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700539 f = _headContext.getFilter();
540 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
541 f.filterFinishArray();
542 }
543 _headContext = _headContext.getParent();
544 _itemFilter = _headContext.getFilter();
545 if (returnEnd) {
546 return (_currToken = t);
547 }
548 }
549 continue main_loop;
550
551 case ID_FIELD_NAME:
552 {
553 final String name = delegate.getCurrentName();
554 f = _headContext.setFieldName(name);
555 if (f == TokenFilter.INCLUDE_ALL) {
556 _itemFilter = f;
557 return (_currToken = t);
558 }
559 if (f == null) { // filter out the value
560 delegate.nextToken();
561 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700562 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700563 }
564 f = f.includeProperty(name);
565 if (f == null) { // filter out the value
566 delegate.nextToken();
567 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700568 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700569 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700570 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700571 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700572 if (_includePath) {
573 return (_currToken = t);
574 }
575// if (_includeImmediateParent) { ...
576 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700577 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700578 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700579 t = _nextTokenWithBuffering(_headContext);
580 if (t != null) {
581 _currToken = t;
582 return t;
583 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700584 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700585 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700586 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700587
588 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700589 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700590 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700591 return (_currToken = t);
592 }
Cowtowncoder32224282015-04-16 16:39:22 -0700593 if (f != null) {
594 f = _headContext.checkValue(f);
595 if ((f == TokenFilter.INCLUDE_ALL)
596 || ((f != null) && f.includeValue(delegate))) {
597 return (_currToken = t);
598 }
599 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700600 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700601 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700602 }
603 }
604 }
605
606 /**
607 * Method called when a new potentially included context is found.
608 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700609 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
610 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700611 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700612 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700613 while (true) {
614 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700615 if (t == null) { // is this even legal?
Cowtowncoderb808f262015-04-23 15:47:39 -0700616 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700617 }
618 TokenFilter f;
619
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700620 // One simplification here: we know for a fact that the item filter is
621 // neither null nor 'include all', for most cases; the only exception
622 // being FIELD_NAME handling
623
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700624 switch (t.id()) {
625 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700626 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700627 if (f == null) {
628 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700629 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700630 }
631 if (f != TokenFilter.INCLUDE_ALL) {
632 f = f.filterStartArray();
633 }
634 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700635 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700636 _headContext = _headContext.createChildArrayContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700637 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700638 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700639 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700640 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700641
642 case ID_START_OBJECT:
643 f = _itemFilter;
644 if (f == TokenFilter.INCLUDE_ALL) {
645 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoderb808f262015-04-23 15:47:39 -0700646 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700647 }
648 if (f == null) { // does this occur?
649 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700650 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700651 }
652 // Otherwise still iffy, need to check
653 f = _headContext.checkValue(f);
654 if (f == null) {
655 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700656 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700657 }
658 if (f != TokenFilter.INCLUDE_ALL) {
659 f = f.filterStartObject();
660 }
661 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700662 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700663 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700664 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700665 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700666 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700667 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700668
669 case ID_END_ARRAY:
670 case ID_END_OBJECT:
671 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700672 // Unlike with other loops, here we know that content was NOT
673 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700674 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700675 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
676 f.filterFinishArray();
677 }
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700678 boolean gotEnd = (_headContext == buffRoot);
679 boolean returnEnd = gotEnd && _headContext.isStartHandled();
680
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700681 _headContext = _headContext.getParent();
682 _itemFilter = _headContext.getFilter();
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700683
684 if (returnEnd) {
685 return t;
686 }
687 // Hmmh. Do we need both checks, or should above suffice?
688 if (gotEnd || (_headContext == buffRoot)) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700689 return null;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700690 }
691 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700692 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700693
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700694 case ID_FIELD_NAME:
695 {
696 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700697 f = _headContext.setFieldName(name);
698 if (f == TokenFilter.INCLUDE_ALL) {
699 _itemFilter = f;
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700700 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700701 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700702 if (f == null) { // filter out the value
703 delegate.nextToken();
704 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700705 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700706 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700707 f = f.includeProperty(name);
708 if (f == null) { // filter out the value
709 delegate.nextToken();
710 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700711 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700712 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700713 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700714 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700715 return _nextBuffered(buffRoot);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700716 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700717 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700718 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700719
720 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700721 f = _itemFilter;
722 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700723 return _nextBuffered(buffRoot);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700724 }
Cowtowncoder32224282015-04-16 16:39:22 -0700725 if (f != null) {
726 f = _headContext.checkValue(f);
727 if ((f == TokenFilter.INCLUDE_ALL)
728 || ((f != null) && f.includeValue(delegate))) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700729 return _nextBuffered(buffRoot);
Cowtowncoder32224282015-04-16 16:39:22 -0700730 }
731 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700732 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700733 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700734 }
735 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700736 }
737
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700738 private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700739 {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700740 _exposedContext = buffRoot;
741 TokenFilterContext ctxt = buffRoot;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700742 JsonToken t = ctxt.nextTokenToRead();
743 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700744 return t;
745 }
746 while (true) {
747 // all done with buffered stuff?
748 if (ctxt == _headContext) {
749 throw _constructError("Internal error: failed to locate expected buffered tokens");
750 /*
751 _exposedContext = null;
752 break;
753 */
754 }
755 // If not, traverse down the context chain
756 ctxt = _exposedContext.findChildOf(ctxt);
757 _exposedContext = ctxt;
758 if (ctxt == null) { // should never occur
759 throw _constructError("Unexpected problem: chain of filtered context broken");
760 }
761 t = _exposedContext.nextTokenToRead();
762 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700763 return t;
764 }
765 }
766 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700767
Tatu Salorantadfd69092015-04-09 22:29:34 -0700768 @Override
769 public JsonToken nextValue() throws IOException {
770 // Re-implemented same as ParserMinimalBase:
771 JsonToken t = nextToken();
772 if (t == JsonToken.FIELD_NAME) {
773 t = nextToken();
774 }
775 return t;
776 }
777
778 /**
779 * Need to override, re-implement similar to how method defined in
780 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
781 * state correct here.
782 */
783 @Override
784 public JsonParser skipChildren() throws IOException
785 {
Cowtowncoder00900312015-04-14 16:02:33 -0700786 if ((_currToken != JsonToken.START_OBJECT)
787 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700788 return this;
789 }
790 int open = 1;
791
792 // Since proper matching of start/end markers is handled
793 // by nextToken(), we'll just count nesting levels here
794 while (true) {
795 JsonToken t = nextToken();
796 if (t == null) { // not ideal but for now, just return
797 return this;
798 }
799 if (t.isStructStart()) {
800 ++open;
801 } else if (t.isStructEnd()) {
802 if (--open == 0) {
803 return this;
804 }
805 }
806 }
807 }
808
809 /*
810 /**********************************************************
811 /* Public API, access to token information, text
812 /**********************************************************
813 */
814
815 @Override public String getText() throws IOException { return delegate.getText(); }
816 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
817 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
818 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
819 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
820
821 /*
822 /**********************************************************
823 /* Public API, access to token information, numeric
824 /**********************************************************
825 */
826
827 @Override
828 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
829
830 @Override
831 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
832
833 @Override
834 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
835
836 @Override
837 public short getShortValue() throws IOException { return delegate.getShortValue(); }
838
839 @Override
840 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
841
842 @Override
843 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
844
845 @Override
846 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
847
848 @Override
849 public int getIntValue() throws IOException { return delegate.getIntValue(); }
850
851 @Override
852 public long getLongValue() throws IOException { return delegate.getLongValue(); }
853
854 @Override
855 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
856
857 @Override
858 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
859
860 /*
861 /**********************************************************
862 /* Public API, access to token information, coercion/conversion
863 /**********************************************************
864 */
865
866 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
867 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
868 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
869 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
870 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
871 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
872 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
873 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
874 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
875 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
876
877 /*
878 /**********************************************************
879 /* Public API, access to token values, other
880 /**********************************************************
881 */
882
883 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
884 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
885 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
886 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700887
888 /*
889 /**********************************************************
890 /* Internal helper methods
891 /**********************************************************
892 */
893
894 protected JsonStreamContext _filterContext() {
895 if (_exposedContext != null) {
896 return _exposedContext;
897 }
898 return _headContext;
899 }
LokeshNfd327722016-03-20 17:28:25 +0530900
Tatu Salorantadfd69092015-04-09 22:29:34 -0700901}