package com.fasterxml.jackson.databind; import java.io.Closeable; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.core.*; /** * Iterator exposed by {@link ObjectMapper} when binding sequence of * objects. Extension is done to allow more convenient exposing of * {@link IOException} (which basic {@link Iterator} does not expose) */ public class MappingIterator<T> implements Iterator<T>, Closeable { protected final static MappingIterator<?> EMPTY_ITERATOR = new MappingIterator<Object>(null, null, null, null, false, null); protected final JavaType _type; protected final DeserializationContext _context; protected final JsonDeserializer<T> _deserializer; protected JsonParser _parser; /** * Flag that indicates whether input {@link JsonParser} should be closed * when we are done or not; generally only called when caller did not * pass JsonParser. */ protected final boolean _closeParser; /** * Flag that is set when we have determined what {@link #hasNextValue()} * should value; reset when {@link #nextValue} is called */ protected boolean _hasNextChecked; /** * If not null, "value to update" instead of creating a new instance * for each call. */ protected final T _updatedValue; /** * @deprecated Since 2.1, to be removed */ @Deprecated protected MappingIterator(JavaType type, JsonParser jp, DeserializationContext ctxt, JsonDeserializer<?> deser) { this(type, jp, ctxt, deser, true, null); } /** * @param managedParser Whether we "own" the {@link JsonParser} passed or not: * if true, it was created by {@link ObjectReader} and code here needs to * close it; if false, it was passed by calling code and should not be * closed by iterator. */ @SuppressWarnings("unchecked") protected MappingIterator(JavaType type, JsonParser jp, DeserializationContext ctxt, JsonDeserializer<?> deser, boolean managedParser, Object valueToUpdate) { _type = type; _parser = jp; _context = ctxt; _deserializer = (JsonDeserializer<T>) deser; _closeParser = managedParser; if (valueToUpdate == null) { _updatedValue = null; } else { _updatedValue = (T) valueToUpdate; } /* Ok: one more thing; we may have to skip START_ARRAY, assuming * "wrapped" sequence; but this is ONLY done for 'managed' parsers * and never if JsonParser was directly passed by caller (if it * was, caller must have either positioned it over first token of * the first element, or cleared the START_ARRAY token explicitly). * Note, however, that we do not try to guess whether this could be * an unwrapped sequence of arrays/Lists: we just assume it is wrapped; * and if not, caller needs to hand us JsonParser instead, pointing to * the first token of the first element. */ if (managedParser && jp != null && jp.getCurrentToken() == JsonToken.START_ARRAY) { jp.clearCurrentToken(); } } @SuppressWarnings("unchecked") protected static <T> MappingIterator<T> emptyIterator() { return (MappingIterator<T>) EMPTY_ITERATOR; } /* /********************************************************** /* Basic iterator impl /********************************************************** */ // @Override public boolean hasNext() { try { return hasNextValue(); } catch (JsonMappingException e) { throw new RuntimeJsonMappingException(e.getMessage(), e); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } // @Override public T next() { try { return nextValue(); } catch (JsonMappingException e) { throw new RuntimeJsonMappingException(e.getMessage(), e); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } // @Override public void remove() { throw new UnsupportedOperationException(); } public void close() throws IOException{ if(_parser != null) { _parser.close(); } } /* /********************************************************** /* Extended API /********************************************************** */ /** * Equivalent of {@link #next} but one that may throw checked * exceptions from Jackson due to invalid input. */ public boolean hasNextValue() throws IOException { if (_parser == null) { return false; } if (!_hasNextChecked) { JsonToken t = _parser.getCurrentToken(); _hasNextChecked = true; if (t == null) { // un-initialized or cleared; find next t = _parser.nextToken(); // If EOF, no more, or if we hit END_ARRAY (although we don't clear the token). if (t == null || t == JsonToken.END_ARRAY) { JsonParser jp = _parser; _parser = null; if (_closeParser) { jp.close(); } return false; } } } return true; } public T nextValue() throws IOException { // caller should always call 'hasNext[Value]' first; but let's ensure: if (!_hasNextChecked) { if (!hasNextValue()) { throw new NoSuchElementException(); } } if (_parser == null) { throw new NoSuchElementException(); } _hasNextChecked = false; T result; if (_updatedValue == null) { result = _deserializer.deserialize(_parser, _context); } else{ _deserializer.deserialize(_parser, _context, _updatedValue); result = _updatedValue; } // Need to consume the token too _parser.clearCurrentToken(); return result; } }