blob: 98c643dd94012236893f7ff843834a07dfe99d03 [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
60 protected boolean _includeImmediateParent = false;
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 {
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700224 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700225 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700226
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700227 if (ctxt != null) {
228 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700229 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700230 if (t != null) {
231 _currToken = t;
232 return t;
233 }
234 // all done with buffered stuff?
235 if (ctxt == _headContext) {
236 _exposedContext = null;
Cowtowncoderac4b4492015-04-23 15:36:50 -0700237 if (ctxt.inArray()) {
238 t = delegate.getCurrentToken();
239// Is this guaranteed to work without further checks?
240// if (t != JsonToken.START_ARRAY) {
241 _currToken = t;
242 return t;
243 }
244
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700245 // Almost! Most likely still have the current token;
246 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700247 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700248 t = delegate.getCurrentToken();
249 if (t != JsonToken.FIELD_NAME) {
250 _currToken = t;
251 return t;
252 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700253 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700254 break;
255 }
256 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700257 ctxt = _headContext.findChildOf(ctxt);
258
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700259 _exposedContext = ctxt;
260 if (ctxt == null) { // should never occur
261 throw _constructError("Unexpected problem: chain of filtered context broken");
262 }
263 }
264 }
265
Tatu Salorantab46e0372015-04-14 21:43:03 -0700266 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700267 JsonToken t = delegate.nextToken();
268 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700269 // no strict need to close, since we have no state here
270 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700271 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700272
Tatu Salorantab46e0372015-04-14 21:43:03 -0700273 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700274 TokenFilter f;
275
276 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700277 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700278 f = _itemFilter;
279 if (f == TokenFilter.INCLUDE_ALL) {
280 _headContext = _headContext.createChildArrayContext(f, true);
281 return (_currToken = t);
282 }
283 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700284 delegate.skipChildren();
285 break;
286 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700287 // Otherwise still iffy, need to check
288 f = _headContext.checkValue(f);
289 if (f == null) {
290 delegate.skipChildren();
291 break;
292 }
293 if (f != TokenFilter.INCLUDE_ALL) {
294 f = f.filterStartArray();
295 }
296 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700297 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700298 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700299 return (_currToken = t);
300 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700301 _headContext = _headContext.createChildArrayContext(f, false);
302
303 // Also: only need buffering if parent path to be included
304 if (_includePath) {
305 return _nextTokenWithBuffering(_headContext);
306 }
307 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700308
309 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700310 f = _itemFilter;
311 if (f == TokenFilter.INCLUDE_ALL) {
312 _headContext = _headContext.createChildObjectContext(f, true);
313 return (_currToken = t);
314 }
315 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700316 delegate.skipChildren();
317 break;
318 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700319 // Otherwise still iffy, need to check
320 f = _headContext.checkValue(f);
321 if (f == null) {
322 delegate.skipChildren();
323 break;
324 }
325 if (f != TokenFilter.INCLUDE_ALL) {
326 f = f.filterStartObject();
327 }
328 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700329 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700330 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700331 return (_currToken = t);
332 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700333 _headContext = _headContext.createChildObjectContext(f, false);
334 // Also: only need buffering if parent path to be included
335 if (_includePath) {
336 return _nextTokenWithBuffering(_headContext);
337 }
338 // note: inclusion of surrounding Object handled separately via
339 // FIELD_NAME
340 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700341
342 case ID_END_ARRAY:
343 case ID_END_OBJECT:
344 {
345 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700346 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700347 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
348 f.filterFinishArray();
349 }
350 _headContext = _headContext.getParent();
351 _itemFilter = _headContext.getFilter();
352 if (returnEnd) {
353 return (_currToken = t);
354 }
355 }
356 break;
357
358 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700359 {
360 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700361 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700362 f = _headContext.setFieldName(name);
363 if (f == TokenFilter.INCLUDE_ALL) {
364 _itemFilter = f;
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700365 if (!_includePath) {
366 // Minor twist here: if parent NOT included, may need to induce output of
367 // surrounding START_OBJECT/END_OBJECT
368 if (_includeImmediateParent && !_headContext.isStartHandled()) {
369 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
370 _exposedContext = _headContext;
371 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700372 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700373 return (_currToken = t);
374 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700375 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700376 delegate.nextToken();
377 delegate.skipChildren();
378 break;
379 }
380 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700381 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700382 delegate.nextToken();
383 delegate.skipChildren();
384 break;
385 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700386 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700387 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700388 if (_includePath) {
389 return (_currToken = t);
390 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700391 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700392 if (_includePath) {
393 return _nextTokenWithBuffering(_headContext);
394 }
395 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700396 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700397
398 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700399 f = _itemFilter;
400 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700401 return (_currToken = t);
402 }
Cowtowncoder32224282015-04-16 16:39:22 -0700403 if (f != null) {
404 f = _headContext.checkValue(f);
405 if ((f == TokenFilter.INCLUDE_ALL)
406 || ((f != null) && f.includeValue(delegate))) {
407 return (_currToken = t);
408 }
409 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700410 // Otherwise not included (leaves must be explicitly included)
411 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700412 }
413
414 // We get here if token was not yet found; offlined handling
415 return _nextToken2();
416 }
417
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700418 /**
419 * Offlined handling for cases where there was no buffered token to
420 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700421 * at least not yet, but where we have not yet established that
422 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700423 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700424 protected final JsonToken _nextToken2() throws IOException
425 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700426 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700427 while (true) {
428 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700429 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700430 return (_currToken = t);
431 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700432 TokenFilter f;
433
434 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700435 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700436 f = _itemFilter;
437 if (f == TokenFilter.INCLUDE_ALL) {
438 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700439 return (_currToken = t);
440 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700441 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700442 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700443 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700444 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700445 // Otherwise still iffy, need to check
446 f = _headContext.checkValue(f);
447 if (f == null) {
448 delegate.skipChildren();
449 continue main_loop;
450 }
451 if (f != TokenFilter.INCLUDE_ALL) {
452 f = f.filterStartArray();
453 }
454 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700455 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700456 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700457 return (_currToken = t);
458 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700459 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700460 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700461 if (_includePath) {
462 return _nextTokenWithBuffering(_headContext);
463 }
464 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700465
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700466 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700467 f = _itemFilter;
468 if (f == TokenFilter.INCLUDE_ALL) {
469 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700470 return (_currToken = t);
471 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700472 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700473 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700474 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700475 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700476 // Otherwise still iffy, need to check
477 f = _headContext.checkValue(f);
478 if (f == null) {
479 delegate.skipChildren();
480 continue main_loop;
481 }
482 if (f != TokenFilter.INCLUDE_ALL) {
483 f = f.filterStartObject();
484 }
485 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700486 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700487 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700488 return (_currToken = t);
489 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700490 _headContext = _headContext.createChildObjectContext(f, false);
491 if (_includePath) {
492 return _nextTokenWithBuffering(_headContext);
493 }
494 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700495
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700496 case ID_END_ARRAY:
497 case ID_END_OBJECT:
498 {
499 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700500 f = _headContext.getFilter();
501 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
502 f.filterFinishArray();
503 }
504 _headContext = _headContext.getParent();
505 _itemFilter = _headContext.getFilter();
506 if (returnEnd) {
507 return (_currToken = t);
508 }
509 }
510 continue main_loop;
511
512 case ID_FIELD_NAME:
513 {
514 final String name = delegate.getCurrentName();
515 f = _headContext.setFieldName(name);
516 if (f == TokenFilter.INCLUDE_ALL) {
517 _itemFilter = f;
518 return (_currToken = t);
519 }
520 if (f == null) { // filter out the value
521 delegate.nextToken();
522 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700523 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700524 }
525 f = f.includeProperty(name);
526 if (f == null) { // filter out the value
527 delegate.nextToken();
528 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700529 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700530 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700531 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700532 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantacfe32d02015-04-21 23:17:22 -0700533 if (_includePath) {
534 return (_currToken = t);
535 }
536// if (_includeImmediateParent) { ...
537 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700538 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700539 if (_includePath) {
540 return _nextTokenWithBuffering(_headContext);
541 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700542 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700543 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700544
545 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700546 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700547 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700548 return (_currToken = t);
549 }
Cowtowncoder32224282015-04-16 16:39:22 -0700550 if (f != null) {
551 f = _headContext.checkValue(f);
552 if ((f == TokenFilter.INCLUDE_ALL)
553 || ((f != null) && f.includeValue(delegate))) {
554 return (_currToken = t);
555 }
556 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700557 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700558 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700559 }
560 }
561 }
562
563 /**
564 * Method called when a new potentially included context is found.
565 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700566 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
567 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700568 {
Cowtowncoder654acf02015-04-16 15:30:43 -0700569 _exposedContext = buffRoot;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700570
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700571 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700572 while (true) {
573 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700574 if (t == null) { // is this even legal?
575 return (_currToken = t);
576 }
577 TokenFilter f;
578
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700579 // One simplification here: we know for a fact that the item filter is
580 // neither null nor 'include all', for most cases; the only exception
581 // being FIELD_NAME handling
582
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700583 switch (t.id()) {
584 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700585 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700586 if (f == null) {
587 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700588 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700589 }
590 if (f != TokenFilter.INCLUDE_ALL) {
591 f = f.filterStartArray();
592 }
593 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700594 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderac4b4492015-04-23 15:36:50 -0700595 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700596 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700597 }
Cowtowncoderac4b4492015-04-23 15:36:50 -0700598 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700599 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700600
601 case ID_START_OBJECT:
602 f = _itemFilter;
603 if (f == TokenFilter.INCLUDE_ALL) {
604 _headContext = _headContext.createChildObjectContext(f, true);
605 return (_currToken = t);
606 }
607 if (f == null) { // does this occur?
608 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700609 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700610 }
611 // Otherwise still iffy, need to check
612 f = _headContext.checkValue(f);
613 if (f == null) {
614 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700615 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700616 }
617 if (f != TokenFilter.INCLUDE_ALL) {
618 f = f.filterStartObject();
619 }
620 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700621 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700622 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700623 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700624 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700625 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700626 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700627
628 case ID_END_ARRAY:
629 case ID_END_OBJECT:
630 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700631 // Unlike with other loops, here we know that content was NOT
632 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700633 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700634 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
635 f.filterFinishArray();
636 }
637 _headContext = _headContext.getParent();
638 _itemFilter = _headContext.getFilter();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700639
640 if (_headContext == buffRoot) {
641 // !!! TBI
642 throw _constructError("Internal error: end of possible inclusion -- TBI");
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700643 }
644 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700645 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700646
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700647 case ID_FIELD_NAME:
648 {
649 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700650 f = _headContext.setFieldName(name);
651 if (f == TokenFilter.INCLUDE_ALL) {
652 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700653 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700654 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700655 if (f == null) { // filter out the value
656 delegate.nextToken();
657 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700658 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700659 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700660 f = f.includeProperty(name);
661 if (f == null) { // filter out the value
662 delegate.nextToken();
663 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700664 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700665 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700666 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700667 if (f == TokenFilter.INCLUDE_ALL) {
668 return _nextBuffered();
669 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700670 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700671 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700672
673 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700674 f = _itemFilter;
675 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700676 return _nextBuffered();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700677 }
Cowtowncoder32224282015-04-16 16:39:22 -0700678 if (f != null) {
679 f = _headContext.checkValue(f);
680 if ((f == TokenFilter.INCLUDE_ALL)
681 || ((f != null) && f.includeValue(delegate))) {
682 return _nextBuffered();
683 }
684 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700685 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700686 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700687 }
688 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700689 }
690
691 private JsonToken _nextBuffered() throws IOException
692 {
693 TokenFilterContext ctxt = _exposedContext;
694 JsonToken t = ctxt.nextTokenToRead();
695 if (t != null) {
696 _currToken = t;
697 return t;
698 }
699 while (true) {
700 // all done with buffered stuff?
701 if (ctxt == _headContext) {
702 throw _constructError("Internal error: failed to locate expected buffered tokens");
703 /*
704 _exposedContext = null;
705 break;
706 */
707 }
708 // If not, traverse down the context chain
709 ctxt = _exposedContext.findChildOf(ctxt);
710 _exposedContext = ctxt;
711 if (ctxt == null) { // should never occur
712 throw _constructError("Unexpected problem: chain of filtered context broken");
713 }
714 t = _exposedContext.nextTokenToRead();
715 if (t != null) {
716 _currToken = t;
717 return t;
718 }
719 }
720 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700721
Tatu Salorantadfd69092015-04-09 22:29:34 -0700722 @Override
723 public JsonToken nextValue() throws IOException {
724 // Re-implemented same as ParserMinimalBase:
725 JsonToken t = nextToken();
726 if (t == JsonToken.FIELD_NAME) {
727 t = nextToken();
728 }
729 return t;
730 }
731
732 /**
733 * Need to override, re-implement similar to how method defined in
734 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
735 * state correct here.
736 */
737 @Override
738 public JsonParser skipChildren() throws IOException
739 {
Cowtowncoder00900312015-04-14 16:02:33 -0700740 if ((_currToken != JsonToken.START_OBJECT)
741 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700742 return this;
743 }
744 int open = 1;
745
746 // Since proper matching of start/end markers is handled
747 // by nextToken(), we'll just count nesting levels here
748 while (true) {
749 JsonToken t = nextToken();
750 if (t == null) { // not ideal but for now, just return
751 return this;
752 }
753 if (t.isStructStart()) {
754 ++open;
755 } else if (t.isStructEnd()) {
756 if (--open == 0) {
757 return this;
758 }
759 }
760 }
761 }
762
763 /*
764 /**********************************************************
765 /* Public API, access to token information, text
766 /**********************************************************
767 */
768
769 @Override public String getText() throws IOException { return delegate.getText(); }
770 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
771 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
772 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
773 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
774
775 /*
776 /**********************************************************
777 /* Public API, access to token information, numeric
778 /**********************************************************
779 */
780
781 @Override
782 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
783
784 @Override
785 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
786
787 @Override
788 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
789
790 @Override
791 public short getShortValue() throws IOException { return delegate.getShortValue(); }
792
793 @Override
794 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
795
796 @Override
797 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
798
799 @Override
800 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
801
802 @Override
803 public int getIntValue() throws IOException { return delegate.getIntValue(); }
804
805 @Override
806 public long getLongValue() throws IOException { return delegate.getLongValue(); }
807
808 @Override
809 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
810
811 @Override
812 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
813
814 /*
815 /**********************************************************
816 /* Public API, access to token information, coercion/conversion
817 /**********************************************************
818 */
819
820 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
821 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
822 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
823 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
824 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
825 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
826 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
827 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
828 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
829 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
830
831 /*
832 /**********************************************************
833 /* Public API, access to token values, other
834 /**********************************************************
835 */
836
837 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
838 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
839 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
840 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700841
842 /*
843 /**********************************************************
844 /* Internal helper methods
845 /**********************************************************
846 */
847
848 protected JsonStreamContext _filterContext() {
849 if (_exposedContext != null) {
850 return _exposedContext;
851 }
852 return _headContext;
853 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700854}