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