blob: 98a507ee76309660d0b9ae77b73830b431cb001b [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;
50
51 /*
52 /**********************************************************
53 /* State
54 /**********************************************************
55 */
56
57 /**
58 * Last token retrieved via {@link #nextToken}, if any.
59 * Null before the first call to <code>nextToken()</code>,
60 * as well as if token has been explicitly cleared
61 */
62 protected JsonToken _currToken;
63
64 /**
65 * Last cleared token, if any: that is, value that was in
66 * effect when {@link #clearCurrentToken} was called.
67 */
68 protected JsonToken _lastClearedToken;
69
70 /**
Cowtowncoder00900312015-04-14 16:02:33 -070071 * During traversal this is the actual "open" parse tree, which sometimes
72 * is the same as {@link #_exposedContext}, and at other times is ahead
73 * of it. Note that this context is never null.
Tatu Salorantadfd69092015-04-09 22:29:34 -070074 */
Cowtowncoder00900312015-04-14 16:02:33 -070075 protected TokenFilterContext _headContext;
Tatu Salorantadfd69092015-04-09 22:29:34 -070076
77 /**
Cowtowncoder00900312015-04-14 16:02:33 -070078 * In cases where {@link #_headContext} is "ahead" of context exposed to
79 * caller, this context points to what is currently exposed to caller.
80 * When the two are in sync, this context reference will be <code>null</code>.
81 */
82 protected TokenFilterContext _exposedContext;
Cowtowncoder9a797fb2015-04-14 16:12:12 -070083
Cowtowncoder00900312015-04-14 16:02:33 -070084 /**
Tatu Salorantadfd69092015-04-09 22:29:34 -070085 * State that applies to the item within container, used where applicable.
86 * Specifically used to pass inclusion state between property name and
87 * property, and also used for array elements.
88 */
89 protected TokenFilter _itemFilter;
90
91 /**
92 * Number of tokens for which {@link TokenFilter#INCLUDE_ALL}
Tatu Saloranta5a5d1192015-04-13 23:05:07 -070093 * has been returned.
Tatu Salorantadfd69092015-04-09 22:29:34 -070094 */
95 protected int _matchCount;
96
97 /*
98 /**********************************************************
99 /* Construction, initialization
100 /**********************************************************
101 */
102
103 public FilteringParserDelegate(JsonParser p, TokenFilter f,
104 boolean includePath, boolean allowMultipleMatches)
105 {
106 super(p);
107 rootFilter = f;
108 // and this is the currently active filter for root values
109 _itemFilter = f;
Cowtowncoder00900312015-04-14 16:02:33 -0700110 _headContext = TokenFilterContext.createRootContext(f);
Tatu Salorantadfd69092015-04-09 22:29:34 -0700111 _includePath = includePath;
112 _allowMultipleMatches = allowMultipleMatches;
113 }
114
115 /*
116 /**********************************************************
117 /* Extended API
118 /**********************************************************
119 */
120
121 public TokenFilter getFilter() { return rootFilter; }
122
123 /**
124 * Accessor for finding number of matches, where specific token and sub-tree
125 * starting (if structured type) are passed.
126 */
127 public int getMatchCount() {
128 return _matchCount;
129 }
130
131 /*
132 /**********************************************************
133 /* Public API, token accessors
134 /**********************************************************
135 */
136
137 @Override public JsonToken getCurrentToken() { return _currToken; }
138
139 @Override public final int getCurrentTokenId() {
140 final JsonToken t = _currToken;
141 return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
142 }
143
144 @Override public boolean hasCurrentToken() { return _currToken != null; }
145 @Override public boolean hasTokenId(int id) {
146 final JsonToken t = _currToken;
147 if (t == null) {
148 return (JsonTokenId.ID_NO_TOKEN == id);
149 }
150 return t.id() == id;
151 }
152
153 @Override public final boolean hasToken(JsonToken t) {
154 return (_currToken == t);
155 }
156
157 @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
158 @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
159
160 @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700161
Cowtowncoder00900312015-04-14 16:02:33 -0700162 @Override
163 public JsonStreamContext getParsingContext() {
164 return _filterContext();
165 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700166
Cowtowncoder00900312015-04-14 16:02:33 -0700167 // !!! TODO: Verify it works as expected: copied from standard JSON parser impl
168 @Override
169 public String getCurrentName() throws IOException {
170 JsonStreamContext ctxt = _filterContext();
171 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
172 JsonStreamContext parent = ctxt.getParent();
173 return (parent == null) ? null : parent.getCurrentName();
174 }
175 return ctxt.getCurrentName();
176 }
177
Tatu Salorantadfd69092015-04-09 22:29:34 -0700178 /*
179 /**********************************************************
180 /* Public API, token state overrides
181 /**********************************************************
182 */
183
184 @Override
185 public void clearCurrentToken() {
186 if (_currToken != null) {
187 _lastClearedToken = _currToken;
188 _currToken = null;
189 }
190 }
191
192 @Override
193 public JsonToken getLastClearedToken() { return _lastClearedToken; }
194
Tatu Salorantadfd69092015-04-09 22:29:34 -0700195 @Override
Cowtowncoder00900312015-04-14 16:02:33 -0700196 public void overrideCurrentName(String name) {
197 /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so,
198 * what to do with it... Delegation won't work for sure, so let's for
199 * now throw an exception
200 */
201 throw new UnsupportedOperationException("Can not currently override name during filtering read");
202 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700203
204 /*
205 /**********************************************************
206 /* Public API, traversal
207 /**********************************************************
208 */
209
Cowtowncoder00900312015-04-14 16:02:33 -0700210 @Override
211 public JsonToken nextToken() throws IOException
212 {
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700213 // Anything buffered?
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700214 TokenFilterContext ctxt = _exposedContext;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700215
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700216 if (ctxt != null) {
217 while (true) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700218 JsonToken t = ctxt.nextTokenToRead();
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700219 if (t != null) {
220 _currToken = t;
221 return t;
222 }
223 // all done with buffered stuff?
224 if (ctxt == _headContext) {
225 _exposedContext = null;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700226 // Almost! Most likely still have the current token;
227 // with the sole exception of
Cowtowncoderdf521572015-04-16 15:58:41 -0700228 /*
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700229 t = delegate.getCurrentToken();
230 if (t != JsonToken.FIELD_NAME) {
231 _currToken = t;
232 return t;
233 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700234 */
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700235 break;
236 }
237 // If not, traverse down the context chain
Cowtowncoderdf521572015-04-16 15:58:41 -0700238 ctxt = _headContext.findChildOf(ctxt);
239
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700240 _exposedContext = ctxt;
241 if (ctxt == null) { // should never occur
242 throw _constructError("Unexpected problem: chain of filtered context broken");
243 }
244 }
245 }
246
Tatu Salorantab46e0372015-04-14 21:43:03 -0700247 // If not, need to read more. If we got any:
Cowtowncoder37d0d6e2015-04-14 19:48:10 -0700248 JsonToken t = delegate.nextToken();
249 if (t == null) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700250 // no strict need to close, since we have no state here
251 return (_currToken = t);
Cowtowncoder9a797fb2015-04-14 16:12:12 -0700252 }
Tatu Salorantace077d42015-04-14 20:48:23 -0700253
Tatu Salorantab46e0372015-04-14 21:43:03 -0700254 // otherwise... to include or not?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700255 TokenFilter f;
256
257 switch (t.id()) {
Tatu Salorantab46e0372015-04-14 21:43:03 -0700258 case ID_START_ARRAY:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700259 f = _itemFilter;
260 if (f == TokenFilter.INCLUDE_ALL) {
261 _headContext = _headContext.createChildArrayContext(f, true);
262 return (_currToken = t);
263 }
264 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700265 delegate.skipChildren();
266 break;
267 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700268 // Otherwise still iffy, need to check
269 f = _headContext.checkValue(f);
270 if (f == null) {
271 delegate.skipChildren();
272 break;
273 }
274 if (f != TokenFilter.INCLUDE_ALL) {
275 f = f.filterStartArray();
276 }
277 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700278 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700279 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700280 return (_currToken = t);
281 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700282 _headContext = _headContext.createChildArrayContext(f, false);
283
284 // Also: only need buffering if parent path to be included
285 if (_includePath) {
286 return _nextTokenWithBuffering(_headContext);
287 }
288 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700289
290 case ID_START_OBJECT:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700291 f = _itemFilter;
292 if (f == TokenFilter.INCLUDE_ALL) {
293 _headContext = _headContext.createChildObjectContext(f, true);
294 return (_currToken = t);
295 }
296 if (f == null) { // does this occur?
Tatu Salorantab46e0372015-04-14 21:43:03 -0700297 delegate.skipChildren();
298 break;
299 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700300 // Otherwise still iffy, need to check
301 f = _headContext.checkValue(f);
302 if (f == null) {
303 delegate.skipChildren();
304 break;
305 }
306 if (f != TokenFilter.INCLUDE_ALL) {
307 f = f.filterStartObject();
308 }
309 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700310 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700311 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab46e0372015-04-14 21:43:03 -0700312 return (_currToken = t);
313 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700314 _headContext = _headContext.createChildObjectContext(f, false);
315 // Also: only need buffering if parent path to be included
316 if (_includePath) {
317 return _nextTokenWithBuffering(_headContext);
318 }
319 // note: inclusion of surrounding Object handled separately via
320 // FIELD_NAME
321 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700322
323 case ID_END_ARRAY:
324 case ID_END_OBJECT:
325 {
326 boolean returnEnd = _headContext.isStartHandled();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700327 f = _headContext.getFilter();
Tatu Salorantab46e0372015-04-14 21:43:03 -0700328 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
329 f.filterFinishArray();
330 }
331 _headContext = _headContext.getParent();
332 _itemFilter = _headContext.getFilter();
333 if (returnEnd) {
334 return (_currToken = t);
335 }
336 }
337 break;
338
339 case ID_FIELD_NAME:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700340 {
341 final String name = delegate.getCurrentName();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700342 // note: this will also set 'needToHandleName'
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700343 f = _headContext.setFieldName(name);
344 if (f == TokenFilter.INCLUDE_ALL) {
345 _itemFilter = f;
Cowtowncoder654acf02015-04-16 15:30:43 -0700346 // Minor twist here: if parent NOT included, may need to induce output of
347 // surrounding START_OBJECT/END_OBJECT
348 if (!_includePath && !_headContext.isStartHandled()) {
349 t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
350 _exposedContext = _headContext;
351 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700352 return (_currToken = t);
353 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700354 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700355 delegate.nextToken();
356 delegate.skipChildren();
357 break;
358 }
359 f = f.includeProperty(name);
Cowtowncoder654acf02015-04-16 15:30:43 -0700360 if (f == null) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700361 delegate.nextToken();
362 delegate.skipChildren();
363 break;
364 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700365 _itemFilter = f;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700366 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700367 return (_currToken = t);
368 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700369 if (_includePath) {
370 return _nextTokenWithBuffering(_headContext);
371 }
372 break;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700373 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700374
375 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700376 f = _itemFilter;
377 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700378 return (_currToken = t);
379 }
Cowtowncoder32224282015-04-16 16:39:22 -0700380 if (f != null) {
381 f = _headContext.checkValue(f);
382 if ((f == TokenFilter.INCLUDE_ALL)
383 || ((f != null) && f.includeValue(delegate))) {
384 return (_currToken = t);
385 }
386 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700387 // Otherwise not included (leaves must be explicitly included)
388 break;
Tatu Salorantab46e0372015-04-14 21:43:03 -0700389 }
390
391 // We get here if token was not yet found; offlined handling
392 return _nextToken2();
393 }
394
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700395 /**
396 * Offlined handling for cases where there was no buffered token to
397 * return, and the token read next could not be returned as-is,
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700398 * at least not yet, but where we have not yet established that
399 * buffering is needed.
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700400 */
Tatu Salorantab46e0372015-04-14 21:43:03 -0700401 protected final JsonToken _nextToken2() throws IOException
402 {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700403 main_loop:
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700404 while (true) {
405 JsonToken t = delegate.nextToken();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700406 if (t == null) { // is this even legal?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700407 return (_currToken = t);
408 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700409 TokenFilter f;
410
411 switch (t.id()) {
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700412 case ID_START_ARRAY:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700413 f = _itemFilter;
414 if (f == TokenFilter.INCLUDE_ALL) {
415 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700416 return (_currToken = t);
417 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700418 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700419 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700420 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700421 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700422 // Otherwise still iffy, need to check
423 f = _headContext.checkValue(f);
424 if (f == null) {
425 delegate.skipChildren();
426 continue main_loop;
427 }
428 if (f != TokenFilter.INCLUDE_ALL) {
429 f = f.filterStartArray();
430 }
431 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700432 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700433 _headContext = _headContext.createChildArrayContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700434 return (_currToken = t);
435 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700436 _headContext = _headContext.createChildArrayContext(f, false);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700437 // but if we didn't figure it out yet, need to buffer possible events
Cowtowncoder654acf02015-04-16 15:30:43 -0700438 if (_includePath) {
439 return _nextTokenWithBuffering(_headContext);
440 }
441 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700442
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700443 case ID_START_OBJECT:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700444 f = _itemFilter;
445 if (f == TokenFilter.INCLUDE_ALL) {
446 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700447 return (_currToken = t);
448 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700449 if (f == null) { // does this occur?
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700450 delegate.skipChildren();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700451 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700452 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700453 // Otherwise still iffy, need to check
454 f = _headContext.checkValue(f);
455 if (f == null) {
456 delegate.skipChildren();
457 continue main_loop;
458 }
459 if (f != TokenFilter.INCLUDE_ALL) {
460 f = f.filterStartObject();
461 }
462 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700463 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoder654acf02015-04-16 15:30:43 -0700464 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700465 return (_currToken = t);
466 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700467 _headContext = _headContext.createChildObjectContext(f, false);
468 if (_includePath) {
469 return _nextTokenWithBuffering(_headContext);
470 }
471 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700472
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700473 case ID_END_ARRAY:
474 case ID_END_OBJECT:
475 {
476 boolean returnEnd = _headContext.isStartHandled();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700477 f = _headContext.getFilter();
478 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
479 f.filterFinishArray();
480 }
481 _headContext = _headContext.getParent();
482 _itemFilter = _headContext.getFilter();
483 if (returnEnd) {
484 return (_currToken = t);
485 }
486 }
487 continue main_loop;
488
489 case ID_FIELD_NAME:
490 {
491 final String name = delegate.getCurrentName();
492 f = _headContext.setFieldName(name);
493 if (f == TokenFilter.INCLUDE_ALL) {
494 _itemFilter = f;
495 return (_currToken = t);
496 }
497 if (f == null) { // filter out the value
498 delegate.nextToken();
499 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700500 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700501 }
502 f = f.includeProperty(name);
503 if (f == null) { // filter out the value
504 delegate.nextToken();
505 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700506 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700507 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700508 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700509 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700510 return (_currToken = t);
511 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700512 if (_includePath) {
513 return _nextTokenWithBuffering(_headContext);
514 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700515 }
Cowtowncoder654acf02015-04-16 15:30:43 -0700516 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700517
518 default: // scalar value
Cowtowncoderc17c3562015-04-16 15:47:28 -0700519 f = _itemFilter;
Cowtowncoder32224282015-04-16 16:39:22 -0700520 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700521 return (_currToken = t);
522 }
Cowtowncoder32224282015-04-16 16:39:22 -0700523 if (f != null) {
524 f = _headContext.checkValue(f);
525 if ((f == TokenFilter.INCLUDE_ALL)
526 || ((f != null) && f.includeValue(delegate))) {
527 return (_currToken = t);
528 }
529 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700530 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700531 break;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700532 }
533 }
534 }
535
536 /**
537 * Method called when a new potentially included context is found.
538 */
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700539 protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
540 throws IOException
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700541 {
Cowtowncoder654acf02015-04-16 15:30:43 -0700542 _exposedContext = buffRoot;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700543
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700544 main_loop:
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700545 while (true) {
546 JsonToken t = delegate.nextToken();
547
548 if (t == null) { // is this even legal?
549 return (_currToken = t);
550 }
551 TokenFilter f;
552
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700553 // One simplification here: we know for a fact that the item filter is
554 // neither null nor 'include all', for most cases; the only exception
555 // being FIELD_NAME handling
556
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700557 switch (t.id()) {
558 case ID_START_ARRAY:
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700559 f = _headContext.checkValue(_itemFilter);
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700560 if (f == null) {
561 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700562 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700563 }
564 if (f != TokenFilter.INCLUDE_ALL) {
565 f = f.filterStartArray();
566 }
567 _itemFilter = f;
568 _headContext = _headContext.createChildArrayContext(f, true);
569 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700570 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700571 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700572 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700573
574 case ID_START_OBJECT:
575 f = _itemFilter;
576 if (f == TokenFilter.INCLUDE_ALL) {
577 _headContext = _headContext.createChildObjectContext(f, true);
578 return (_currToken = t);
579 }
580 if (f == null) { // does this occur?
581 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700582 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700583 }
584 // Otherwise still iffy, need to check
585 f = _headContext.checkValue(f);
586 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.filterStartObject();
592 }
593 _itemFilter = f;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700594 if (f == TokenFilter.INCLUDE_ALL) {
Cowtowncoderdf521572015-04-16 15:58:41 -0700595 _headContext = _headContext.createChildObjectContext(f, true);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700596 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700597 }
Cowtowncoderdf521572015-04-16 15:58:41 -0700598 _headContext = _headContext.createChildObjectContext(f, false);
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700599 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700600
601 case ID_END_ARRAY:
602 case ID_END_OBJECT:
603 {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700604 // Unlike with other loops, here we know that content was NOT
605 // included (won't get this far otherwise)
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700606 f = _headContext.getFilter();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700607 if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
608 f.filterFinishArray();
609 }
610 _headContext = _headContext.getParent();
611 _itemFilter = _headContext.getFilter();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700612
613 if (_headContext == buffRoot) {
614 // !!! TBI
615 throw _constructError("Internal error: end of possible inclusion -- TBI");
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700616 }
617 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700618 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700619
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700620 case ID_FIELD_NAME:
621 {
622 final String name = delegate.getCurrentName();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700623 f = _headContext.setFieldName(name);
624 if (f == TokenFilter.INCLUDE_ALL) {
625 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700626 return _nextBuffered();
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700627 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700628 if (f == null) { // filter out the value
629 delegate.nextToken();
630 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700631 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700632 }
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700633 f = f.includeProperty(name);
634 if (f == null) { // filter out the value
635 delegate.nextToken();
636 delegate.skipChildren();
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700637 continue main_loop;
Tatu Salorantab1ac0572015-04-15 22:35:00 -0700638 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700639 _itemFilter = f;
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700640 if (f == TokenFilter.INCLUDE_ALL) {
641 return _nextBuffered();
642 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700643 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700644 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700645
646 default: // scalar value
Cowtowncoder32224282015-04-16 16:39:22 -0700647 f = _itemFilter;
648 if (f == TokenFilter.INCLUDE_ALL) {
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700649 return _nextBuffered();
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700650 }
Cowtowncoder32224282015-04-16 16:39:22 -0700651 if (f != null) {
652 f = _headContext.checkValue(f);
653 if ((f == TokenFilter.INCLUDE_ALL)
654 || ((f != null) && f.includeValue(delegate))) {
655 return _nextBuffered();
656 }
657 }
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700658 // Otherwise not included (leaves must be explicitly included)
Cowtowncoder32224282015-04-16 16:39:22 -0700659 continue main_loop;
Tatu Saloranta404aebc2015-04-14 22:47:07 -0700660 }
661 }
Tatu Salorantaf6d583d2015-04-15 23:06:23 -0700662 }
663
664 private JsonToken _nextBuffered() throws IOException
665 {
666 TokenFilterContext ctxt = _exposedContext;
667 JsonToken t = ctxt.nextTokenToRead();
668 if (t != null) {
669 _currToken = t;
670 return t;
671 }
672 while (true) {
673 // all done with buffered stuff?
674 if (ctxt == _headContext) {
675 throw _constructError("Internal error: failed to locate expected buffered tokens");
676 /*
677 _exposedContext = null;
678 break;
679 */
680 }
681 // If not, traverse down the context chain
682 ctxt = _exposedContext.findChildOf(ctxt);
683 _exposedContext = ctxt;
684 if (ctxt == null) { // should never occur
685 throw _constructError("Unexpected problem: chain of filtered context broken");
686 }
687 t = _exposedContext.nextTokenToRead();
688 if (t != null) {
689 _currToken = t;
690 return t;
691 }
692 }
693 }
Tatu Salorantab46e0372015-04-14 21:43:03 -0700694
Tatu Salorantadfd69092015-04-09 22:29:34 -0700695 @Override
696 public JsonToken nextValue() throws IOException {
697 // Re-implemented same as ParserMinimalBase:
698 JsonToken t = nextToken();
699 if (t == JsonToken.FIELD_NAME) {
700 t = nextToken();
701 }
702 return t;
703 }
704
705 /**
706 * Need to override, re-implement similar to how method defined in
707 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
708 * state correct here.
709 */
710 @Override
711 public JsonParser skipChildren() throws IOException
712 {
Cowtowncoder00900312015-04-14 16:02:33 -0700713 if ((_currToken != JsonToken.START_OBJECT)
714 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700715 return this;
716 }
717 int open = 1;
718
719 // Since proper matching of start/end markers is handled
720 // by nextToken(), we'll just count nesting levels here
721 while (true) {
722 JsonToken t = nextToken();
723 if (t == null) { // not ideal but for now, just return
724 return this;
725 }
726 if (t.isStructStart()) {
727 ++open;
728 } else if (t.isStructEnd()) {
729 if (--open == 0) {
730 return this;
731 }
732 }
733 }
734 }
735
736 /*
737 /**********************************************************
738 /* Public API, access to token information, text
739 /**********************************************************
740 */
741
742 @Override public String getText() throws IOException { return delegate.getText(); }
743 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
744 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
745 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
746 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
747
748 /*
749 /**********************************************************
750 /* Public API, access to token information, numeric
751 /**********************************************************
752 */
753
754 @Override
755 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
756
757 @Override
758 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
759
760 @Override
761 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
762
763 @Override
764 public short getShortValue() throws IOException { return delegate.getShortValue(); }
765
766 @Override
767 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
768
769 @Override
770 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
771
772 @Override
773 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
774
775 @Override
776 public int getIntValue() throws IOException { return delegate.getIntValue(); }
777
778 @Override
779 public long getLongValue() throws IOException { return delegate.getLongValue(); }
780
781 @Override
782 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
783
784 @Override
785 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
786
787 /*
788 /**********************************************************
789 /* Public API, access to token information, coercion/conversion
790 /**********************************************************
791 */
792
793 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
794 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
795 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
796 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
797 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
798 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
799 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
800 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
801 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
802 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
803
804 /*
805 /**********************************************************
806 /* Public API, access to token values, other
807 /**********************************************************
808 */
809
810 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
811 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
812 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
813 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700814
815 /*
816 /**********************************************************
817 /* Internal helper methods
818 /**********************************************************
819 */
820
821 protected JsonStreamContext _filterContext() {
822 if (_exposedContext != null) {
823 return _exposedContext;
824 }
825 return _headContext;
826 }
827
Tatu Salorantadfd69092015-04-09 22:29:34 -0700828}