/** 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 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;
  }
 private final String[] handleNonArray(JsonParser p, DeserializationContext ctxt)
     throws IOException {
   // implicit arrays from single values?
   boolean canWrap =
       (_unwrapSingle == Boolean.TRUE)
           || ((_unwrapSingle == null)
               && ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
   if (canWrap) {
     return new String[] {p.hasToken(JsonToken.VALUE_NULL) ? null : _parseString(p, ctxt)};
   }
   if (p.hasToken(JsonToken.VALUE_STRING)
       && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
     String str = p.getText();
     if (str.length() == 0) {
       return null;
     }
   }
   return (String[]) ctxt.handleUnexpectedToken(_valueClass, p);
 }
 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");
 }