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