blob: 1904f1fe6ede52f14e8d856bdb457cc99b78f9ca [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.*;
Cowtowncoder00900312015-04-14 16:02:33 -07009import com.fasterxml.jackson.core.json.JsonReadContext;
Tatu Salorantadfd69092015-04-09 22:29:34 -070010import com.fasterxml.jackson.core.util.JsonParserDelegate;
11
12/**
13 * Specialized {@link JsonParserDelegate} that allows use of
14 * {@link TokenFilter} for outputting a subset of content that
15 * is visible to caller
16 *
17 * @since 2.6
18 */
19public class FilteringParserDelegate extends JsonParserDelegate
20{
21 /*
22 /**********************************************************
23 /* Configuration
24 /**********************************************************
25 */
26
27 /**
28 * Object consulted to determine whether to write parts of content generator
29 * is asked to write or not.
30 */
31 protected TokenFilter rootFilter;
32
33 /**
34 * Flag that determines whether filtering will continue after the first
35 * match is indicated or not: if `false`, output is based on just the first
36 * full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more
37 * checks are made; if `true` then filtering will be applied as necessary
38 * until end of content.
39 */
40 protected boolean _allowMultipleMatches;
41
42 /**
43 * Flag that determines whether path leading up to included content should
44 * also be automatically included or not. If `false`, no path inclusion is
45 * done and only explicitly included entries are output; if `true` then
46 * path from main level down to match is also included as necessary.
47 */
48 protected boolean _includePath;
49
50 /*
51 /**********************************************************
52 /* State
53 /**********************************************************
54 */
55
56 /**
57 * Last token retrieved via {@link #nextToken}, if any.
58 * Null before the first call to <code>nextToken()</code>,
59 * as well as if token has been explicitly cleared
60 */
61 protected JsonToken _currToken;
62
63 /**
64 * Last cleared token, if any: that is, value that was in
65 * effect when {@link #clearCurrentToken} was called.
66 */
67 protected JsonToken _lastClearedToken;
68
69 /**
Cowtowncoder00900312015-04-14 16:02:33 -070070 * During traversal this is the actual "open" parse tree, which sometimes
71 * is the same as {@link #_exposedContext}, and at other times is ahead
72 * of it. Note that this context is never null.
Tatu Salorantadfd69092015-04-09 22:29:34 -070073 */
Cowtowncoder00900312015-04-14 16:02:33 -070074 protected TokenFilterContext _headContext;
Tatu Salorantadfd69092015-04-09 22:29:34 -070075
76 /**
Cowtowncoder00900312015-04-14 16:02:33 -070077 * In cases where {@link #_headContext} is "ahead" of context exposed to
78 * caller, this context points to what is currently exposed to caller.
79 * When the two are in sync, this context reference will be <code>null</code>.
80 */
81 protected TokenFilterContext _exposedContext;
82
83 /**
Tatu Salorantadfd69092015-04-09 22:29:34 -070084 * State that applies to the item within container, used where applicable.
85 * Specifically used to pass inclusion state between property name and
86 * property, and also used for array elements.
87 */
88 protected TokenFilter _itemFilter;
89
90 /**
91 * Number of tokens for which {@link TokenFilter#INCLUDE_ALL}
92 * has been returned
93 */
94 protected int _matchCount;
95
96 /*
97 /**********************************************************
98 /* Construction, initialization
99 /**********************************************************
100 */
101
102 public FilteringParserDelegate(JsonParser p, TokenFilter f,
103 boolean includePath, boolean allowMultipleMatches)
104 {
105 super(p);
106 rootFilter = f;
107 // and this is the currently active filter for root values
108 _itemFilter = f;
Cowtowncoder00900312015-04-14 16:02:33 -0700109 _headContext = TokenFilterContext.createRootContext(f);
Tatu Salorantadfd69092015-04-09 22:29:34 -0700110 _includePath = includePath;
111 _allowMultipleMatches = allowMultipleMatches;
112 }
113
114 /*
115 /**********************************************************
116 /* Extended API
117 /**********************************************************
118 */
119
120 public TokenFilter getFilter() { return rootFilter; }
121
122 /**
123 * Accessor for finding number of matches, where specific token and sub-tree
124 * starting (if structured type) are passed.
125 */
126 public int getMatchCount() {
127 return _matchCount;
128 }
129
130 /*
131 /**********************************************************
132 /* Public API, token accessors
133 /**********************************************************
134 */
135
136 @Override public JsonToken getCurrentToken() { return _currToken; }
137
138 @Override public final int getCurrentTokenId() {
139 final JsonToken t = _currToken;
140 return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
141 }
142
143 @Override public boolean hasCurrentToken() { return _currToken != null; }
144 @Override public boolean hasTokenId(int id) {
145 final JsonToken t = _currToken;
146 if (t == null) {
147 return (JsonTokenId.ID_NO_TOKEN == id);
148 }
149 return t.id() == id;
150 }
151
152 @Override public final boolean hasToken(JsonToken t) {
153 return (_currToken == t);
154 }
155
156 @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
157 @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
158
159 @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700160
Cowtowncoder00900312015-04-14 16:02:33 -0700161 @Override
162 public JsonStreamContext getParsingContext() {
163 return _filterContext();
164 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700165
Cowtowncoder00900312015-04-14 16:02:33 -0700166 // !!! TODO: Verify it works as expected: copied from standard JSON parser impl
167 @Override
168 public String getCurrentName() throws IOException {
169 JsonStreamContext ctxt = _filterContext();
170 if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
171 JsonStreamContext parent = ctxt.getParent();
172 return (parent == null) ? null : parent.getCurrentName();
173 }
174 return ctxt.getCurrentName();
175 }
176
Tatu Salorantadfd69092015-04-09 22:29:34 -0700177 /*
178 /**********************************************************
179 /* Public API, token state overrides
180 /**********************************************************
181 */
182
183 @Override
184 public void clearCurrentToken() {
185 if (_currToken != null) {
186 _lastClearedToken = _currToken;
187 _currToken = null;
188 }
189 }
190
191 @Override
192 public JsonToken getLastClearedToken() { return _lastClearedToken; }
193
Tatu Salorantadfd69092015-04-09 22:29:34 -0700194 @Override
Cowtowncoder00900312015-04-14 16:02:33 -0700195 public void overrideCurrentName(String name) {
196 /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so,
197 * what to do with it... Delegation won't work for sure, so let's for
198 * now throw an exception
199 */
200 throw new UnsupportedOperationException("Can not currently override name during filtering read");
201 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700202
203 /*
204 /**********************************************************
205 /* Public API, traversal
206 /**********************************************************
207 */
208
Cowtowncoder00900312015-04-14 16:02:33 -0700209 @Override
210 public JsonToken nextToken() throws IOException
211 {
212 return delegate.nextToken();
213 }
Tatu Salorantadfd69092015-04-09 22:29:34 -0700214
215 @Override
216 public JsonToken nextValue() throws IOException {
217 // Re-implemented same as ParserMinimalBase:
218 JsonToken t = nextToken();
219 if (t == JsonToken.FIELD_NAME) {
220 t = nextToken();
221 }
222 return t;
223 }
224
225 /**
226 * Need to override, re-implement similar to how method defined in
227 * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
228 * state correct here.
229 */
230 @Override
231 public JsonParser skipChildren() throws IOException
232 {
Cowtowncoder00900312015-04-14 16:02:33 -0700233 if ((_currToken != JsonToken.START_OBJECT)
234 && (_currToken != JsonToken.START_ARRAY)) {
Tatu Salorantadfd69092015-04-09 22:29:34 -0700235 return this;
236 }
237 int open = 1;
238
239 // Since proper matching of start/end markers is handled
240 // by nextToken(), we'll just count nesting levels here
241 while (true) {
242 JsonToken t = nextToken();
243 if (t == null) { // not ideal but for now, just return
244 return this;
245 }
246 if (t.isStructStart()) {
247 ++open;
248 } else if (t.isStructEnd()) {
249 if (--open == 0) {
250 return this;
251 }
252 }
253 }
254 }
255
256 /*
257 /**********************************************************
258 /* Public API, access to token information, text
259 /**********************************************************
260 */
261
262 @Override public String getText() throws IOException { return delegate.getText(); }
263 @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
264 @Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
265 @Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
266 @Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
267
268 /*
269 /**********************************************************
270 /* Public API, access to token information, numeric
271 /**********************************************************
272 */
273
274 @Override
275 public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
276
277 @Override
278 public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
279
280 @Override
281 public byte getByteValue() throws IOException { return delegate.getByteValue(); }
282
283 @Override
284 public short getShortValue() throws IOException { return delegate.getShortValue(); }
285
286 @Override
287 public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
288
289 @Override
290 public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
291
292 @Override
293 public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
294
295 @Override
296 public int getIntValue() throws IOException { return delegate.getIntValue(); }
297
298 @Override
299 public long getLongValue() throws IOException { return delegate.getLongValue(); }
300
301 @Override
302 public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
303
304 @Override
305 public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
306
307 /*
308 /**********************************************************
309 /* Public API, access to token information, coercion/conversion
310 /**********************************************************
311 */
312
313 @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
314 @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
315 @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
316 @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
317 @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
318 @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
319 @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
320 @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
321 @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
322 @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
323
324 /*
325 /**********************************************************
326 /* Public API, access to token values, other
327 /**********************************************************
328 */
329
330 @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
331 @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
332 @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
333 @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
Cowtowncoder00900312015-04-14 16:02:33 -0700334
335 /*
336 /**********************************************************
337 /* Internal helper methods
338 /**********************************************************
339 */
340
341 protected JsonStreamContext _filterContext() {
342 if (_exposedContext != null) {
343 return _exposedContext;
344 }
345 return _headContext;
346 }
347
Tatu Salorantadfd69092015-04-09 22:29:34 -0700348}