blob: 6fd5f82be940b2e205a7772e08b6c6117608b57b [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
Tatu Saloranta4ed6f922017-05-29 19:33:04 -0700236
Tatu Saloranta96faf2e2017-05-23 16:08:31 -0700237 if (!_allowMultipleMatches && (_currToken != null) && (_exposedContext == null)) {
Tatu Saloranta4ed6f922017-05-29 19:33:04 -0700238 // if not scalar and ended successfully, then return null
239 if ((_currToken.isStructEnd() && _headContext.isStartHandled()) ){
240 return (_currToken = null);
241 }
242 // else if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL
243 // matched once, return null
244 if (_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath
245 && (_itemFilter == TokenFilter.INCLUDE_ALL)) {
246 return (_currToken = null);
Tatu Saloranta96faf2e2017-05-23 16:08:31 -0700247 }
248 }
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700249 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700250 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700251
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700252 if (ctxt != null) {
253 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700254 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700255 if (t != null) {
256 _currToken = t;
257 return t;
258 }
259 // all done with buffered stuff?
260 if (ctxt == _headContext) {
261 _exposedContext = null;
Cowtowncoderac4b4492015-04-23 15:36:50 -0700262 if (ctxt.inArray()) {
263 t = delegate.getCurrentToken();
264// Is this guaranteed to work without further checks?
265// if (t != JsonToken.START_ARRAY) {
266 _currToken = t;
267 return t;
268 }
269
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700270 // Almost! Most likely still have the current token;
271 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700272 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700273 t = delegate.getCurrentToken();
274 if (t != JsonToken.FIELD_NAME) {
275 _currToken = t;
276 return t;
277 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700278 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700279 break;
280 }
281 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700282 ctxt = _headContext.findChildOf(ctxt);
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700283 _exposedContext = ctxt;
284 if (ctxt == null) { // should never occur
285 throw _constructError("Unexpected problem: chain of filtered context broken");
286 }
287 }
288 }
289
Tatu Salorantab46e0372015-04-14 21:43:03 -0700290 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700291 JsonToken t = delegate.nextToken();
292 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700293 // no strict need to close, since we have no state here
Tatu Salorantae962fb92016-07-21 14:14:26 -0700294 _currToken = t;
295 return t;
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700296 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700297
Tatu Salorantab46e0372015-04-14 21:43:03 -0700298 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700299 TokenFilter f;
300
301 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700302 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700303 f = _itemFilter;
304 if (f == TokenFilter.INCLUDE_ALL) {
305 _headContext = _headContext.createChildArrayContext(f, true);
306 return (_currToken = t);
307 }
308 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700309 delegate.skipChildren();
310 break;
311 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700312 // Otherwise still iffy, need to check
313 f = _headContext.checkValue(f);
314 if (f == null) {
315 delegate.skipChildren();
316 break;
317 }
318 if (f != TokenFilter.INCLUDE_ALL) {
319 f = f.filterStartArray();
320 }
321 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700322 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700323 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700324 return (_currToken = t);
325 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700326 _headContext = _headContext.createChildArrayContext(f, false);
327
328 // Also: only need buffering if parent path to be included
329 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700330 t = _nextTokenWithBuffering(_headContext);
331 if (t != null) {
332 _currToken = t;
333 return t;
334 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700335 }
336 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700337
338 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700339 f = _itemFilter;
340 if (f == TokenFilter.INCLUDE_ALL) {
341 _headContext = _headContext.createChildObjectContext(f, true);
342 return (_currToken = t);
343 }
344 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700345 delegate.skipChildren();
346 break;
347 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700348 // Otherwise still iffy, need to check
349 f = _headContext.checkValue(f);
350 if (f == null) {
351 delegate.skipChildren();
352 break;
353 }
354 if (f != TokenFilter.INCLUDE_ALL) {
355 f = f.filterStartObject();
356 }
357 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700358 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700359 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700360 return (_currToken = t);
361 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700362 _headContext = _headContext.createChildObjectContext(f, false);
363 // Also: only need buffering if parent path to be included
364 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700365 t = _nextTokenWithBuffering(_headContext);
366 if (t != null) {
367 _currToken = t;
368 return t;
369 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700370 }
371 // note: inclusion of surrounding Object handled separately via
372 // FIELD_NAME
373 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700374
375 case ID_END_ARRAY:
376 case ID_END_OBJECT:
377 {
378 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700379 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700380 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
381 f.filterFinishArray();
382 }
383 _headContext = _headContext.getParent();
384 _itemFilter = _headContext.getFilter();
385 if (returnEnd) {
386 return (_currToken = t);
387 }
388 }
389 break;
390
391 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700392 {
393 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700394 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700395 f = _headContext.setFieldName(name);
396 if (f == TokenFilter.INCLUDE_ALL) {
397 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700398 if (!_includePath) {
399 // Minor twist here: if parent NOT included, may need to induce output of
400 // surrounding START_OBJECT/END_OBJECT
401 if (_includeImmediateParent && !_headContext.isStartHandled()) {
402 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
403 _exposedContext = _headContext;
404 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700405 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700406 return (_currToken = t);
407 }
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 }
413 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700414 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700415 delegate.nextToken();
416 delegate.skipChildren();
417 break;
418 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700419 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700420 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700421 if (_includePath) {
422 return (_currToken = t);
423 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700424 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700425 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700426 t = _nextTokenWithBuffering(_headContext);
427 if (t != null) {
428 _currToken = t;
429 return t;
430 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700431 }
432 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700433 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700434
435 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700436 f = _itemFilter;
437 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700438 return (_currToken = t);
439 }
Cowtowncoder32224282015-04-16 16:39:22 -0700440 if (f != null) {
441 f = _headContext.checkValue(f);
442 if ((f == TokenFilter.INCLUDE_ALL)
443 || ((f != null) && f.includeValue(delegate))) {
444 return (_currToken = t);
445 }
446 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700447 // Otherwise not included (leaves must be explicitly included)
448 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700449 }
450
451 // We get here if token was not yet found; offlined handling
452 return _nextToken2();
453 }
454
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700455 /**
456 * Offlined handling for cases where there was no buffered token to
457 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700458 * at least not yet, but where we have not yet established that
459 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700460 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700461 protected final JsonToken _nextToken2() throws IOException
462 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700463 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700464 while (true) {
465 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700466 if (t == null) { // is this even legal?
Tatu Salorantae962fb92016-07-21 14:14:26 -0700467 _currToken = t;
468 return t;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700469 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700470 TokenFilter f;
471
472 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700473 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700474 f = _itemFilter;
475 if (f == TokenFilter.INCLUDE_ALL) {
476 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700477 return (_currToken = t);
478 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700479 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700480 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700481 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700482 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700483 // Otherwise still iffy, need to check
484 f = _headContext.checkValue(f);
485 if (f == null) {
486 delegate.skipChildren();
487 continue main_loop;
488 }
489 if (f != TokenFilter.INCLUDE_ALL) {
490 f = f.filterStartArray();
491 }
492 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700493 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700494 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700495 return (_currToken = t);
496 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700497 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700498 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700499 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700500 t = _nextTokenWithBuffering(_headContext);
501 if (t != null) {
502 _currToken = t;
503 return t;
504 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700505 }
506 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700507
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700508 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700509 f = _itemFilter;
510 if (f == TokenFilter.INCLUDE_ALL) {
511 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700512 return (_currToken = t);
513 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700514 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700515 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700516 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700517 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700518 // Otherwise still iffy, need to check
519 f = _headContext.checkValue(f);
520 if (f == null) {
521 delegate.skipChildren();
522 continue main_loop;
523 }
524 if (f != TokenFilter.INCLUDE_ALL) {
525 f = f.filterStartObject();
526 }
527 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700528 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700529 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700530 return (_currToken = t);
531 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700532 _headContext = _headContext.createChildObjectContext(f, false);
533 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700534 t = _nextTokenWithBuffering(_headContext);
535 if (t != null) {
536 _currToken = t;
537 return t;
538 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700539 }
540 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700541
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700542 case ID_END_ARRAY:
543 case ID_END_OBJECT:
544 {
545 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700546 f = _headContext.getFilter();
547 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
548 f.filterFinishArray();
549 }
550 _headContext = _headContext.getParent();
551 _itemFilter = _headContext.getFilter();
552 if (returnEnd) {
553 return (_currToken = t);
554 }
555 }
556 continue main_loop;
557
558 case ID_FIELD_NAME:
559 {
560 final String name = delegate.getCurrentName();
561 f = _headContext.setFieldName(name);
562 if (f == TokenFilter.INCLUDE_ALL) {
563 _itemFilter = f;
564 return (_currToken = t);
565 }
566 if (f == null) { // filter out the value
567 delegate.nextToken();
568 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700569 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700570 }
571 f = f.includeProperty(name);
572 if (f == null) { // filter out the value
573 delegate.nextToken();
574 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700575 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700576 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700577 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700578 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700579 if (_includePath) {
580 return (_currToken = t);
581 }
582// if (_includeImmediateParent) { ...
583 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700584 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700585 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700586 t = _nextTokenWithBuffering(_headContext);
587 if (t != null) {
588 _currToken = t;
589 return t;
590 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700591 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700592 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700593 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700594
595 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700596 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700597 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700598 return (_currToken = t);
599 }
Cowtowncoder32224282015-04-16 16:39:22 -0700600 if (f != null) {
601 f = _headContext.checkValue(f);
602 if ((f == TokenFilter.INCLUDE_ALL)
603 || ((f != null) && f.includeValue(delegate))) {
604 return (_currToken = t);
605 }
606 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700607 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700608 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700609 }
610 }
611 }
612
613 /**
614 * Method called when a new potentially included context is found.
615 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700616 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
617 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700618 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700619 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700620 while (true) {
621 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700622 if (t == null) { // is this even legal?
Cowtowncoderb808f262015-04-23 15:47:39 -0700623 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700624 }
625 TokenFilter f;
626
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700627 // One simplification here: we know for a fact that the item filter is
628 // neither null nor 'include all', for most cases; the only exception
629 // being FIELD_NAME handling
630
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700631 switch (t.id()) {
632 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700633 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700634 if (f == null) {
635 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700636 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700637 }
638 if (f != TokenFilter.INCLUDE_ALL) {
639 f = f.filterStartArray();
640 }
641 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700642 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700643 _headContext = _headContext.createChildArrayContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700644 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700645 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700646 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700647 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700648
649 case ID_START_OBJECT:
650 f = _itemFilter;
651 if (f == TokenFilter.INCLUDE_ALL) {
652 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoderb808f262015-04-23 15:47:39 -0700653 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700654 }
655 if (f == null) { // does this occur?
656 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700657 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700658 }
659 // Otherwise still iffy, need to check
660 f = _headContext.checkValue(f);
661 if (f == null) {
662 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700663 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700664 }
665 if (f != TokenFilter.INCLUDE_ALL) {
666 f = f.filterStartObject();
667 }
668 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700669 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700670 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700671 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700672 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700673 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700674 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700675
676 case ID_END_ARRAY:
677 case ID_END_OBJECT:
678 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700679 // Unlike with other loops, here we know that content was NOT
680 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700681 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700682 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
683 f.filterFinishArray();
684 }
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700685 boolean gotEnd = (_headContext == buffRoot);
686 boolean returnEnd = gotEnd && _headContext.isStartHandled();
687
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700688 _headContext = _headContext.getParent();
689 _itemFilter = _headContext.getFilter();
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700690
691 if (returnEnd) {
692 return t;
693 }
694 // Hmmh. Do we need both checks, or should above suffice?
695 if (gotEnd || (_headContext == buffRoot)) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700696 return null;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700697 }
698 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700699 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700700
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700701 case ID_FIELD_NAME:
702 {
703 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700704 f = _headContext.setFieldName(name);
705 if (f == TokenFilter.INCLUDE_ALL) {
706 _itemFilter = f;
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700707 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700708 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700709 if (f == null) { // filter out the value
710 delegate.nextToken();
711 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700712 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700713 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700714 f = f.includeProperty(name);
715 if (f == null) { // filter out the value
716 delegate.nextToken();
717 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700718 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700719 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700720 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700721 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700722 return _nextBuffered(buffRoot);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700723 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700724 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700725 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700726
727 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700728 f = _itemFilter;
729 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700730 return _nextBuffered(buffRoot);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700731 }
Cowtowncoder32224282015-04-16 16:39:22 -0700732 if (f != null) {
733 f = _headContext.checkValue(f);
734 if ((f == TokenFilter.INCLUDE_ALL)
735 || ((f != null) && f.includeValue(delegate))) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700736 return _nextBuffered(buffRoot);
Cowtowncoder32224282015-04-16 16:39:22 -0700737 }
738 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700739 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700740 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700741 }
742 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700743 }
744
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700745 private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700746 {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700747 _exposedContext = buffRoot;
748 TokenFilterContext ctxt = buffRoot;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700749 JsonToken t = ctxt.nextTokenToRead();
750 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700751 return t;
752 }
753 while (true) {
754 // all done with buffered stuff?
755 if (ctxt == _headContext) {
756 throw _constructError("Internal error: failed to locate expected buffered tokens");
757 /*
758 _exposedContext = null;
759 break;
760 */
761 }
762 // If not, traverse down the context chain
763 ctxt = _exposedContext.findChildOf(ctxt);
764 _exposedContext = ctxt;
765 if (ctxt == null) { // should never occur
766 throw _constructError("Unexpected problem: chain of filtered context broken");
767 }
768 t = _exposedContext.nextTokenToRead();
769 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700770 return t;
771 }
772 }
773 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700774
Tatu Salorantadfd69092015-04-09 22:29:34 -0700775 @Override
776 public JsonToken nextValue() throws IOException {
777 // Re-implemented same as ParserMinimalBase:
778 JsonToken t = nextToken();
779 if (t == JsonToken.FIELD_NAME) {
780 t = nextToken();
781 }
782 return t;
783 }
784
785 /**
786 * Need to override, re-implement similar to how method defined in
787 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
788 * state correct here.
789 */
790 @Override
791 public JsonParser skipChildren() throws IOException
792 {
Cowtowncoder00900312015-04-14 16:02:33 -0700793 if ((_currToken != JsonToken.START_OBJECT)
794 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700795 return this;
796 }
797 int open = 1;
798
799 // Since proper matching of start/end markers is handled
800 // by nextToken(), we'll just count nesting levels here
801 while (true) {
802 JsonToken t = nextToken();
803 if (t == null) { // not ideal but for now, just return
804 return this;
805 }
806 if (t.isStructStart()) {
807 ++open;
808 } else if (t.isStructEnd()) {
809 if (--open == 0) {
810 return this;
811 }
812 }
813 }
814 }
815
816 /*
817 /**********************************************************
818 /* Public API, access to token information, text
819 /**********************************************************
820 */
821
822 @Override public String getText() throws IOException { return delegate.getText(); }
823 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
824 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
825 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
826 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
827
828 /*
829 /**********************************************************
830 /* Public API, access to token information, numeric
831 /**********************************************************
832 */
833
834 @Override
835 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
836
837 @Override
838 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
839
840 @Override
841 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
842
843 @Override
844 public short getShortValue() throws IOException { return delegate.getShortValue(); }
845
846 @Override
847 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
848
849 @Override
850 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
851
852 @Override
853 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
854
855 @Override
856 public int getIntValue() throws IOException { return delegate.getIntValue(); }
857
858 @Override
859 public long getLongValue() throws IOException { return delegate.getLongValue(); }
860
861 @Override
862 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
863
864 @Override
865 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
866
867 /*
868 /**********************************************************
869 /* Public API, access to token information, coercion/conversion
870 /**********************************************************
871 */
872
873 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
874 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
875 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
876 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
877 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
878 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
879 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
880 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
881 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
882 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
883
884 /*
885 /**********************************************************
886 /* Public API, access to token values, other
887 /**********************************************************
888 */
889
890 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
891 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
892 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
893 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700894
895 /*
896 /**********************************************************
897 /* Internal helper methods
898 /**********************************************************
899 */
900
901 protected JsonStreamContext _filterContext() {
902 if (_exposedContext != null) {
903 return _exposedContext;
904 }
905 return _headContext;
906 }
LokeshNfd327722016-03-20 17:28:25 +0530907
Tatu Salorantadfd69092015-04-09 22:29:34 -0700908}