| package com.fasterxml.jackson.core.filter; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| |
| import com.fasterxml.jackson.core.*; |
| import com.fasterxml.jackson.core.util.JsonParserDelegate; |
| |
| import static com.fasterxml.jackson.core.JsonTokenId.*; |
| |
| /** |
| * Specialized {@link JsonParserDelegate} that allows use of |
| * {@link TokenFilter} for outputting a subset of content that |
| * is visible to caller |
| * |
| * @since 2.6 |
| */ |
| public class FilteringParserDelegate extends JsonParserDelegate |
| { |
| /* |
| /********************************************************** |
| /* Configuration |
| /********************************************************** |
| */ |
| |
| /** |
| * Object consulted to determine whether to write parts of content generator |
| * is asked to write or not. |
| */ |
| protected TokenFilter rootFilter; |
| |
| /** |
| * Flag that determines whether filtering will continue after the first |
| * match is indicated or not: if `false`, output is based on just the first |
| * full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more |
| * checks are made; if `true` then filtering will be applied as necessary |
| * until end of content. |
| */ |
| protected boolean _allowMultipleMatches; |
| |
| /** |
| * Flag that determines whether path leading up to included content should |
| * also be automatically included or not. If `false`, no path inclusion is |
| * done and only explicitly included entries are output; if `true` then |
| * path from main level down to match is also included as necessary. |
| */ |
| protected boolean _includePath; |
| |
| /* NOTE: this feature is included in the first version (2.6), but |
| * there is no public API to enable it, yet, since there isn't an |
| * actual use case. But it seemed possible need could arise, which |
| * is feature has not yet been removed. If no use is found within |
| * first version or two, just remove. |
| * |
| * Marked as deprecated since its status is uncertain. |
| */ |
| @Deprecated |
| protected boolean _includeImmediateParent; |
| |
| /* |
| /********************************************************** |
| /* State |
| /********************************************************** |
| */ |
| |
| /** |
| * Last token retrieved via {@link #nextToken}, if any. |
| * Null before the first call to <code>nextToken()</code>, |
| * as well as if token has been explicitly cleared |
| */ |
| protected JsonToken _currToken; |
| |
| /** |
| * Last cleared token, if any: that is, value that was in |
| * effect when {@link #clearCurrentToken} was called. |
| */ |
| protected JsonToken _lastClearedToken; |
| |
| /** |
| * During traversal this is the actual "open" parse tree, which sometimes |
| * is the same as {@link #_exposedContext}, and at other times is ahead |
| * of it. Note that this context is never null. |
| */ |
| protected TokenFilterContext _headContext; |
| |
| /** |
| * In cases where {@link #_headContext} is "ahead" of context exposed to |
| * caller, this context points to what is currently exposed to caller. |
| * When the two are in sync, this context reference will be <code>null</code>. |
| */ |
| protected TokenFilterContext _exposedContext; |
| |
| /** |
| * State that applies to the item within container, used where applicable. |
| * Specifically used to pass inclusion state between property name and |
| * property, and also used for array elements. |
| */ |
| protected TokenFilter _itemFilter; |
| |
| /** |
| * Number of tokens for which {@link TokenFilter#INCLUDE_ALL} |
| * has been returned. |
| */ |
| protected int _matchCount; |
| |
| /* |
| /********************************************************** |
| /* Construction, initialization |
| /********************************************************** |
| */ |
| |
| public FilteringParserDelegate(JsonParser p, TokenFilter f, |
| boolean includePath, boolean allowMultipleMatches) |
| { |
| super(p); |
| rootFilter = f; |
| // and this is the currently active filter for root values |
| _itemFilter = f; |
| _headContext = TokenFilterContext.createRootContext(f); |
| _includePath = includePath; |
| _allowMultipleMatches = allowMultipleMatches; |
| } |
| |
| /* |
| /********************************************************** |
| /* Extended API |
| /********************************************************** |
| */ |
| |
| public TokenFilter getFilter() { return rootFilter; } |
| |
| /** |
| * Accessor for finding number of matches, where specific token and sub-tree |
| * starting (if structured type) are passed. |
| */ |
| public int getMatchCount() { |
| return _matchCount; |
| } |
| |
| /* |
| /********************************************************** |
| /* Public API, token accessors |
| /********************************************************** |
| */ |
| |
| @Override public JsonToken getCurrentToken() { return _currToken; } |
| @Override public JsonToken currentToken() { return _currToken; } |
| |
| @Override public final int getCurrentTokenId() { |
| final JsonToken t = _currToken; |
| return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id(); |
| } |
| @Override public final int currentTokenId() { |
| final JsonToken t = _currToken; |
| return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id(); |
| } |
| |
| @Override public boolean hasCurrentToken() { return _currToken != null; } |
| @Override public boolean hasTokenId(int id) { |
| final JsonToken t = _currToken; |
| if (t == null) { |
| return (JsonTokenId.ID_NO_TOKEN == id); |
| } |
| return t.id() == id; |
| } |
| |
| @Override public final boolean hasToken(JsonToken t) { |
| return (_currToken == t); |
| } |
| |
| @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; } |
| @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; } |
| |
| @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); } |
| |
| @Override |
| public JsonStreamContext getParsingContext() { |
| return _filterContext(); |
| } |
| |
| // !!! TODO: Verify it works as expected: copied from standard JSON parser impl |
| @Override |
| public String getCurrentName() throws IOException { |
| JsonStreamContext ctxt = _filterContext(); |
| if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { |
| JsonStreamContext parent = ctxt.getParent(); |
| return (parent == null) ? null : parent.getCurrentName(); |
| } |
| return ctxt.getCurrentName(); |
| } |
| |
| /* |
| /********************************************************** |
| /* Public API, token state overrides |
| /********************************************************** |
| */ |
| |
| @Override |
| public void clearCurrentToken() { |
| if (_currToken != null) { |
| _lastClearedToken = _currToken; |
| _currToken = null; |
| } |
| } |
| |
| @Override |
| public JsonToken getLastClearedToken() { return _lastClearedToken; } |
| |
| @Override |
| public void overrideCurrentName(String name) { |
| /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so, |
| * what to do with it... Delegation won't work for sure, so let's for |
| * now throw an exception |
| */ |
| throw new UnsupportedOperationException("Can not currently override name during filtering read"); |
| } |
| |
| /* |
| /********************************************************** |
| /* Public API, traversal |
| /********************************************************** |
| */ |
| |
| @Override |
| public JsonToken nextToken() throws IOException |
| { |
| // 23-May-2017, tatu: To be honest, code here is rather hairy and I don't like all |
| // conditionals; and it seems odd to return `null` but NOT considering input |
| // as closed... would love a rewrite to simplify/clear up logic here. |
| |
| // Check for _allowMultipleMatches - false and at least there is one token - which is _currToken |
| // check for no buffered context _exposedContext - null |
| // If all the conditions matches then check for scalar / non-scalar property |
| |
| if (!_allowMultipleMatches && (_currToken != null) && (_exposedContext == null)) { |
| // if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL |
| // matched once, return null |
| if (_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath |
| && (_itemFilter == TokenFilter.INCLUDE_ALL)) { |
| return (_currToken = null); |
| } |
| } |
| // Anything buffered? |
| TokenFilterContext ctxt = _exposedContext; |
| |
| if (ctxt != null) { |
| while (true) { |
| JsonToken t = ctxt.nextTokenToRead(); |
| if (t != null) { |
| _currToken = t; |
| return t; |
| } |
| // all done with buffered stuff? |
| if (ctxt == _headContext) { |
| _exposedContext = null; |
| if (ctxt.inArray()) { |
| t = delegate.getCurrentToken(); |
| // Is this guaranteed to work without further checks? |
| // if (t != JsonToken.START_ARRAY) { |
| _currToken = t; |
| return t; |
| } |
| |
| // Almost! Most likely still have the current token; |
| // with the sole exception of |
| /* |
| t = delegate.getCurrentToken(); |
| if (t != JsonToken.FIELD_NAME) { |
| _currToken = t; |
| return t; |
| } |
| */ |
| break; |
| } |
| // If not, traverse down the context chain |
| ctxt = _headContext.findChildOf(ctxt); |
| _exposedContext = ctxt; |
| if (ctxt == null) { // should never occur |
| throw _constructError("Unexpected problem: chain of filtered context broken"); |
| } |
| } |
| } |
| |
| // If not, need to read more. If we got any: |
| JsonToken t = delegate.nextToken(); |
| if (t == null) { |
| // no strict need to close, since we have no state here |
| _currToken = t; |
| return t; |
| } |
| |
| // otherwise... to include or not? |
| TokenFilter f; |
| |
| switch (t.id()) { |
| case ID_START_ARRAY: |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildArrayContext(f, true); |
| return (_currToken = t); |
| } |
| if (f == null) { // does this occur? |
| delegate.skipChildren(); |
| break; |
| } |
| // Otherwise still iffy, need to check |
| f = _headContext.checkValue(f); |
| if (f == null) { |
| delegate.skipChildren(); |
| break; |
| } |
| if (f != TokenFilter.INCLUDE_ALL) { |
| f = f.filterStartArray(); |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildArrayContext(f, true); |
| return (_currToken = t); |
| } |
| _headContext = _headContext.createChildArrayContext(f, false); |
| |
| // Also: only need buffering if parent path to be included |
| if (_includePath) { |
| t = _nextTokenWithBuffering(_headContext); |
| if (t != null) { |
| _currToken = t; |
| return t; |
| } |
| } |
| break; |
| |
| case ID_START_OBJECT: |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildObjectContext(f, true); |
| return (_currToken = t); |
| } |
| if (f == null) { // does this occur? |
| delegate.skipChildren(); |
| break; |
| } |
| // Otherwise still iffy, need to check |
| f = _headContext.checkValue(f); |
| if (f == null) { |
| delegate.skipChildren(); |
| break; |
| } |
| if (f != TokenFilter.INCLUDE_ALL) { |
| f = f.filterStartObject(); |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildObjectContext(f, true); |
| return (_currToken = t); |
| } |
| _headContext = _headContext.createChildObjectContext(f, false); |
| // Also: only need buffering if parent path to be included |
| if (_includePath) { |
| t = _nextTokenWithBuffering(_headContext); |
| if (t != null) { |
| _currToken = t; |
| return t; |
| } |
| } |
| // note: inclusion of surrounding Object handled separately via |
| // FIELD_NAME |
| break; |
| |
| case ID_END_ARRAY: |
| case ID_END_OBJECT: |
| { |
| boolean returnEnd = _headContext.isStartHandled(); |
| f = _headContext.getFilter(); |
| if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) { |
| f.filterFinishArray(); |
| } |
| _headContext = _headContext.getParent(); |
| _itemFilter = _headContext.getFilter(); |
| if (returnEnd) { |
| return (_currToken = t); |
| } |
| } |
| break; |
| |
| case ID_FIELD_NAME: |
| { |
| final String name = delegate.getCurrentName(); |
| // note: this will also set 'needToHandleName' |
| f = _headContext.setFieldName(name); |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _itemFilter = f; |
| if (!_includePath) { |
| // Minor twist here: if parent NOT included, may need to induce output of |
| // surrounding START_OBJECT/END_OBJECT |
| if (_includeImmediateParent && !_headContext.isStartHandled()) { |
| t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled |
| _exposedContext = _headContext; |
| } |
| } |
| return (_currToken = t); |
| } |
| if (f == null) { |
| delegate.nextToken(); |
| delegate.skipChildren(); |
| break; |
| } |
| f = f.includeProperty(name); |
| if (f == null) { |
| delegate.nextToken(); |
| delegate.skipChildren(); |
| break; |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| if (_verifyAllowedMatches() && _includePath) { |
| return (_currToken = t); |
| } |
| } |
| if (_includePath) { |
| t = _nextTokenWithBuffering(_headContext); |
| if (t != null) { |
| _currToken = t; |
| return t; |
| } |
| } |
| break; |
| } |
| |
| default: // scalar value |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| return (_currToken = t); |
| } |
| if (f != null) { |
| f = _headContext.checkValue(f); |
| if ((f == TokenFilter.INCLUDE_ALL) |
| || ((f != null) && f.includeValue(delegate))) { |
| if (_verifyAllowedMatches()) { |
| return (_currToken = t); |
| } |
| } |
| } |
| // Otherwise not included (leaves must be explicitly included) |
| break; |
| } |
| |
| // We get here if token was not yet found; offlined handling |
| return _nextToken2(); |
| } |
| |
| /** |
| * Offlined handling for cases where there was no buffered token to |
| * return, and the token read next could not be returned as-is, |
| * at least not yet, but where we have not yet established that |
| * buffering is needed. |
| */ |
| protected final JsonToken _nextToken2() throws IOException |
| { |
| main_loop: |
| while (true) { |
| JsonToken t = delegate.nextToken(); |
| if (t == null) { // is this even legal? |
| _currToken = t; |
| return t; |
| } |
| TokenFilter f; |
| |
| switch (t.id()) { |
| case ID_START_ARRAY: |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildArrayContext(f, true); |
| return (_currToken = t); |
| } |
| if (f == null) { // does this occur? |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| // Otherwise still iffy, need to check |
| f = _headContext.checkValue(f); |
| if (f == null) { |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| if (f != TokenFilter.INCLUDE_ALL) { |
| f = f.filterStartArray(); |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildArrayContext(f, true); |
| return (_currToken = t); |
| } |
| _headContext = _headContext.createChildArrayContext(f, false); |
| // but if we didn't figure it out yet, need to buffer possible events |
| if (_includePath) { |
| t = _nextTokenWithBuffering(_headContext); |
| if (t != null) { |
| _currToken = t; |
| return t; |
| } |
| } |
| continue main_loop; |
| |
| case ID_START_OBJECT: |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildObjectContext(f, true); |
| return (_currToken = t); |
| } |
| if (f == null) { // does this occur? |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| // Otherwise still iffy, need to check |
| f = _headContext.checkValue(f); |
| if (f == null) { |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| if (f != TokenFilter.INCLUDE_ALL) { |
| f = f.filterStartObject(); |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildObjectContext(f, true); |
| return (_currToken = t); |
| } |
| _headContext = _headContext.createChildObjectContext(f, false); |
| if (_includePath) { |
| t = _nextTokenWithBuffering(_headContext); |
| if (t != null) { |
| _currToken = t; |
| return t; |
| } |
| } |
| continue main_loop; |
| |
| case ID_END_ARRAY: |
| case ID_END_OBJECT: |
| { |
| boolean returnEnd = _headContext.isStartHandled(); |
| f = _headContext.getFilter(); |
| if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) { |
| f.filterFinishArray(); |
| } |
| _headContext = _headContext.getParent(); |
| _itemFilter = _headContext.getFilter(); |
| if (returnEnd) { |
| return (_currToken = t); |
| } |
| } |
| continue main_loop; |
| |
| case ID_FIELD_NAME: |
| { |
| final String name = delegate.getCurrentName(); |
| f = _headContext.setFieldName(name); |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _itemFilter = f; |
| return (_currToken = t); |
| } |
| if (f == null) { // filter out the value |
| delegate.nextToken(); |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| f = f.includeProperty(name); |
| if (f == null) { // filter out the value |
| delegate.nextToken(); |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| if (_verifyAllowedMatches() && _includePath) { |
| return (_currToken = t); |
| } |
| // if (_includeImmediateParent) { ... |
| continue main_loop; |
| } |
| if (_includePath) { |
| t = _nextTokenWithBuffering(_headContext); |
| if (t != null) { |
| _currToken = t; |
| return t; |
| } |
| } |
| } |
| continue main_loop; |
| |
| default: // scalar value |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| return (_currToken = t); |
| } |
| if (f != null) { |
| f = _headContext.checkValue(f); |
| if ((f == TokenFilter.INCLUDE_ALL) |
| || ((f != null) && f.includeValue(delegate))) { |
| if (_verifyAllowedMatches()) { |
| return (_currToken = t); |
| } |
| } |
| } |
| // Otherwise not included (leaves must be explicitly included) |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Method called when a new potentially included context is found. |
| */ |
| protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot) |
| throws IOException |
| { |
| main_loop: |
| while (true) { |
| JsonToken t = delegate.nextToken(); |
| if (t == null) { // is this even legal? |
| return t; |
| } |
| TokenFilter f; |
| |
| // One simplification here: we know for a fact that the item filter is |
| // neither null nor 'include all', for most cases; the only exception |
| // being FIELD_NAME handling |
| |
| switch (t.id()) { |
| case ID_START_ARRAY: |
| f = _headContext.checkValue(_itemFilter); |
| if (f == null) { |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| if (f != TokenFilter.INCLUDE_ALL) { |
| f = f.filterStartArray(); |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildArrayContext(f, true); |
| return _nextBuffered(buffRoot); |
| } |
| _headContext = _headContext.createChildArrayContext(f, false); |
| continue main_loop; |
| |
| case ID_START_OBJECT: |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildObjectContext(f, true); |
| return t; |
| } |
| if (f == null) { // does this occur? |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| // Otherwise still iffy, need to check |
| f = _headContext.checkValue(f); |
| if (f == null) { |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| if (f != TokenFilter.INCLUDE_ALL) { |
| f = f.filterStartObject(); |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _headContext = _headContext.createChildObjectContext(f, true); |
| return _nextBuffered(buffRoot); |
| } |
| _headContext = _headContext.createChildObjectContext(f, false); |
| continue main_loop; |
| |
| case ID_END_ARRAY: |
| case ID_END_OBJECT: |
| { |
| // Unlike with other loops, here we know that content was NOT |
| // included (won't get this far otherwise) |
| f = _headContext.getFilter(); |
| if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) { |
| f.filterFinishArray(); |
| } |
| boolean gotEnd = (_headContext == buffRoot); |
| boolean returnEnd = gotEnd && _headContext.isStartHandled(); |
| |
| _headContext = _headContext.getParent(); |
| _itemFilter = _headContext.getFilter(); |
| |
| if (returnEnd) { |
| return t; |
| } |
| // Hmmh. Do we need both checks, or should above suffice? |
| if (gotEnd || (_headContext == buffRoot)) { |
| return null; |
| } |
| } |
| continue main_loop; |
| |
| case ID_FIELD_NAME: |
| { |
| final String name = delegate.getCurrentName(); |
| f = _headContext.setFieldName(name); |
| if (f == TokenFilter.INCLUDE_ALL) { |
| _itemFilter = f; |
| return _nextBuffered(buffRoot); |
| } |
| if (f == null) { // filter out the value |
| delegate.nextToken(); |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| f = f.includeProperty(name); |
| if (f == null) { // filter out the value |
| delegate.nextToken(); |
| delegate.skipChildren(); |
| continue main_loop; |
| } |
| _itemFilter = f; |
| if (f == TokenFilter.INCLUDE_ALL && _verifyAllowedMatches()) { |
| return _nextBuffered(buffRoot); |
| } |
| } |
| continue main_loop; |
| |
| default: // scalar value |
| f = _itemFilter; |
| if (f == TokenFilter.INCLUDE_ALL) { |
| return _nextBuffered(buffRoot); |
| } |
| if (f != null) { |
| f = _headContext.checkValue(f); |
| if ((f == TokenFilter.INCLUDE_ALL) |
| || ((f != null) && f.includeValue(delegate))) { |
| if (_verifyAllowedMatches()) { |
| return _nextBuffered(buffRoot); |
| } |
| } |
| } |
| // Otherwise not included (leaves must be explicitly included) |
| continue main_loop; |
| } |
| } |
| } |
| |
| private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException |
| { |
| _exposedContext = buffRoot; |
| TokenFilterContext ctxt = buffRoot; |
| JsonToken t = ctxt.nextTokenToRead(); |
| if (t != null) { |
| return t; |
| } |
| while (true) { |
| // all done with buffered stuff? |
| if (ctxt == _headContext) { |
| throw _constructError("Internal error: failed to locate expected buffered tokens"); |
| /* |
| _exposedContext = null; |
| break; |
| */ |
| } |
| // If not, traverse down the context chain |
| ctxt = _exposedContext.findChildOf(ctxt); |
| _exposedContext = ctxt; |
| if (ctxt == null) { // should never occur |
| throw _constructError("Unexpected problem: chain of filtered context broken"); |
| } |
| t = _exposedContext.nextTokenToRead(); |
| if (t != null) { |
| return t; |
| } |
| } |
| } |
| |
| private final boolean _verifyAllowedMatches() throws IOException { |
| if (_matchCount == 0 || _allowMultipleMatches) { |
| ++_matchCount; |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public JsonToken nextValue() throws IOException { |
| // Re-implemented same as ParserMinimalBase: |
| JsonToken t = nextToken(); |
| if (t == JsonToken.FIELD_NAME) { |
| t = nextToken(); |
| } |
| return t; |
| } |
| |
| /** |
| * Need to override, re-implement similar to how method defined in |
| * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep |
| * state correct here. |
| */ |
| @Override |
| public JsonParser skipChildren() throws IOException |
| { |
| if ((_currToken != JsonToken.START_OBJECT) |
| && (_currToken != JsonToken.START_ARRAY)) { |
| return this; |
| } |
| int open = 1; |
| |
| // Since proper matching of start/end markers is handled |
| // by nextToken(), we'll just count nesting levels here |
| while (true) { |
| JsonToken t = nextToken(); |
| if (t == null) { // not ideal but for now, just return |
| return this; |
| } |
| if (t.isStructStart()) { |
| ++open; |
| } else if (t.isStructEnd()) { |
| if (--open == 0) { |
| return this; |
| } |
| } |
| } |
| } |
| |
| /* |
| /********************************************************** |
| /* Public API, access to token information, text |
| /********************************************************** |
| */ |
| |
| @Override public String getText() throws IOException { return delegate.getText(); } |
| @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); } |
| @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); } |
| @Override public int getTextLength() throws IOException { return delegate.getTextLength(); } |
| @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); } |
| |
| /* |
| /********************************************************** |
| /* Public API, access to token information, numeric |
| /********************************************************** |
| */ |
| |
| @Override |
| public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); } |
| |
| @Override |
| public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); } |
| |
| @Override |
| public byte getByteValue() throws IOException { return delegate.getByteValue(); } |
| |
| @Override |
| public short getShortValue() throws IOException { return delegate.getShortValue(); } |
| |
| @Override |
| public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); } |
| |
| @Override |
| public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); } |
| |
| @Override |
| public float getFloatValue() throws IOException { return delegate.getFloatValue(); } |
| |
| @Override |
| public int getIntValue() throws IOException { return delegate.getIntValue(); } |
| |
| @Override |
| public long getLongValue() throws IOException { return delegate.getLongValue(); } |
| |
| @Override |
| public NumberType getNumberType() throws IOException { return delegate.getNumberType(); } |
| |
| @Override |
| public Number getNumberValue() throws IOException { return delegate.getNumberValue(); } |
| |
| /* |
| /********************************************************** |
| /* Public API, access to token information, coercion/conversion |
| /********************************************************** |
| */ |
| |
| @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); } |
| @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); } |
| @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); } |
| @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); } |
| @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); } |
| @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); } |
| @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); } |
| @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); } |
| @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); } |
| @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); } |
| |
| /* |
| /********************************************************** |
| /* Public API, access to token values, other |
| /********************************************************** |
| */ |
| |
| @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); } |
| @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); } |
| @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); } |
| @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); } |
| |
| /* |
| /********************************************************** |
| /* Internal helper methods |
| /********************************************************** |
| */ |
| |
| protected JsonStreamContext _filterContext() { |
| if (_exposedContext != null) { |
| return _exposedContext; |
| } |
| return _headContext; |
| } |
| |
| } |