1 package com.fasterxml.jackson.databind;
2 
3 import java.io.Closeable;
4 import java.io.IOException;
5 import java.util.*;
6 
7 import com.fasterxml.jackson.core.*;
8 
9 /**
10  * Iterator exposed by {@link ObjectMapper} when binding sequence of
11  * objects. Extension is done to allow more convenient exposing of
12  * {@link IOException} (which basic {@link Iterator} does not expose)
13  */
14 public class MappingIterator<T> implements Iterator<T>, Closeable
15 {
16     protected final static MappingIterator<?> EMPTY_ITERATOR =
17         new MappingIterator<Object>(null, null, null, null, false, null);
18 
19     /*
20     /**********************************************************
21     /* State constants
22     /**********************************************************
23      */
24 
25     /**
26      * State in which iterator is closed
27      */
28     protected final static int STATE_CLOSED = 0;
29 
30     /**
31      * State in which value read failed
32      */
33     protected final static int STATE_NEED_RESYNC = 1;
34 
35     /**
36      * State in which no recovery is needed, but "hasNextValue()" needs
37      * to be called first
38      */
39     protected final static int STATE_MAY_HAVE_VALUE = 2;
40 
41     /**
42      * State in which "hasNextValue()" has been succesfully called
43      * and deserializer can be called to fetch value
44      */
45     protected final static int STATE_HAS_VALUE = 3;
46 
47     /*
48     /**********************************************************
49     /* Configuration
50     /**********************************************************
51      */
52 
53     /**
54      * Type to bind individual elements to.
55      */
56     protected final JavaType _type;
57 
58     /**
59      * Context for deserialization, needed to pass through to deserializer
60      */
61     protected final DeserializationContext _context;
62 
63     /**
64      * Deserializer for individual element values.
65      */
66     protected final JsonDeserializer<T> _deserializer;
67 
68     /**
69      * Underlying parser used for reading content to bind. Initialized
70      * as not <code>null</code> but set as <code>null</code> when
71      * iterator is closed, to denote closing.
72      */
73     protected final JsonParser _parser;
74 
75     /**
76      * Context to resynchronize to, in case an exception is encountered
77      * but caller wants to try to read more elements.
78      */
79     protected final JsonStreamContext _seqContext;
80 
81     /**
82      * If not null, "value to update" instead of creating a new instance
83      * for each call.
84      */
85     protected final T _updatedValue;
86 
87     /**
88      * Flag that indicates whether input {@link JsonParser} should be closed
89      * when we are done or not; generally only called when caller did not
90      * pass JsonParser.
91      */
92     protected final boolean _closeParser;
93 
94     /*
95     /**********************************************************
96     /* Parsing state
97     /**********************************************************
98      */
99 
100     /**
101      * State of the iterator
102      */
103     protected int _state;
104 
105     /*
106     /**********************************************************
107     /* Construction
108     /**********************************************************
109      */
110 
111     /**
112      * @param managedParser Whether we "own" the {@link JsonParser} passed or not:
113      *   if true, it was created by {@link ObjectReader} and code here needs to
114      *   close it; if false, it was passed by calling code and should not be
115      *   closed by iterator.
116      */
117     @SuppressWarnings("unchecked")
MappingIterator(JavaType type, JsonParser p, DeserializationContext ctxt, JsonDeserializer<?> deser, boolean managedParser, Object valueToUpdate)118     protected MappingIterator(JavaType type, JsonParser p, DeserializationContext ctxt,
119             JsonDeserializer<?> deser,
120             boolean managedParser, Object valueToUpdate)
121     {
122         _type = type;
123         _parser = p;
124         _context = ctxt;
125         _deserializer = (JsonDeserializer<T>) deser;
126         _closeParser = managedParser;
127         if (valueToUpdate == null) {
128             _updatedValue = null;
129         } else {
130             _updatedValue = (T) valueToUpdate;
131         }
132 
133         /* Ok: one more thing; we may have to skip START_ARRAY, assuming
134          * "wrapped" sequence; but this is ONLY done for 'managed' parsers
135          * and never if JsonParser was directly passed by caller (if it
136          * was, caller must have either positioned it over first token of
137          * the first element, or cleared the START_ARRAY token explicitly).
138          * Note, however, that we do not try to guess whether this could be
139          * an unwrapped sequence of arrays/Lists: we just assume it is wrapped;
140          * and if not, caller needs to hand us JsonParser instead, pointing to
141          * the first token of the first element.
142          */
143         if (p == null) { // can this occur?
144             _seqContext = null;
145             _state = STATE_CLOSED;
146         } else {
147             JsonStreamContext sctxt = p.getParsingContext();
148             if (managedParser && p.isExpectedStartArrayToken()) {
149                 // If pointing to START_ARRAY, context should be that ARRAY
150                 p.clearCurrentToken();
151             } else {
152                 // regardless, recovery context should be whatever context we have now,
153                 // with sole exception of pointing to a start marker, in which case it's
154                 // the parent
155                 JsonToken t = p.currentToken();
156                 if ((t == JsonToken.START_OBJECT) || (t == JsonToken.START_ARRAY)) {
157                     sctxt = sctxt.getParent();
158                 }
159             }
160             _seqContext = sctxt;
161             _state = STATE_MAY_HAVE_VALUE;
162         }
163     }
164 
165     /**
166      * Method for getting an "empty" iterator instance: one that never
167      * has more values; may be freely shared.
168      *
169      * @since 2.10 Existed earlier but {@code public} since 2.10
170      */
171     @SuppressWarnings("unchecked")
emptyIterator()172     public static <T> MappingIterator<T> emptyIterator() {
173         return (MappingIterator<T>) EMPTY_ITERATOR;
174     }
175 
176     /*
177     /**********************************************************
178     /* Basic iterator impl
179     /**********************************************************
180      */
181 
182     @Override
hasNext()183     public boolean hasNext()
184     {
185         try {
186             return hasNextValue();
187         } catch (JsonMappingException e) {
188             return (Boolean) _handleMappingException(e);
189         } catch (IOException e) {
190             return (Boolean) _handleIOException(e);
191         }
192     }
193 
194     @SuppressWarnings("unchecked")
195     @Override
next()196     public T next()
197     {
198         try {
199             return nextValue();
200         } catch (JsonMappingException e) {
201             return (T) _handleMappingException(e);
202         } catch (IOException e) {
203             return (T) _handleIOException(e);
204         }
205     }
206 
207     @Override
remove()208     public void remove() {
209         throw new UnsupportedOperationException();
210     }
211 
212     @Override
close()213     public void close() throws IOException {
214         if (_state != STATE_CLOSED) {
215             _state = STATE_CLOSED;
216             if (_parser != null) {
217                 _parser.close();
218             }
219         }
220     }
221 
222     /*
223     /**********************************************************
224     /* Extended API, iteration
225     /**********************************************************
226      */
227 
228     /**
229      * Equivalent of {@link #next} but one that may throw checked
230      * exceptions from Jackson due to invalid input.
231      */
hasNextValue()232     public boolean hasNextValue() throws IOException
233     {
234         switch (_state) {
235         case STATE_CLOSED:
236             return false;
237         case STATE_NEED_RESYNC:
238             _resync();
239             // fall-through
240         case STATE_MAY_HAVE_VALUE:
241             JsonToken t = _parser.currentToken();
242             if (t == null) { // un-initialized or cleared; find next
243                 t = _parser.nextToken();
244                 // If EOF, no more, or if we hit END_ARRAY (although we don't clear the token).
245                 if (t == null || t == JsonToken.END_ARRAY) {
246                     _state = STATE_CLOSED;
247                     if (_closeParser && (_parser != null)) {
248                         _parser.close();
249                     }
250                     return false;
251                 }
252             }
253             _state = STATE_HAS_VALUE;
254             return true;
255         case STATE_HAS_VALUE:
256             // fall through
257         }
258         return true;
259     }
260 
nextValue()261     public T nextValue() throws IOException
262     {
263         switch (_state) {
264         case STATE_CLOSED:
265             return _throwNoSuchElement();
266         case STATE_NEED_RESYNC: // fall-through, will do re-sync
267         case STATE_MAY_HAVE_VALUE:
268             if (!hasNextValue()) {
269                 return _throwNoSuchElement();
270             }
271             break;
272         case STATE_HAS_VALUE:
273             break;
274         }
275 
276         int nextState = STATE_NEED_RESYNC;
277         try {
278             T value;
279             if (_updatedValue == null) {
280                 value = _deserializer.deserialize(_parser, _context);
281             } else{
282                 _deserializer.deserialize(_parser, _context, _updatedValue);
283                 value = _updatedValue;
284             }
285             nextState = STATE_MAY_HAVE_VALUE;
286             return value;
287         } finally {
288             _state = nextState;
289             /* 24-Mar-2015, tatu: As per [#733], need to mark token consumed no
290              *   matter what, to avoid infinite loop for certain failure cases.
291              *   For 2.6 need to improve further.
292              */
293             _parser.clearCurrentToken();
294         }
295     }
296 
297     /**
298      * Convenience method for reading all entries accessible via
299      * this iterator; resulting container will be a {@link java.util.ArrayList}.
300      *
301      * @return List of entries read
302      *
303      * @since 2.2
304      */
readAll()305     public List<T> readAll() throws IOException {
306         return readAll(new ArrayList<T>());
307     }
308 
309     /**
310      * Convenience method for reading all entries accessible via
311      * this iterator
312      *
313      * @return List of entries read (same as passed-in argument)
314      *
315      * @since 2.2
316      */
readAll(L resultList)317     public <L extends List<? super T>> L readAll(L resultList) throws IOException
318     {
319         while (hasNextValue()) {
320             resultList.add(nextValue());
321         }
322         return resultList;
323     }
324 
325     /**
326      * Convenience method for reading all entries accessible via
327      * this iterator
328      *
329      * @since 2.5
330      */
readAll(C results)331     public <C extends Collection<? super T>> C readAll(C results) throws IOException
332     {
333         while (hasNextValue()) {
334             results.add(nextValue());
335         }
336         return results;
337     }
338 
339     /*
340     /**********************************************************
341     /* Extended API, accessors
342     /**********************************************************
343      */
344 
345     /**
346      * Accessor for getting underlying parser this iterator uses.
347      *
348      * @since 2.2
349      */
getParser()350     public JsonParser getParser() {
351         return _parser;
352     }
353 
354     /**
355      * Accessor for accessing {@link FormatSchema} that the underlying parser
356      * (as per {@link #getParser}) is using, if any; only parser of schema-aware
357      * formats use schemas.
358      *
359      * @since 2.2
360      */
getParserSchema()361     public FormatSchema getParserSchema() {
362     	return _parser.getSchema();
363     }
364 
365     /**
366      * Convenience method, functionally equivalent to:
367      *<code>
368      *   iterator.getParser().getCurrentLocation()
369      *</code>
370      *
371      * @return Location of the input stream of the underlying parser
372      *
373      * @since 2.2.1
374      */
getCurrentLocation()375     public JsonLocation getCurrentLocation() {
376         return _parser.getCurrentLocation();
377     }
378 
379     /*
380     /**********************************************************
381     /* Helper methods
382     /**********************************************************
383      */
384 
_resync()385     protected void _resync() throws IOException
386     {
387         final JsonParser p = _parser;
388         // First, a quick check to see if we might have been lucky and no re-sync needed
389         if (p.getParsingContext() == _seqContext) {
390             return;
391         }
392 
393         while (true) {
394             JsonToken t = p.nextToken();
395             if ((t == JsonToken.END_ARRAY) || (t == JsonToken.END_OBJECT)) {
396                 if (p.getParsingContext() == _seqContext) {
397                     p.clearCurrentToken();
398                     return;
399                 }
400             } else if ((t == JsonToken.START_ARRAY) || (t == JsonToken.START_OBJECT)) {
401                 p.skipChildren();
402             } else if (t == null) {
403                 return;
404             }
405         }
406     }
407 
_throwNoSuchElement()408     protected <R> R _throwNoSuchElement() {
409         throw new NoSuchElementException();
410     }
411 
_handleMappingException(JsonMappingException e)412     protected <R> R _handleMappingException(JsonMappingException e) {
413         throw new RuntimeJsonMappingException(e.getMessage(), e);
414     }
415 
_handleIOException(IOException e)416     protected <R> R _handleIOException(IOException e) {
417         throw new RuntimeException(e.getMessage(), e);
418     }
419 }
420