blob: d395b11d37d357c4d11e2e2dd93c2fb18dd27d0d [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
Tatu Salorantae962fb92016-07-21 14:14:26 -0700284 _currToken = t;
285 return t;
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700286 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700287
Tatu Salorantab46e0372015-04-14 21:43:03 -0700288 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700289 TokenFilter f;
290
291 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700292 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700293 f = _itemFilter;
294 if (f == TokenFilter.INCLUDE_ALL) {
295 _headContext = _headContext.createChildArrayContext(f, true);
296 return (_currToken = t);
297 }
298 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700299 delegate.skipChildren();
300 break;
301 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700302 // Otherwise still iffy, need to check
303 f = _headContext.checkValue(f);
304 if (f == null) {
305 delegate.skipChildren();
306 break;
307 }
308 if (f != TokenFilter.INCLUDE_ALL) {
309 f = f.filterStartArray();
310 }
311 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700312 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700313 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700314 return (_currToken = t);
315 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700316 _headContext = _headContext.createChildArrayContext(f, false);
317
318 // Also: only need buffering if parent path to be included
319 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700320 t = _nextTokenWithBuffering(_headContext);
321 if (t != null) {
322 _currToken = t;
323 return t;
324 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700325 }
326 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700327
328 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700329 f = _itemFilter;
330 if (f == TokenFilter.INCLUDE_ALL) {
331 _headContext = _headContext.createChildObjectContext(f, true);
332 return (_currToken = t);
333 }
334 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700335 delegate.skipChildren();
336 break;
337 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700338 // Otherwise still iffy, need to check
339 f = _headContext.checkValue(f);
340 if (f == null) {
341 delegate.skipChildren();
342 break;
343 }
344 if (f != TokenFilter.INCLUDE_ALL) {
345 f = f.filterStartObject();
346 }
347 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700348 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700349 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700350 return (_currToken = t);
351 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700352 _headContext = _headContext.createChildObjectContext(f, false);
353 // Also: only need buffering if parent path to be included
354 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700355 t = _nextTokenWithBuffering(_headContext);
356 if (t != null) {
357 _currToken = t;
358 return t;
359 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700360 }
361 // note: inclusion of surrounding Object handled separately via
362 // FIELD_NAME
363 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700364
365 case ID_END_ARRAY:
366 case ID_END_OBJECT:
367 {
368 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700369 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700370 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
371 f.filterFinishArray();
372 }
373 _headContext = _headContext.getParent();
374 _itemFilter = _headContext.getFilter();
375 if (returnEnd) {
376 return (_currToken = t);
377 }
378 }
379 break;
380
381 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700382 {
383 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700384 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700385 f = _headContext.setFieldName(name);
386 if (f == TokenFilter.INCLUDE_ALL) {
387 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700388 if (!_includePath) {
389 // Minor twist here: if parent NOT included, may need to induce output of
390 // surrounding START_OBJECT/END_OBJECT
391 if (_includeImmediateParent && !_headContext.isStartHandled()) {
392 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
393 _exposedContext = _headContext;
394 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700395 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700396 return (_currToken = t);
397 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700398 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700399 delegate.nextToken();
400 delegate.skipChildren();
401 break;
402 }
403 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700404 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700405 delegate.nextToken();
406 delegate.skipChildren();
407 break;
408 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700409 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700410 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700411 if (_includePath) {
412 return (_currToken = t);
413 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700414 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700415 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700416 t = _nextTokenWithBuffering(_headContext);
417 if (t != null) {
418 _currToken = t;
419 return t;
420 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700421 }
422 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700423 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700424
425 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700426 f = _itemFilter;
427 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700428 return (_currToken = t);
429 }
Cowtowncoder32224282015-04-16 16:39:22 -0700430 if (f != null) {
431 f = _headContext.checkValue(f);
432 if ((f == TokenFilter.INCLUDE_ALL)
433 || ((f != null) && f.includeValue(delegate))) {
434 return (_currToken = t);
435 }
436 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700437 // Otherwise not included (leaves must be explicitly included)
438 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700439 }
440
441 // We get here if token was not yet found; offlined handling
442 return _nextToken2();
443 }
444
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700445 /**
446 * Offlined handling for cases where there was no buffered token to
447 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700448 * at least not yet, but where we have not yet established that
449 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700450 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700451 protected final JsonToken _nextToken2() throws IOException
452 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700453 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700454 while (true) {
455 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700456 if (t == null) { // is this even legal?
Tatu Salorantae962fb92016-07-21 14:14:26 -0700457 _currToken = t;
458 return t;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700459 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700460 TokenFilter f;
461
462 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700463 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700464 f = _itemFilter;
465 if (f == TokenFilter.INCLUDE_ALL) {
466 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700467 return (_currToken = t);
468 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700469 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700470 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700471 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700472 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700473 // Otherwise still iffy, need to check
474 f = _headContext.checkValue(f);
475 if (f == null) {
476 delegate.skipChildren();
477 continue main_loop;
478 }
479 if (f != TokenFilter.INCLUDE_ALL) {
480 f = f.filterStartArray();
481 }
482 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700483 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700484 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700485 return (_currToken = t);
486 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700487 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700488 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700489 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700490 t = _nextTokenWithBuffering(_headContext);
491 if (t != null) {
492 _currToken = t;
493 return t;
494 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700495 }
496 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700497
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700498 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700499 f = _itemFilter;
500 if (f == TokenFilter.INCLUDE_ALL) {
501 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700502 return (_currToken = t);
503 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700504 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700505 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700506 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700507 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700508 // Otherwise still iffy, need to check
509 f = _headContext.checkValue(f);
510 if (f == null) {
511 delegate.skipChildren();
512 continue main_loop;
513 }
514 if (f != TokenFilter.INCLUDE_ALL) {
515 f = f.filterStartObject();
516 }
517 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700518 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700519 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700520 return (_currToken = t);
521 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700522 _headContext = _headContext.createChildObjectContext(f, false);
523 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700524 t = _nextTokenWithBuffering(_headContext);
525 if (t != null) {
526 _currToken = t;
527 return t;
528 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700529 }
530 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700531
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700532 case ID_END_ARRAY:
533 case ID_END_OBJECT:
534 {
535 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700536 f = _headContext.getFilter();
537 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
538 f.filterFinishArray();
539 }
540 _headContext = _headContext.getParent();
541 _itemFilter = _headContext.getFilter();
542 if (returnEnd) {
543 return (_currToken = t);
544 }
545 }
546 continue main_loop;
547
548 case ID_FIELD_NAME:
549 {
550 final String name = delegate.getCurrentName();
551 f = _headContext.setFieldName(name);
552 if (f == TokenFilter.INCLUDE_ALL) {
553 _itemFilter = f;
554 return (_currToken = t);
555 }
556 if (f == null) { // filter out the value
557 delegate.nextToken();
558 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700559 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700560 }
561 f = f.includeProperty(name);
562 if (f == null) { // filter out the value
563 delegate.nextToken();
564 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700565 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700566 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700567 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700568 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700569 if (_includePath) {
570 return (_currToken = t);
571 }
572// if (_includeImmediateParent) { ...
573 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700574 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700575 if (_includePath) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700576 t = _nextTokenWithBuffering(_headContext);
577 if (t != null) {
578 _currToken = t;
579 return t;
580 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700581 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700582 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700583 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700584
585 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700586 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700587 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700588 return (_currToken = t);
589 }
Cowtowncoder32224282015-04-16 16:39:22 -0700590 if (f != null) {
591 f = _headContext.checkValue(f);
592 if ((f == TokenFilter.INCLUDE_ALL)
593 || ((f != null) && f.includeValue(delegate))) {
594 return (_currToken = t);
595 }
596 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700597 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700598 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700599 }
600 }
601 }
602
603 /**
604 * Method called when a new potentially included context is found.
605 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700606 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
607 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700608 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700609 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700610 while (true) {
611 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700612 if (t == null) { // is this even legal?
Cowtowncoderb808f262015-04-23 15:47:39 -0700613 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700614 }
615 TokenFilter f;
616
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700617 // One simplification here: we know for a fact that the item filter is
618 // neither null nor 'include all', for most cases; the only exception
619 // being FIELD_NAME handling
620
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700621 switch (t.id()) {
622 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700623 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700624 if (f == null) {
625 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700626 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700627 }
628 if (f != TokenFilter.INCLUDE_ALL) {
629 f = f.filterStartArray();
630 }
631 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700632 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700633 _headContext = _headContext.createChildArrayContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700634 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700635 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700636 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700637 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700638
639 case ID_START_OBJECT:
640 f = _itemFilter;
641 if (f == TokenFilter.INCLUDE_ALL) {
642 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoderb808f262015-04-23 15:47:39 -0700643 return t;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700644 }
645 if (f == null) { // does this occur?
646 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700647 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700648 }
649 // Otherwise still iffy, need to check
650 f = _headContext.checkValue(f);
651 if (f == null) {
652 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700653 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700654 }
655 if (f != TokenFilter.INCLUDE_ALL) {
656 f = f.filterStartObject();
657 }
658 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700659 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700660 _headContext = _headContext.createChildObjectContext(f, true);
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700661 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700662 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700663 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700664 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700665
666 case ID_END_ARRAY:
667 case ID_END_OBJECT:
668 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700669 // Unlike with other loops, here we know that content was NOT
670 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700671 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700672 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
673 f.filterFinishArray();
674 }
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700675 boolean gotEnd = (_headContext == buffRoot);
676 boolean returnEnd = gotEnd && _headContext.isStartHandled();
677
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700678 _headContext = _headContext.getParent();
679 _itemFilter = _headContext.getFilter();
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700680
681 if (returnEnd) {
682 return t;
683 }
684 // Hmmh. Do we need both checks, or should above suffice?
685 if (gotEnd || (_headContext == buffRoot)) {
Cowtowncoderb808f262015-04-23 15:47:39 -0700686 return null;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700687 }
688 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700689 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700690
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700691 case ID_FIELD_NAME:
692 {
693 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700694 f = _headContext.setFieldName(name);
695 if (f == TokenFilter.INCLUDE_ALL) {
696 _itemFilter = f;
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700697 return _nextBuffered(buffRoot);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700698 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700699 if (f == null) { // filter out the value
700 delegate.nextToken();
701 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700702 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700703 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700704 f = f.includeProperty(name);
705 if (f == null) { // filter out the value
706 delegate.nextToken();
707 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700708 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700709 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700710 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700711 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700712 return _nextBuffered(buffRoot);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700713 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700714 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700715 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700716
717 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700718 f = _itemFilter;
719 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700720 return _nextBuffered(buffRoot);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700721 }
Cowtowncoder32224282015-04-16 16:39:22 -0700722 if (f != null) {
723 f = _headContext.checkValue(f);
724 if ((f == TokenFilter.INCLUDE_ALL)
725 || ((f != null) && f.includeValue(delegate))) {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700726 return _nextBuffered(buffRoot);
Cowtowncoder32224282015-04-16 16:39:22 -0700727 }
728 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700729 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700730 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700731 }
732 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700733 }
734
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700735 private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700736 {
Cowtowncoder7f7d10a2015-04-29 16:45:02 -0700737 _exposedContext = buffRoot;
738 TokenFilterContext ctxt = buffRoot;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700739 JsonToken t = ctxt.nextTokenToRead();
740 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700741 return t;
742 }
743 while (true) {
744 // all done with buffered stuff?
745 if (ctxt == _headContext) {
746 throw _constructError("Internal error: failed to locate expected buffered tokens");
747 /*
748 _exposedContext = null;
749 break;
750 */
751 }
752 // If not, traverse down the context chain
753 ctxt = _exposedContext.findChildOf(ctxt);
754 _exposedContext = ctxt;
755 if (ctxt == null) { // should never occur
756 throw _constructError("Unexpected problem: chain of filtered context broken");
757 }
758 t = _exposedContext.nextTokenToRead();
759 if (t != null) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700760 return t;
761 }
762 }
763 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700764
Tatu Salorantadfd69092015-04-09 22:29:34 -0700765 @Override
766 public JsonToken nextValue() throws IOException {
767 // Re-implemented same as ParserMinimalBase:
768 JsonToken t = nextToken();
769 if (t == JsonToken.FIELD_NAME) {
770 t = nextToken();
771 }
772 return t;
773 }
774
775 /**
776 * Need to override, re-implement similar to how method defined in
777 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
778 * state correct here.
779 */
780 @Override
781 public JsonParser skipChildren() throws IOException
782 {
Cowtowncoder00900312015-04-14 16:02:33 -0700783 if ((_currToken != JsonToken.START_OBJECT)
784 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700785 return this;
786 }
787 int open = 1;
788
789 // Since proper matching of start/end markers is handled
790 // by nextToken(), we'll just count nesting levels here
791 while (true) {
792 JsonToken t = nextToken();
793 if (t == null) { // not ideal but for now, just return
794 return this;
795 }
796 if (t.isStructStart()) {
797 ++open;
798 } else if (t.isStructEnd()) {
799 if (--open == 0) {
800 return this;
801 }
802 }
803 }
804 }
805
806 /*
807 /**********************************************************
808 /* Public API, access to token information, text
809 /**********************************************************
810 */
811
812 @Override public String getText() throws IOException { return delegate.getText(); }
813 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
814 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
815 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
816 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
817
818 /*
819 /**********************************************************
820 /* Public API, access to token information, numeric
821 /**********************************************************
822 */
823
824 @Override
825 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
826
827 @Override
828 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
829
830 @Override
831 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
832
833 @Override
834 public short getShortValue() throws IOException { return delegate.getShortValue(); }
835
836 @Override
837 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
838
839 @Override
840 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
841
842 @Override
843 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
844
845 @Override
846 public int getIntValue() throws IOException { return delegate.getIntValue(); }
847
848 @Override
849 public long getLongValue() throws IOException { return delegate.getLongValue(); }
850
851 @Override
852 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
853
854 @Override
855 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
856
857 /*
858 /**********************************************************
859 /* Public API, access to token information, coercion/conversion
860 /**********************************************************
861 */
862
863 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
864 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
865 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
866 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
867 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
868 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
869 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
870 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
871 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
872 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
873
874 /*
875 /**********************************************************
876 /* Public API, access to token values, other
877 /**********************************************************
878 */
879
880 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
881 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
882 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
883 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700884
885 /*
886 /**********************************************************
887 /* Internal helper methods
888 /**********************************************************
889 */
890
891 protected JsonStreamContext _filterContext() {
892 if (_exposedContext != null) {
893 return _exposedContext;
894 }
895 return _headContext;
896 }
LokeshNfd327722016-03-20 17:28:25 +0530897
Tatu Salorantadfd69092015-04-09 22:29:34 -0700898}