package com.fasterxml.jackson.jr.ob.impl;

import static com.fasterxml.jackson.jr.ob.impl.ValueWriterLocator.*;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.*;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.jr.ob.JSONObjectException;
import com.fasterxml.jackson.jr.ob.api.ValueReader;

Default ValueReader used for simple scalar types and related, not including POJO-, Map and Collection types.
/** * Default {@link ValueReader} used for simple scalar types and related, * not including POJO-, {@link java.util.Map} and {@link java.util.Collection} * types. */
public class SimpleValueReader extends ValueReader { protected final int _typeId; public SimpleValueReader(Class<?> raw, int typeId) { super(raw); _typeId = typeId; } @Override public Object readNext(JSONReader reader, JsonParser p) throws IOException { // NOTE: only cases where we can optimize switch (_typeId) { // Textual types, related: case SER_STRING: case SER_CHARACTER_SEQUENCE: { String str = p.nextTextValue(); if (str != null) { return str; } return read(reader, p); } case SER_CHAR_ARRAY: { String str = p.nextTextValue(); if (str != null) { return str.toCharArray(); } return read(reader, p); } // Number types: // Let's only optimize common ones, int/Integer, long/Long; // and only when `nextXxx()` correctly returns value. In all other // cases default to "standard" handling which does range checks etc case SER_NUMBER_INTEGER: { int i = p.nextIntValue(-2); if (i != -2) { return i; } return read(reader, p); } case SER_NUMBER_LONG: { long l = p.nextLongValue(-2L); if (l != -2L) { return l; } return read(reader, p); } // Other scalar types: case SER_BOOLEAN: { Boolean b = p.nextBooleanValue(); if (b != null) { return b; } return read(reader, p); } } p.nextToken(); return read(reader, p); } @Override public Object read(JSONReader reader, JsonParser p) throws IOException { switch (_typeId) { case SER_INT_ARRAY: return _readIntArray(p); case SER_TREE_NODE: return reader.readTree(); // Textual types, related: case SER_STRING: case SER_CHARACTER_SEQUENCE: return p.getValueAsString(); case SER_CHAR_ARRAY: return p.getValueAsString().toCharArray(); case SER_BYTE_ARRAY: return _readBinary(p); // Number types: case SER_NUMBER_FLOAT: // fall through return Float.valueOf((float) p.getValueAsDouble()); case SER_NUMBER_DOUBLE: return p.getValueAsDouble(); case SER_NUMBER_BYTE: // fall through return (byte) p.getValueAsInt(); case SER_NUMBER_SHORT: // fall through return (short) p.getValueAsInt(); case SER_NUMBER_INTEGER: return p.getValueAsInt(); case SER_NUMBER_LONG: return p.getValueAsLong(); case SER_NUMBER_BIG_DECIMAL: return p.getDecimalValue(); case SER_NUMBER_BIG_INTEGER: return p.getBigIntegerValue(); // Other scalar types: case SER_BOOLEAN: switch (p.currentTokenId()) { case JsonTokenId.ID_TRUE: return Boolean.TRUE; case JsonTokenId.ID_FALSE: return Boolean.FALSE; case JsonTokenId.ID_NULL: // 07-Jul-2020, tatu: since `boolean` and `java.lang.Boolean` both handled // here, can not (alas!) separate yet return Boolean.FALSE; case JsonTokenId.ID_STRING: // 07-Jul-2020, tatu: Allow coercion for backwards compatibility (with 2.11) return p.getValueAsBoolean(); } // 07-Jul-2020, tatu: leave out more esoteric coercions with 2.12 break; case SER_CHAR: { String str = p.getValueAsString(); return (str == null || str.isEmpty()) ? ' ' : str.charAt(0); } case SER_CALENDAR: // [jackson-jr#73]: should allow null if (p.hasToken(JsonToken.VALUE_NULL)) { return null; } { long l = _fetchLong(p); Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(l); return cal; } case SER_DATE: // [jackson-jr#73]: should allow null if (p.hasToken(JsonToken.VALUE_NULL)) { return null; } return new Date(_fetchLong(p)); case SER_CLASS: { // [jackson-jr#73]: should allow null if (p.hasToken(JsonToken.VALUE_NULL)) { return null; } String v = p.getValueAsString(); try { return Class.forName(v); } catch (Exception e) { throw new JSONObjectException("Failed to bind java.lang.Class from value '"+v+"'"); } } case SER_FILE: // [jackson-jr#73]: should allow null if (p.hasToken(JsonToken.VALUE_NULL)) { return null; } return new File(p.getValueAsString()); case SER_UUID: // [jackson-jr#73]: should allow null if (p.hasToken(JsonToken.VALUE_NULL)) { return null; } return UUID.fromString(p.getValueAsString()); case SER_URL: // [jackson-jr#73]: should allow null if (p.hasToken(JsonToken.VALUE_NULL)) { return null; } return new URL(p.getValueAsString()); case SER_URI: // [jackson-jr#73]: should allow null if (p.hasToken(JsonToken.VALUE_NULL)) { return null; } return URI.create(p.getValueAsString()); // case SER_MAP: // case SER_LIST: // case SER_COLLECTION: // case SER_OBJECT_ARRAY: // should never get here: we have dedicated readers default: // types that shouldn't get here //case SER_ENUM: } throw JSONObjectException.from(p, "Can not create a `"+_valueType.getName()+"` instance out of "+_tokenDesc(p)); } /* /********************************************************************** /* Read methods for scalars /********************************************************************** */ protected byte[] _readBinary(JsonParser p) throws IOException { return p.getBinaryValue(); } protected int[] _readIntArray(JsonParser p) throws IOException { // !!! TODO throw new JSONObjectException("Reading of int[] not yet implemented"); } protected long _fetchLong(JsonParser p) throws IOException { JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NUMBER_INT) { return p.getLongValue(); } throw JSONObjectException.from(p, "Can not get long numeric value from JSON (to construct " +_valueType.getName()+") from "+_tokenDesc(p, t)); } }