blob: bf68cd773b9158cd5807a3eff2ac41745f06b1cf [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; }
149
150 @Override public final int getCurrentTokenId() {
151 final JsonToken t = _currToken;
152 return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
153 }
154
155 @Override public boolean hasCurrentToken() { return _currToken != null; }
156 @Override public boolean hasTokenId(int id) {
157 final JsonToken t = _currToken;
158 if (t == null) {
159 return (JsonTokenId.ID_NO_TOKEN == id);
160 }
161 return t.id() == id;
162 }
163
164 @Override public final boolean hasToken(JsonToken t) {
165 return (_currToken == t);
166 }
167
168 @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
169 @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
170
171 @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700172
Cowtowncoder00900312015-04-14 16:02:33 -0700173 @Override
174 public JsonStreamContext getParsingContext() {
175 return _filterContext();
176 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700177
Cowtowncoder00900312015-04-14 16:02:33 -0700178 // !!! TODO: Verify it works as expected: copied from standard JSON parser impl
179 @Override
180 public String getCurrentName() throws IOException {
181 JsonStreamContext ctxt = _filterContext();
182 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
183 JsonStreamContext parent = ctxt.getParent();
184 return (parent == null) ? null : parent.getCurrentName();
185 }
186 return ctxt.getCurrentName();
187 }
188
Tatu Salorantadfd69092015-04-09 22:29:34 -0700189 /*
190 /**********************************************************
191 /* Public API, token state overrides
192 /**********************************************************
193 */
194
195 @Override
196 public void clearCurrentToken() {
197 if (_currToken != null) {
198 _lastClearedToken = _currToken;
199 _currToken = null;
200 }
201 }
202
203 @Override
204 public JsonToken getLastClearedToken() { return _lastClearedToken; }
205
Tatu Salorantadfd69092015-04-09 22:29:34 -0700206 @Override
Cowtowncoder00900312015-04-14 16:02:33 -0700207 public void overrideCurrentName(String name) {
208 /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so,
209 * what to do with it... Delegation won't work for sure, so let's for
210 * now throw an exception
211 */
212 throw new UnsupportedOperationException("Can not currently override name during filtering read");
213 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700214
215 /*
216 /**********************************************************
217 /* Public API, traversal
218 /**********************************************************
219 */
220
Cowtowncoder00900312015-04-14 16:02:33 -0700221 @Override
222 public JsonToken nextToken() throws IOException
223 {
LokeshNfd327722016-03-20 17:28:25 +0530224 //Check for _allowMultipleMatches - false and atleast there is one token - which is _currToken
225 // check for no buffered context _exposedContext - null
226 //If all the conditions matches then check for scalar / non-scalar property
227 if(!_allowMultipleMatches && _currToken != null && _exposedContext == null){
228 //if not scalar and ended successfully, then return null
229 if((_currToken.isStructEnd() && _headContext.isStartHandled()) ){
230 return (_currToken = null);
231 }
232 //else if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL matched once
233 // then return null
234 else if(_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath
235 && _itemFilter == TokenFilter.INCLUDE_ALL) {
236 return (_currToken = null);
237 }
238 }
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700239 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700240 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700241
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700242 if (ctxt != null) {
243 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700244 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700245 if (t != null) {
246 _currToken = t;
247 return t;
248 }
249 // all done with buffered stuff?
250 if (ctxt == _headContext) {
251 _exposedContext = null;
Cowtowncoderac4b4492015-04-23 15:36:50 -0700252 if (ctxt.inArray()) {
253 t = delegate.getCurrentToken();
254// Is this guaranteed to work without further checks?
255// if (t != JsonToken.START_ARRAY) {
256 _currToken = t;
257 return t;
258 }
259
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700260 // Almost! Most likely still have the current token;
261 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700262 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700263 t = delegate.getCurrentToken();
264 if (t != JsonToken.FIELD_NAME) {
265 _currToken = t;
266 return t;
267 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700268 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700269 break;
270 }
271 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700272 ctxt = _headContext.findChildOf(ctxt);
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700273 _exposedContext = ctxt;
274 if (ctxt == null) { // should never occur
275 throw _constructError("Unexpected problem: chain of filtered context broken");
276 }
277 }
278 }
279
Tatu Salorantab46e0372015-04-14 21:43:03 -0700280 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700281 JsonToken t = delegate.nextToken();
282 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700283 // no strict need to close, since we have no state here
284 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700285 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700286
Tatu Salorantab46e0372015-04-14 21:43:03 -0700287 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700288 TokenFilter f;
289
290 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700291 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700292 f = _itemFilter;
293 if (f == TokenFilter.INCLUDE_ALL) {
294 _headContext = _headContext.createChildArrayContext(f, true);
295 return (_currToken = t);
296 }
297 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700298 delegate.skipChildren();
299 break;
300 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700301 // Otherwise still iffy, need to check
302 f = _headContext.checkValue(f);
303 if (f == null) {
304 delegate.skipChildren();
305 break;
306 }
307 if (f != TokenFilter.INCLUDE_ALL) {
308 f = f.filterStartArray();
309 }
310 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700311 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700312 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700313 return (_currToken = t);
314 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700315 _headContext = _headContext.createChildArrayContext(f, false);
316
317 // Also: only need buffering if parent path to be included
318 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700319 t = _nextTokenWithBuffering(_headContext);
320 if (t != null) {
321 _currToken = t;
322 return t;
323 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700324 }
325 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700326
327 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700328 f = _itemFilter;
329 if (f == TokenFilter.INCLUDE_ALL) {
330 _headContext = _headContext.createChildObjectContext(f, true);
331 return (_currToken = t);
332 }
333 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700334 delegate.skipChildren();
335 break;
336 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700337 // Otherwise still iffy, need to check
338 f = _headContext.checkValue(f);
339 if (f == null) {
340 delegate.skipChildren();
341 break;
342 }
343 if (f != TokenFilter.INCLUDE_ALL) {
344 f = f.filterStartObject();
345 }
346 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700347 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700348 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700349 return (_currToken = t);
350 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700351 _headContext = _headContext.createChildObjectContext(f, false);
352 // Also: only need buffering if parent path to be included
353 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700354 t = _nextTokenWithBuffering(_headContext);
355 if (t != null) {
356 _currToken = t;
357 return t;
358 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700359 }
360 // note: inclusion of surrounding Object handled separately via
361 // FIELD_NAME
362 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700363
364 case ID_END_ARRAY:
365 case ID_END_OBJECT:
366 {
367 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700368 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700369 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
370 f.filterFinishArray();
371 }
372 _headContext = _headContext.getParent();
373 _itemFilter = _headContext.getFilter();
374 if (returnEnd) {
375 return (_currToken = t);
376 }
377 }
378 break;
379
380 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700381 {
382 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700383 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700384 f = _headContext.setFieldName(name);
385 if (f == TokenFilter.INCLUDE_ALL) {
386 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700387 if (!_includePath) {
388 // Minor twist here: if parent NOT included, may need to induce output of
389 // surrounding START_OBJECT/END_OBJECT
390 if (_includeImmediateParent && !_headContext.isStartHandled()) {
391 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
392 _exposedContext = _headContext;
393 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700394 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700395 return (_currToken = t);
396 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700397 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700398 delegate.nextToken();
399 delegate.skipChildren();
400 break;
401 }
402 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700403 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700404 delegate.nextToken();
405 delegate.skipChildren();
406 break;
407 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700408 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700409 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700410 if (_includePath) {
411 return (_currToken = t);
412 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700413 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700414 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700415 t = _nextTokenWithBuffering(_headContext);
416 if (t != null) {
417 _currToken = t;
418 return t;
419 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700420 }
421 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700422 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700423
424 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700425 f = _itemFilter;
426 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700427 return (_currToken = t);
428 }
Cowtowncoder32224282015-04-16 16:39:22 -0700429 if (f != null) {
430 f = _headContext.checkValue(f);
431 if ((f == TokenFilter.INCLUDE_ALL)
432 || ((f != null) && f.includeValue(delegate))) {
433 return (_currToken = t);
434 }
435 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700436 // Otherwise not included (leaves must be explicitly included)
437 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700438 }
439
440 // We get here if token was not yet found; offlined handling
441 return _nextToken2();
442 }
443
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700444 /**
445 * Offlined handling for cases where there was no buffered token to
446 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700447 * at least not yet, but where we have not yet established that
448 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700449 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700450 protected final JsonToken _nextToken2() throws IOException
451 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700452 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700453 while (true) {
454 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700455 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700456 return (_currToken = t);
457 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700458 TokenFilter f;
459
460 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700461 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700462 f = _itemFilter;
463 if (f == TokenFilter.INCLUDE_ALL) {
464 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700465 return (_currToken = t);
466 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700467 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700468 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700469 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700470 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700471 // Otherwise still iffy, need to check
472 f = _headContext.checkValue(f);
473 if (f == null) {
474 delegate.skipChildren();
475 continue main_loop;
476 }
477 if (f != TokenFilter.INCLUDE_ALL) {
478 f = f.filterStartArray();
479 }
480 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700481 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700482 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700483 return (_currToken = t);
484 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700485 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700486 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700487 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700488 t = _nextTokenWithBuffering(_headContext);
489 if (t != null) {
490 _currToken = t;
491 return t;
492 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700493 }
494 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700495
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700496 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700497 f = _itemFilter;
498 if (f == TokenFilter.INCLUDE_ALL) {
499 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700500 return (_currToken = t);
501 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700502 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700503 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700504 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700505 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700506 // Otherwise still iffy, need to check
507 f = _headContext.checkValue(f);
508 if (f == null) {
509 delegate.skipChildren();
510 continue main_loop;
511 }
512 if (f != TokenFilter.INCLUDE_ALL) {
513 f = f.filterStartObject();
514 }
515 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700516 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700517 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700518 return (_currToken = t);
519 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700520 _headContext = _headContext.createChildObjectContext(f, false);
521 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700522 t = _nextTokenWithBuffering(_headContext);
523 if (t != null) {
524 _currToken = t;
525 return t;
526 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700527 }
528 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700529
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700530 case ID_END_ARRAY:
531 case ID_END_OBJECT:
532 {
533 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700534 f = _headContext.getFilter();
535 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
536 f.filterFinishArray();
537 }
538 _headContext = _headContext.getParent();
539 _itemFilter = _headContext.getFilter();
540 if (returnEnd) {
541 return (_currToken = t);
542 }
543 }
544 continue main_loop;
545
546 case ID_FIELD_NAME:
547 {
548 final String name = delegate.getCurrentName();
549 f = _headContext.setFieldName(name);
550 if (f == TokenFilter.INCLUDE_ALL) {
551 _itemFilter = f;
552 return (_currToken = t);
553 }
554 if (f == null) { // filter out the value
555 delegate.nextToken();
556 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700557 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700558 }
559 f = f.includeProperty(name);
560 if (f == null) { // filter out the value
561 delegate.nextToken();
562 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700563 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700564 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700565 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700566 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700567 if (_includePath) {
568 return (_currToken = t);
569 }
570// if (_includeImmediateParent) { ...
571 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700572 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700573 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700574 t = _nextTokenWithBuffering(_headContext);
575 if (t != null) {
576 _currToken = t;
577 return t;
578 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700579 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700580 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700581 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700582
583 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700584 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700585 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700586 return (_currToken = t);
587 }
Cowtowncoder32224282015-04-16 16:39:22 -0700588 if (f != null) {
589 f = _headContext.checkValue(f);
590 if ((f == TokenFilter.INCLUDE_ALL)
591 || ((f != null) && f.includeValue(delegate))) {
592 return (_currToken = t);
593 }
594 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700595 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700596 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700597 }
598 }
599 }
600
601 /**
602 * Method called when a new potentially included context is found.
603 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700604 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
605 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700606 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700607 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700608 while (true) {
609 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700610 if (t == null) { // is this even legal?
Cowtowncoderb808f262015-04-23 15:47:39 -0700611 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700612 }
613 TokenFilter f;
614
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700615 // One simplification here: we know for a fact that the item filter is
616 // neither null nor 'include all', for most cases; the only exception
617 // being FIELD_NAME handling
618
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700619 switch (t.id()) {
620 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700621 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700622 if (f == null) {
623 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700624 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700625 }
626 if (f != TokenFilter.INCLUDE_ALL) {
627 f = f.filterStartArray();
628 }
629 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700630 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700631 _headContext = _headContext.createChildArrayContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700632 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700633 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700634 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700635 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700636
637 case ID_START_OBJECT:
638 f = _itemFilter;
639 if (f == TokenFilter.INCLUDE_ALL) {
640 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoderb808f262015-04-23 15:47:39 -0700641 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700642 }
643 if (f == null) { // does this occur?
644 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700645 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700646 }
647 // Otherwise still iffy, need to check
648 f = _headContext.checkValue(f);
649 if (f == null) {
650 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700651 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700652 }
653 if (f != TokenFilter.INCLUDE_ALL) {
654 f = f.filterStartObject();
655 }
656 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700657 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700658 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700659 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700660 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700661 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700662 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700663
664 case ID_END_ARRAY:
665 case ID_END_OBJECT:
666 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700667 // Unlike with other loops, here we know that content was NOT
668 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700669 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700670 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
671 f.filterFinishArray();
672 }
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700673 boolean gotEnd = (_headContext == buffRoot);
674 boolean returnEnd = gotEnd && _headContext.isStartHandled();
675
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700676 _headContext = _headContext.getParent();
677 _itemFilter = _headContext.getFilter();
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700678
679 if (returnEnd) {
680 return t;
681 }
682 // Hmmh. Do we need both checks, or should above suffice?
683 if (gotEnd || (_headContext == buffRoot)) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700684 return null;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700685 }
686 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700687 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700688
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700689 case ID_FIELD_NAME:
690 {
691 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700692 f = _headContext.setFieldName(name);
693 if (f == TokenFilter.INCLUDE_ALL) {
694 _itemFilter = f;
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700695 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700696 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700697 if (f == null) { // filter out the value
698 delegate.nextToken();
699 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700700 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700701 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700702 f = f.includeProperty(name);
703 if (f == null) { // filter out the value
704 delegate.nextToken();
705 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700706 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700707 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700708 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700709 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700710 return _nextBuffered(buffRoot);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700711 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700712 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700713 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700714
715 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700716 f = _itemFilter;
717 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700718 return _nextBuffered(buffRoot);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700719 }
Cowtowncoder32224282015-04-16 16:39:22 -0700720 if (f != null) {
721 f = _headContext.checkValue(f);
722 if ((f == TokenFilter.INCLUDE_ALL)
723 || ((f != null) && f.includeValue(delegate))) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700724 return _nextBuffered(buffRoot);
Cowtowncoder32224282015-04-16 16:39:22 -0700725 }
726 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700727 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700728 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700729 }
730 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700731 }
732
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700733 private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700734 {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700735 _exposedContext = buffRoot;
736 TokenFilterContext ctxt = buffRoot;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700737 JsonToken t = ctxt.nextTokenToRead();
738 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700739 return t;
740 }
741 while (true) {
742 // all done with buffered stuff?
743 if (ctxt == _headContext) {
744 throw _constructError("Internal error: failed to locate expected buffered tokens");
745 /*
746 _exposedContext = null;
747 break;
748 */
749 }
750 // If not, traverse down the context chain
751 ctxt = _exposedContext.findChildOf(ctxt);
752 _exposedContext = ctxt;
753 if (ctxt == null) { // should never occur
754 throw _constructError("Unexpected problem: chain of filtered context broken");
755 }
756 t = _exposedContext.nextTokenToRead();
757 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700758 return t;
759 }
760 }
761 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700762
Tatu Salorantadfd69092015-04-09 22:29:34 -0700763 @Override
764 public JsonToken nextValue() throws IOException {
765 // Re-implemented same as ParserMinimalBase:
766 JsonToken t = nextToken();
767 if (t == JsonToken.FIELD_NAME) {
768 t = nextToken();
769 }
770 return t;
771 }
772
773 /**
774 * Need to override, re-implement similar to how method defined in
775 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
776 * state correct here.
777 */
778 @Override
779 public JsonParser skipChildren() throws IOException
780 {
Cowtowncoder00900312015-04-14 16:02:33 -0700781 if ((_currToken != JsonToken.START_OBJECT)
782 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700783 return this;
784 }
785 int open = 1;
786
787 // Since proper matching of start/end markers is handled
788 // by nextToken(), we'll just count nesting levels here
789 while (true) {
790 JsonToken t = nextToken();
791 if (t == null) { // not ideal but for now, just return
792 return this;
793 }
794 if (t.isStructStart()) {
795 ++open;
796 } else if (t.isStructEnd()) {
797 if (--open == 0) {
798 return this;
799 }
800 }
801 }
802 }
803
804 /*
805 /**********************************************************
806 /* Public API, access to token information, text
807 /**********************************************************
808 */
809
810 @Override public String getText() throws IOException { return delegate.getText(); }
811 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
812 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
813 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
814 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
815
816 /*
817 /**********************************************************
818 /* Public API, access to token information, numeric
819 /**********************************************************
820 */
821
822 @Override
823 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
824
825 @Override
826 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
827
828 @Override
829 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
830
831 @Override
832 public short getShortValue() throws IOException { return delegate.getShortValue(); }
833
834 @Override
835 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
836
837 @Override
838 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
839
840 @Override
841 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
842
843 @Override
844 public int getIntValue() throws IOException { return delegate.getIntValue(); }
845
846 @Override
847 public long getLongValue() throws IOException { return delegate.getLongValue(); }
848
849 @Override
850 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
851
852 @Override
853 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
854
855 /*
856 /**********************************************************
857 /* Public API, access to token information, coercion/conversion
858 /**********************************************************
859 */
860
861 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
862 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
863 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
864 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
865 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
866 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
867 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
868 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
869 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
870 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
871
872 /*
873 /**********************************************************
874 /* Public API, access to token values, other
875 /**********************************************************
876 */
877
878 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
879 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
880 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
881 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700882
883 /*
884 /**********************************************************
885 /* Internal helper methods
886 /**********************************************************
887 */
888
889 protected JsonStreamContext _filterContext() {
890 if (_exposedContext != null) {
891 return _exposedContext;
892 }
893 return _headContext;
894 }
LokeshNfd327722016-03-20 17:28:25 +0530895
Tatu Salorantadfd69092015-04-09 22:29:34 -0700896}