package com.fasterxml.jackson.core.filter;

import java.io.IOException;

import com.fasterxml.jackson.core.*;

Alternative variant of JsonStreamContext, used when filtering content being read or written (based on TokenFilter).
Since:2.6
/** * Alternative variant of {@link JsonStreamContext}, used when filtering * content being read or written (based on {@link TokenFilter}). * * @since 2.6 */
public class TokenFilterContext extends JsonStreamContext {
Parent context for this context; null for root context.
/** * Parent context for this context; null for root context. */
protected final TokenFilterContext _parent; /* /********************************************************** /* Simple instance reuse slots; speed up things /* a bit (10-15%) for docs with lots of small /* arrays/objects /********************************************************** */ protected TokenFilterContext _child; /* /********************************************************** /* Location/state information /********************************************************** */
Name of the field of which value is to be parsed; only used for OBJECT contexts
/** * Name of the field of which value is to be parsed; only * used for OBJECT contexts */
protected String _currentName;
Filter to use for items in this state (for properties of Objects, elements of Arrays, and root-level values of root context)
/** * Filter to use for items in this state (for properties of Objects, * elements of Arrays, and root-level values of root context) */
protected TokenFilter _filter;
Flag that indicates that start token has been read/written, so that matching close token needs to be read/written as well when context is getting closed.
/** * Flag that indicates that start token has been read/written, * so that matching close token needs to be read/written as well * when context is getting closed. */
protected boolean _startHandled;
Flag that indicates that the current name of this context still needs to be read/written, if path from root down to included leaf is to be exposed.
/** * Flag that indicates that the current name of this context * still needs to be read/written, if path from root down to * included leaf is to be exposed. */
protected boolean _needToHandleName; /* /********************************************************** /* Life-cycle /********************************************************** */ protected TokenFilterContext(int type, TokenFilterContext parent, TokenFilter filter, boolean startHandled) { super(); _type = type; _parent = parent; _filter = filter; _index = -1; _startHandled = startHandled; _needToHandleName = false; } protected TokenFilterContext reset(int type, TokenFilter filter, boolean startWritten) { _type = type; _filter = filter; _index = -1; _currentName = null; _startHandled = startWritten; _needToHandleName = false; return this; } /* /********************************************************** /* Factory methods /********************************************************** */ public static TokenFilterContext createRootContext(TokenFilter filter) { // true -> since we have no start/end marker, consider start handled return new TokenFilterContext(TYPE_ROOT, null, filter, true); } public TokenFilterContext createChildArrayContext(TokenFilter filter, boolean writeStart) { TokenFilterContext ctxt = _child; if (ctxt == null) { _child = ctxt = new TokenFilterContext(TYPE_ARRAY, this, filter, writeStart); return ctxt; } return ctxt.reset(TYPE_ARRAY, filter, writeStart); } public TokenFilterContext createChildObjectContext(TokenFilter filter, boolean writeStart) { TokenFilterContext ctxt = _child; if (ctxt == null) { _child = ctxt = new TokenFilterContext(TYPE_OBJECT, this, filter, writeStart); return ctxt; } return ctxt.reset(TYPE_OBJECT, filter, writeStart); } /* /********************************************************** /* State changes /********************************************************** */ public TokenFilter setFieldName(String name) throws JsonProcessingException { _currentName = name; _needToHandleName = true; return _filter; }
Method called to check whether value is to be included at current output position, either as Object property, Array element, or root value.
/** * Method called to check whether value is to be included at current output * position, either as Object property, Array element, or root value. */
public TokenFilter checkValue(TokenFilter filter) { // First, checks for Object properties have been made earlier: if (_type == TYPE_OBJECT) { return filter; } // We increase it first because at the beginning of array, value is -1 int ix = ++_index; if (_type == TYPE_ARRAY) { return filter.includeElement(ix); } return filter.includeRootValue(ix); }
Method called to ensure that parent path from root is written up to and including this node.
/** * Method called to ensure that parent path from root is written up to * and including this node. */
public void writePath(JsonGenerator gen) throws IOException { if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { return; } if (_parent != null) { _parent._writePath(gen); } if (_startHandled) { // even if Object started, need to start leaf-level name if (_needToHandleName) { gen.writeFieldName(_currentName); } } else { _startHandled = true; if (_type == TYPE_OBJECT) { gen.writeStartObject(); gen.writeFieldName(_currentName); // we know name must be written } else if (_type == TYPE_ARRAY) { gen.writeStartArray(); } } }
Variant of writePath(JsonGenerator) called when all we need is immediately surrounding Object. Method typically called when including a single property but not including full path to root.
/** * Variant of {@link #writePath(JsonGenerator)} called when all we * need is immediately surrounding Object. Method typically called * when including a single property but not including full path * to root. */
public void writeImmediatePath(JsonGenerator gen) throws IOException { if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { return; } if (_startHandled) { // even if Object started, need to start leaf-level name if (_needToHandleName) { gen.writeFieldName(_currentName); } } else { _startHandled = true; if (_type == TYPE_OBJECT) { gen.writeStartObject(); if (_needToHandleName) { gen.writeFieldName(_currentName); } } else if (_type == TYPE_ARRAY) { gen.writeStartArray(); } } } private void _writePath(JsonGenerator gen) throws IOException { if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { return; } if (_parent != null) { _parent._writePath(gen); } if (_startHandled) { // even if Object started, need to start leaf-level name if (_needToHandleName) { _needToHandleName = false; // at parent must explicitly clear gen.writeFieldName(_currentName); } } else { _startHandled = true; if (_type == TYPE_OBJECT) { gen.writeStartObject(); if (_needToHandleName) { _needToHandleName = false; // at parent must explicitly clear gen.writeFieldName(_currentName); } } else if (_type == TYPE_ARRAY) { gen.writeStartArray(); } } } public TokenFilterContext closeArray(JsonGenerator gen) throws IOException { if (_startHandled) { gen.writeEndArray(); } if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) { _filter.filterFinishArray(); } return _parent; } public TokenFilterContext closeObject(JsonGenerator gen) throws IOException { if (_startHandled) { gen.writeEndObject(); } if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) { _filter.filterFinishObject(); } return _parent; } public void skipParentChecks() { _filter = null; for (TokenFilterContext ctxt = _parent; ctxt != null; ctxt = ctxt._parent) { _parent._filter = null; } } /* /********************************************************** /* Accessors, mutators /********************************************************** */ @Override public Object getCurrentValue() { return null; } @Override public void setCurrentValue(Object v) { } @Override public final TokenFilterContext getParent() { return _parent; } @Override public final String getCurrentName() { return _currentName; } // @since 2.9 @Override public boolean hasCurrentName() { return _currentName != null; } public TokenFilter getFilter() { return _filter; } public boolean isStartHandled() { return _startHandled; } public JsonToken nextTokenToRead() { if (!_startHandled) { _startHandled = true; if (_type == TYPE_OBJECT) { return JsonToken.START_OBJECT; } // Note: root should never be unhandled return JsonToken.START_ARRAY; } // But otherwise at most might have FIELD_NAME if (_needToHandleName && (_type == TYPE_OBJECT)) { _needToHandleName = false; return JsonToken.FIELD_NAME; } return null; } public TokenFilterContext findChildOf(TokenFilterContext parent) { if (_parent == parent) { return this; } TokenFilterContext curr = _parent; while (curr != null) { TokenFilterContext p = curr._parent; if (p == parent) { return curr; } curr = p; } // should never occur but... return null; } // // // Internally used abstract methods protected void appendDesc(StringBuilder sb) { if (_parent != null) { _parent.appendDesc(sb); } if (_type == TYPE_OBJECT) { sb.append('{'); if (_currentName != null) { sb.append('"'); // !!! TODO: Name chars should be escaped? sb.append(_currentName); sb.append('"'); } else { sb.append('?'); } sb.append('}'); } else if (_type == TYPE_ARRAY) { sb.append('['); sb.append(getCurrentIndex()); sb.append(']'); } else { // nah, ROOT: sb.append("/"); } } // // // Overridden standard methods
Overridden to provide developer writeable "JsonPath" representation of the context.
/** * Overridden to provide developer writeable "JsonPath" representation * of the context. */
@Override public String toString() { StringBuilder sb = new StringBuilder(64); appendDesc(sb); return sb.toString(); } }