blob: 5274f33e390f726ec9e93f371a76ee8fe5049b41 [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
236 if (!_allowMultipleMatches && (_currToken != null) && (_exposedContext == null)) {
237 //if not scalar and ended successfully, and !includePath, then return null
238 if (!_includePath) {
239 if (_currToken.isStructEnd()) {
240 if (_headContext.isStartHandled()) {
241 return (_currToken = null);
242 }
243 } else if (_currToken.isScalarValue()) {
244 //else if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL matched once
245 // then return null
246 if (!_headContext.isStartHandled() && (_itemFilter == TokenFilter.INCLUDE_ALL)) {
247 return (_currToken = null);
248 }
249 }
250 }
251 }
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700252 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700253 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700254
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700255 if (ctxt != null) {
256 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700257 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700258 if (t != null) {
259 _currToken = t;
260 return t;
261 }
262 // all done with buffered stuff?
263 if (ctxt == _headContext) {
264 _exposedContext = null;
Cowtowncoderac4b4492015-04-23 15:36:50 -0700265 if (ctxt.inArray()) {
266 t = delegate.getCurrentToken();
267// Is this guaranteed to work without further checks?
268// if (t != JsonToken.START_ARRAY) {
269 _currToken = t;
270 return t;
271 }
272
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700273 // Almost! Most likely still have the current token;
274 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700275 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700276 t = delegate.getCurrentToken();
277 if (t != JsonToken.FIELD_NAME) {
278 _currToken = t;
279 return t;
280 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700281 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700282 break;
283 }
284 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700285 ctxt = _headContext.findChildOf(ctxt);
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700286 _exposedContext = ctxt;
287 if (ctxt == null) { // should never occur
288 throw _constructError("Unexpected problem: chain of filtered context broken");
289 }
290 }
291 }
292
Tatu Salorantab46e0372015-04-14 21:43:03 -0700293 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700294 JsonToken t = delegate.nextToken();
295 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700296 // no strict need to close, since we have no state here
Tatu Salorantae962fb92016-07-21 14:14:26 -0700297 _currToken = t;
298 return t;
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700299 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700300
Tatu Salorantab46e0372015-04-14 21:43:03 -0700301 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700302 TokenFilter f;
303
304 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700305 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700306 f = _itemFilter;
307 if (f == TokenFilter.INCLUDE_ALL) {
308 _headContext = _headContext.createChildArrayContext(f, true);
309 return (_currToken = t);
310 }
311 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700312 delegate.skipChildren();
313 break;
314 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700315 // Otherwise still iffy, need to check
316 f = _headContext.checkValue(f);
317 if (f == null) {
318 delegate.skipChildren();
319 break;
320 }
321 if (f != TokenFilter.INCLUDE_ALL) {
322 f = f.filterStartArray();
323 }
324 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700325 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700326 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700327 return (_currToken = t);
328 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700329 _headContext = _headContext.createChildArrayContext(f, false);
330
331 // Also: only need buffering if parent path to be included
332 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700333 t = _nextTokenWithBuffering(_headContext);
334 if (t != null) {
335 _currToken = t;
336 return t;
337 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700338 }
339 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700340
341 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700342 f = _itemFilter;
343 if (f == TokenFilter.INCLUDE_ALL) {
344 _headContext = _headContext.createChildObjectContext(f, true);
345 return (_currToken = t);
346 }
347 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700348 delegate.skipChildren();
349 break;
350 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700351 // Otherwise still iffy, need to check
352 f = _headContext.checkValue(f);
353 if (f == null) {
354 delegate.skipChildren();
355 break;
356 }
357 if (f != TokenFilter.INCLUDE_ALL) {
358 f = f.filterStartObject();
359 }
360 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700361 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700362 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700363 return (_currToken = t);
364 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700365 _headContext = _headContext.createChildObjectContext(f, false);
366 // Also: only need buffering if parent path to be included
367 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700368 t = _nextTokenWithBuffering(_headContext);
369 if (t != null) {
370 _currToken = t;
371 return t;
372 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700373 }
374 // note: inclusion of surrounding Object handled separately via
375 // FIELD_NAME
376 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700377
378 case ID_END_ARRAY:
379 case ID_END_OBJECT:
380 {
381 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700382 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700383 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
384 f.filterFinishArray();
385 }
386 _headContext = _headContext.getParent();
387 _itemFilter = _headContext.getFilter();
388 if (returnEnd) {
389 return (_currToken = t);
390 }
391 }
392 break;
393
394 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700395 {
396 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700397 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700398 f = _headContext.setFieldName(name);
399 if (f == TokenFilter.INCLUDE_ALL) {
400 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700401 if (!_includePath) {
402 // Minor twist here: if parent NOT included, may need to induce output of
403 // surrounding START_OBJECT/END_OBJECT
404 if (_includeImmediateParent && !_headContext.isStartHandled()) {
405 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
406 _exposedContext = _headContext;
407 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700408 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700409 return (_currToken = t);
410 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700411 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700412 delegate.nextToken();
413 delegate.skipChildren();
414 break;
415 }
416 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700417 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700418 delegate.nextToken();
419 delegate.skipChildren();
420 break;
421 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700422 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700423 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700424 if (_includePath) {
425 return (_currToken = t);
426 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700427 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700428 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700429 t = _nextTokenWithBuffering(_headContext);
430 if (t != null) {
431 _currToken = t;
432 return t;
433 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700434 }
435 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700436 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700437
438 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700439 f = _itemFilter;
440 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700441 return (_currToken = t);
442 }
Cowtowncoder32224282015-04-16 16:39:22 -0700443 if (f != null) {
444 f = _headContext.checkValue(f);
445 if ((f == TokenFilter.INCLUDE_ALL)
446 || ((f != null) && f.includeValue(delegate))) {
447 return (_currToken = t);
448 }
449 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700450 // Otherwise not included (leaves must be explicitly included)
451 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700452 }
453
454 // We get here if token was not yet found; offlined handling
455 return _nextToken2();
456 }
457
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700458 /**
459 * Offlined handling for cases where there was no buffered token to
460 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700461 * at least not yet, but where we have not yet established that
462 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700463 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700464 protected final JsonToken _nextToken2() throws IOException
465 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700466 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700467 while (true) {
468 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700469 if (t == null) { // is this even legal?
Tatu Salorantae962fb92016-07-21 14:14:26 -0700470 _currToken = t;
471 return t;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700472 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700473 TokenFilter f;
474
475 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700476 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700477 f = _itemFilter;
478 if (f == TokenFilter.INCLUDE_ALL) {
479 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700480 return (_currToken = t);
481 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700482 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700483 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700484 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700485 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700486 // Otherwise still iffy, need to check
487 f = _headContext.checkValue(f);
488 if (f == null) {
489 delegate.skipChildren();
490 continue main_loop;
491 }
492 if (f != TokenFilter.INCLUDE_ALL) {
493 f = f.filterStartArray();
494 }
495 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700496 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700497 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700498 return (_currToken = t);
499 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700500 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700501 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700502 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700503 t = _nextTokenWithBuffering(_headContext);
504 if (t != null) {
505 _currToken = t;
506 return t;
507 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700508 }
509 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700510
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700511 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700512 f = _itemFilter;
513 if (f == TokenFilter.INCLUDE_ALL) {
514 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700515 return (_currToken = t);
516 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700517 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700518 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700519 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700520 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700521 // Otherwise still iffy, need to check
522 f = _headContext.checkValue(f);
523 if (f == null) {
524 delegate.skipChildren();
525 continue main_loop;
526 }
527 if (f != TokenFilter.INCLUDE_ALL) {
528 f = f.filterStartObject();
529 }
530 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700531 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700532 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700533 return (_currToken = t);
534 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700535 _headContext = _headContext.createChildObjectContext(f, false);
536 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700537 t = _nextTokenWithBuffering(_headContext);
538 if (t != null) {
539 _currToken = t;
540 return t;
541 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700542 }
543 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700544
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700545 case ID_END_ARRAY:
546 case ID_END_OBJECT:
547 {
548 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700549 f = _headContext.getFilter();
550 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
551 f.filterFinishArray();
552 }
553 _headContext = _headContext.getParent();
554 _itemFilter = _headContext.getFilter();
555 if (returnEnd) {
556 return (_currToken = t);
557 }
558 }
559 continue main_loop;
560
561 case ID_FIELD_NAME:
562 {
563 final String name = delegate.getCurrentName();
564 f = _headContext.setFieldName(name);
565 if (f == TokenFilter.INCLUDE_ALL) {
566 _itemFilter = f;
567 return (_currToken = t);
568 }
569 if (f == null) { // filter out the value
570 delegate.nextToken();
571 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700572 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700573 }
574 f = f.includeProperty(name);
575 if (f == null) { // filter out the value
576 delegate.nextToken();
577 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700578 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700579 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700580 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700581 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700582 if (_includePath) {
583 return (_currToken = t);
584 }
585// if (_includeImmediateParent) { ...
586 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700587 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700588 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700589 t = _nextTokenWithBuffering(_headContext);
590 if (t != null) {
591 _currToken = t;
592 return t;
593 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700594 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700595 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700596 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700597
598 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700599 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700600 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700601 return (_currToken = t);
602 }
Cowtowncoder32224282015-04-16 16:39:22 -0700603 if (f != null) {
604 f = _headContext.checkValue(f);
605 if ((f == TokenFilter.INCLUDE_ALL)
606 || ((f != null) && f.includeValue(delegate))) {
607 return (_currToken = t);
608 }
609 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700610 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700611 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700612 }
613 }
614 }
615
616 /**
617 * Method called when a new potentially included context is found.
618 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700619 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
620 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700621 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700622 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700623 while (true) {
624 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700625 if (t == null) { // is this even legal?
Cowtowncoderb808f262015-04-23 15:47:39 -0700626 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700627 }
628 TokenFilter f;
629
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700630 // One simplification here: we know for a fact that the item filter is
631 // neither null nor 'include all', for most cases; the only exception
632 // being FIELD_NAME handling
633
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700634 switch (t.id()) {
635 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700636 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700637 if (f == null) {
638 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700639 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700640 }
641 if (f != TokenFilter.INCLUDE_ALL) {
642 f = f.filterStartArray();
643 }
644 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700645 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700646 _headContext = _headContext.createChildArrayContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700647 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700648 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700649 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700650 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700651
652 case ID_START_OBJECT:
653 f = _itemFilter;
654 if (f == TokenFilter.INCLUDE_ALL) {
655 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoderb808f262015-04-23 15:47:39 -0700656 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700657 }
658 if (f == null) { // does this occur?
659 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700660 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700661 }
662 // Otherwise still iffy, need to check
663 f = _headContext.checkValue(f);
664 if (f == null) {
665 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700666 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700667 }
668 if (f != TokenFilter.INCLUDE_ALL) {
669 f = f.filterStartObject();
670 }
671 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700672 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700673 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700674 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700675 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700676 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700677 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700678
679 case ID_END_ARRAY:
680 case ID_END_OBJECT:
681 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700682 // Unlike with other loops, here we know that content was NOT
683 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700684 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700685 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
686 f.filterFinishArray();
687 }
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700688 boolean gotEnd = (_headContext == buffRoot);
689 boolean returnEnd = gotEnd && _headContext.isStartHandled();
690
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700691 _headContext = _headContext.getParent();
692 _itemFilter = _headContext.getFilter();
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700693
694 if (returnEnd) {
695 return t;
696 }
697 // Hmmh. Do we need both checks, or should above suffice?
698 if (gotEnd || (_headContext == buffRoot)) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700699 return null;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700700 }
701 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700702 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700703
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700704 case ID_FIELD_NAME:
705 {
706 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700707 f = _headContext.setFieldName(name);
708 if (f == TokenFilter.INCLUDE_ALL) {
709 _itemFilter = f;
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700710 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700711 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700712 if (f == null) { // filter out the value
713 delegate.nextToken();
714 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700715 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700716 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700717 f = f.includeProperty(name);
718 if (f == null) { // filter out the value
719 delegate.nextToken();
720 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700721 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700722 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700723 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700724 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700725 return _nextBuffered(buffRoot);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700726 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700727 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700728 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700729
730 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700731 f = _itemFilter;
732 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700733 return _nextBuffered(buffRoot);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700734 }
Cowtowncoder32224282015-04-16 16:39:22 -0700735 if (f != null) {
736 f = _headContext.checkValue(f);
737 if ((f == TokenFilter.INCLUDE_ALL)
738 || ((f != null) && f.includeValue(delegate))) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700739 return _nextBuffered(buffRoot);
Cowtowncoder32224282015-04-16 16:39:22 -0700740 }
741 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700742 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700743 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700744 }
745 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700746 }
747
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700748 private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700749 {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700750 _exposedContext = buffRoot;
751 TokenFilterContext ctxt = buffRoot;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700752 JsonToken t = ctxt.nextTokenToRead();
753 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700754 return t;
755 }
756 while (true) {
757 // all done with buffered stuff?
758 if (ctxt == _headContext) {
759 throw _constructError("Internal error: failed to locate expected buffered tokens");
760 /*
761 _exposedContext = null;
762 break;
763 */
764 }
765 // If not, traverse down the context chain
766 ctxt = _exposedContext.findChildOf(ctxt);
767 _exposedContext = ctxt;
768 if (ctxt == null) { // should never occur
769 throw _constructError("Unexpected problem: chain of filtered context broken");
770 }
771 t = _exposedContext.nextTokenToRead();
772 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700773 return t;
774 }
775 }
776 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700777
Tatu Salorantadfd69092015-04-09 22:29:34 -0700778 @Override
779 public JsonToken nextValue() throws IOException {
780 // Re-implemented same as ParserMinimalBase:
781 JsonToken t = nextToken();
782 if (t == JsonToken.FIELD_NAME) {
783 t = nextToken();
784 }
785 return t;
786 }
787
788 /**
789 * Need to override, re-implement similar to how method defined in
790 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
791 * state correct here.
792 */
793 @Override
794 public JsonParser skipChildren() throws IOException
795 {
Cowtowncoder00900312015-04-14 16:02:33 -0700796 if ((_currToken != JsonToken.START_OBJECT)
797 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700798 return this;
799 }
800 int open = 1;
801
802 // Since proper matching of start/end markers is handled
803 // by nextToken(), we'll just count nesting levels here
804 while (true) {
805 JsonToken t = nextToken();
806 if (t == null) { // not ideal but for now, just return
807 return this;
808 }
809 if (t.isStructStart()) {
810 ++open;
811 } else if (t.isStructEnd()) {
812 if (--open == 0) {
813 return this;
814 }
815 }
816 }
817 }
818
819 /*
820 /**********************************************************
821 /* Public API, access to token information, text
822 /**********************************************************
823 */
824
825 @Override public String getText() throws IOException { return delegate.getText(); }
826 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
827 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
828 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
829 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
830
831 /*
832 /**********************************************************
833 /* Public API, access to token information, numeric
834 /**********************************************************
835 */
836
837 @Override
838 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
839
840 @Override
841 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
842
843 @Override
844 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
845
846 @Override
847 public short getShortValue() throws IOException { return delegate.getShortValue(); }
848
849 @Override
850 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
851
852 @Override
853 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
854
855 @Override
856 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
857
858 @Override
859 public int getIntValue() throws IOException { return delegate.getIntValue(); }
860
861 @Override
862 public long getLongValue() throws IOException { return delegate.getLongValue(); }
863
864 @Override
865 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
866
867 @Override
868 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
869
870 /*
871 /**********************************************************
872 /* Public API, access to token information, coercion/conversion
873 /**********************************************************
874 */
875
876 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
877 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
878 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
879 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
880 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
881 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
882 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
883 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
884 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
885 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
886
887 /*
888 /**********************************************************
889 /* Public API, access to token values, other
890 /**********************************************************
891 */
892
893 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
894 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
895 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
896 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700897
898 /*
899 /**********************************************************
900 /* Internal helper methods
901 /**********************************************************
902 */
903
904 protected JsonStreamContext _filterContext() {
905 if (_exposedContext != null) {
906 return _exposedContext;
907 }
908 return _headContext;
909 }
LokeshNfd327722016-03-20 17:28:25 +0530910
Tatu Salorantadfd69092015-04-09 22:29:34 -0700911}