/** Unit test to check for regression of [JACKSON-18]. */
  public void testSmallNumbers() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    ArrayNode root = mapper.createArrayNode();
    for (int i = -20; i <= 20; ++i) {
      JsonNode n = root.numberNode(i);
      root.add(n);
      // Hmmh. Not sure why toString() won't be triggered otherwise...
      assertEquals(String.valueOf(i), n.toString());
    }

    // Loop over 2 different serialization methods
    for (int type = 0; type < 2; ++type) {
      StringWriter sw = new StringWriter();
      if (type == 0) {
        JsonGenerator gen = new JsonFactory().createGenerator(sw);
        root.serialize(gen, null);
        gen.close();
      } else {
        mapper.writeValue(sw, root);
      }

      String doc = sw.toString();
      JsonParser p = new JsonFactory().createParser(new StringReader(doc));

      assertEquals(JsonToken.START_ARRAY, p.nextToken());
      for (int i = -20; i <= 20; ++i) {
        assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(i, p.getIntValue());
        assertEquals("" + i, p.getText());
      }
      assertEquals(JsonToken.END_ARRAY, p.nextToken());
      p.close();
    }
  }
  @Override
  public EnumMap<?, ?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
    // Ok: must point to START_OBJECT
    if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
      return _deserializeFromEmpty(jp, ctxt);
    }
    EnumMap result = constructMap();
    final JsonDeserializer<Object> valueDes = _valueDeserializer;
    final TypeDeserializer typeDeser = _valueTypeDeserializer;

    while ((jp.nextToken()) == JsonToken.FIELD_NAME) {
      String keyName = jp.getCurrentName(); // just for error message
      // but we need to let key deserializer handle it separately, nonetheless
      Enum<?> key = (Enum<?>) _keyDeserializer.deserializeKey(keyName, ctxt);
      if (key == null) {
        if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
          throw ctxt.weirdStringException(
              keyName,
              _enumClass,
              "value not one of declared Enum instance names for " + _mapType.getKeyType());
        }
        /* 24-Mar-2012, tatu: Null won't work as a key anyway, so let's
         *  just skip the entry then. But we must skip the value as well, if so.
         */
        jp.nextToken();
        jp.skipChildren();
        continue;
      }
      // And then the value...
      JsonToken t = jp.nextToken();
      /* note: MUST check for nulls separately: deserializers will
       * not handle them (and maybe fail or return bogus data)
       */
      Object value;

      try {
        if (t == JsonToken.VALUE_NULL) {
          value = valueDes.getNullValue(ctxt);
        } else if (typeDeser == null) {
          value = valueDes.deserialize(jp, ctxt);
        } else {
          value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
        }
      } catch (Exception e) {
        wrapAndThrow(e, result, keyName);
        return null;
      }
      result.put(key, value);
    }
    return result;
  }
  @Override
  public Collection<String> deserialize(
      JsonParser jp, DeserializationContext ctxt, Collection<String> result) throws IOException {
    // Ok: must point to START_ARRAY
    if (!jp.isExpectedStartArrayToken()) {
      return handleNonArray(jp, ctxt, result);
    }

    if (_valueDeserializer != null) {
      return deserializeUsingCustom(jp, ctxt, result, _valueDeserializer);
    }
    JsonToken t;

    while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
      String value;
      if (t == JsonToken.VALUE_STRING) {
        value = jp.getText();
      } else if (t == JsonToken.VALUE_NULL) {
        value = null;
      } else {
        value = _parseString(jp, ctxt);
      }
      result.add(value);
    }
    return result;
  }
  public void testBinary() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    final int LENGTH = 13045;
    byte[] data = new byte[LENGTH];
    for (int i = 0; i < LENGTH; ++i) {
      data[i] = (byte) i;
    }
    StringWriter sw = new StringWriter();
    mapper.writeValue(sw, BinaryNode.valueOf(data));

    JsonParser p = new JsonFactory().createParser(sw.toString());
    // note: can't determine it's binary from json alone:
    assertToken(JsonToken.VALUE_STRING, p.nextToken());
    assertArrayEquals(data, p.getBinaryValue());
    p.close();
  }
  private Collection<String> deserializeUsingCustom(
      JsonParser jp,
      DeserializationContext ctxt,
      Collection<String> result,
      final JsonDeserializer<String> deser)
      throws IOException {
    JsonToken t;
    while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
      String value;

      if (t == JsonToken.VALUE_NULL) {
        value = deser.getNullValue();
      } else {
        value = deser.deserialize(jp, ctxt);
      }
      result.add(value);
    }
    return result;
  }
 protected final double _testRawDeser(int reps, byte[] json, ObjectReader reader)
     throws IOException {
   long start = System.nanoTime();
   final JsonFactory f = reader.getFactory();
   while (--reps >= 0) {
     JsonParser p = f.createParser(new ByteArrayInputStream(json));
     JsonToken t;
     while ((t = p.nextToken()) != null) {
       if (t == JsonToken.VALUE_STRING) {
         p.getText();
       } else if (t.isNumeric()) {
         p.getNumberValue();
       }
       ;
     }
     p.close();
   }
   hash = f.hashCode();
   return _msecsFromNanos(System.nanoTime() - start);
 }
 @Override
 public JSONArray deserialize(JsonParser jp, DeserializationContext ctxt)
     throws IOException, JsonProcessingException {
   JSONArray array = new JSONArray();
   JsonToken t;
   while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
     switch (t) {
       case START_ARRAY:
         array.put(deserialize(jp, ctxt));
         continue;
       case START_OBJECT:
         array.put(JSONObjectDeserializer.instance.deserialize(jp, ctxt));
         continue;
       case VALUE_STRING:
         array.put(jp.getText());
         continue;
       case VALUE_NULL:
         array.put(JSONObject.NULL);
         continue;
       case VALUE_TRUE:
         array.put(Boolean.TRUE);
         continue;
       case VALUE_FALSE:
         array.put(Boolean.FALSE);
         continue;
       case VALUE_NUMBER_INT:
         array.put(jp.getNumberValue());
         continue;
       case VALUE_NUMBER_FLOAT:
         array.put(jp.getNumberValue());
         continue;
       case VALUE_EMBEDDED_OBJECT:
         array.put(jp.getEmbeddedObject());
         continue;
     }
     throw ctxt.mappingException("Urecognized or unsupported JsonToken type: " + t);
   }
   return array;
 }
 @Override
 public OffsetTime deserialize(JsonParser parser, DeserializationContext context)
     throws IOException {
   if (parser.hasToken(JsonToken.VALUE_STRING)) {
     String string = parser.getText().trim();
     if (string.length() == 0) {
       return null;
     }
     try {
       return OffsetTime.parse(string, _formatter);
     } catch (DateTimeException e) {
       _rethrowDateTimeException(parser, context, e, string);
     }
   }
   if (!parser.isExpectedStartArrayToken()) {
     if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) {
       return (OffsetTime) parser.getEmbeddedObject();
     }
     throw context.wrongTokenException(parser, JsonToken.START_ARRAY, "Expected array or string.");
   }
   int hour = parser.nextIntValue(-1);
   if (hour == -1) {
     JsonToken t = parser.getCurrentToken();
     if (t == JsonToken.END_ARRAY) {
       return null;
     }
     if (t != JsonToken.VALUE_NUMBER_INT) {
       _reportWrongToken(parser, context, JsonToken.VALUE_NUMBER_INT, "hours");
     }
     hour = parser.getIntValue();
   }
   int minute = parser.nextIntValue(-1);
   if (minute == -1) {
     JsonToken t = parser.getCurrentToken();
     if (t == JsonToken.END_ARRAY) {
       return null;
     }
     if (t != JsonToken.VALUE_NUMBER_INT) {
       _reportWrongToken(parser, context, JsonToken.VALUE_NUMBER_INT, "minutes");
     }
     minute = parser.getIntValue();
   }
   int partialSecond = 0;
   int second = 0;
   if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) {
     second = parser.getIntValue();
     if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) {
       partialSecond = parser.getIntValue();
       if (partialSecond < 1_000
           && !context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) {
         partialSecond *= 1_000_000; // value is milliseconds, convert it to nanoseconds
       }
       parser.nextToken();
     }
   }
   if (parser.getCurrentToken() == JsonToken.VALUE_STRING) {
     OffsetTime t =
         OffsetTime.of(hour, minute, second, partialSecond, ZoneOffset.of(parser.getText()));
     if (parser.nextToken() != JsonToken.END_ARRAY) {
       _reportWrongToken(parser, context, JsonToken.END_ARRAY, "timezone");
     }
     return t;
   }
   throw context.wrongTokenException(
       parser, JsonToken.VALUE_STRING, "Expected string for TimeZone after numeric values");
 }
  private void verifyFromMap(String input) throws Exception {
    JsonParser p = new JsonFactory().createParser(input);
    assertEquals(JsonToken.START_OBJECT, p.nextToken());
    assertEquals(JsonToken.FIELD_NAME, p.nextToken());
    assertEquals(FIELD4, getAndVerifyText(p));
    assertEquals(JsonToken.VALUE_STRING, p.nextToken());
    assertEquals(TEXT2, getAndVerifyText(p));

    assertEquals(JsonToken.FIELD_NAME, p.nextToken());
    assertEquals(FIELD3, getAndVerifyText(p));
    assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
    assertEquals(-1, p.getIntValue());

    assertEquals(JsonToken.FIELD_NAME, p.nextToken());
    assertEquals(FIELD2, getAndVerifyText(p));
    assertEquals(JsonToken.START_ARRAY, p.nextToken());
    assertEquals(JsonToken.END_ARRAY, p.nextToken());

    assertEquals(JsonToken.FIELD_NAME, p.nextToken());
    assertEquals(FIELD1, getAndVerifyText(p));
    assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
    assertEquals(DOUBLE_VALUE, p.getDoubleValue(), 0);

    assertEquals(JsonToken.END_OBJECT, p.nextToken());

    assertNull(p.nextToken());
    p.close();
  }
  private void verifyFromArray(String input) throws Exception {
    JsonParser p = new JsonFactory().createParser(new StringReader(input));

    assertEquals(JsonToken.START_ARRAY, p.nextToken());

    assertEquals(JsonToken.VALUE_STRING, p.nextToken());
    assertEquals(TEXT1, getAndVerifyText(p));

    assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
    assertEquals(3, p.getIntValue());

    assertEquals(JsonToken.START_OBJECT, p.nextToken());
    assertEquals(JsonToken.FIELD_NAME, p.nextToken());
    assertEquals(FIELD1, getAndVerifyText(p));

    assertEquals(JsonToken.VALUE_TRUE, p.nextToken());
    assertEquals(JsonToken.FIELD_NAME, p.nextToken());
    assertEquals(FIELD2, getAndVerifyText(p));

    assertEquals(JsonToken.START_ARRAY, p.nextToken());
    assertEquals(JsonToken.END_ARRAY, p.nextToken());
    assertEquals(JsonToken.END_OBJECT, p.nextToken());

    assertEquals(JsonToken.VALUE_FALSE, p.nextToken());

    assertEquals(JsonToken.END_ARRAY, p.nextToken());
    assertNull(p.nextToken());
    p.close();
  }