Add skeletal FilteringParserDelegate, to (eventually) add filtering on parser too
diff --git a/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
new file mode 100644
index 0000000..18750d1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
@@ -0,0 +1,306 @@
+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;
+
+/**
+ * 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;
+
+ /*
+ /**********************************************************
+ /* 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;
+
+ /**
+ * Although delegate has its own output context it is not sufficient since we actually
+ * have to keep track of excluded (filtered out) structures as well as ones delegate
+ * actually outputs.
+ */
+ protected TokenFilterContext _filterContext;
+
+ /**
+ * 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;
+ _filterContext = 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 final int getCurrentTokenId() {
+ 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: not necessarily correct...
+ @Override public String getCurrentName() throws IOException { return delegate.getCurrentName(); }
+
+ /*
+ /**********************************************************
+ /* Public API, token state overrides
+ /**********************************************************
+ */
+
+ @Override
+ public void clearCurrentToken() {
+ if (_currToken != null) {
+ _lastClearedToken = _currToken;
+ _currToken = null;
+ }
+ }
+
+ @Override
+ public JsonToken getLastClearedToken() { return _lastClearedToken; }
+
+ // !!! TODO: re-implement
+ @Override
+ public void overrideCurrentName(String name) { delegate.overrideCurrentName(name); }
+
+ /*
+ /**********************************************************
+ /* Public API, traversal
+ /**********************************************************
+ */
+
+ // !!! TODO: re-implement
+ @Override public JsonToken nextToken() throws IOException { return delegate.nextToken(); }
+
+ @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(); }
+}