java-commons/commons-serialize-databind/src/main/java/io/gitlab/jfronny/commons/serialize/databind/impl/adapter/TypeAdapters.java

605 lines
26 KiB
Java

package io.gitlab.jfronny.commons.serialize.databind.impl.adapter;
import io.gitlab.jfronny.commons.data.LazilyParsedNumber;
import io.gitlab.jfronny.commons.data.NumberLimits;
import io.gitlab.jfronny.commons.serialize.databind.api.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.api.TypeAdapter;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.Token;
import io.gitlab.jfronny.commons.serialize.emulated.DataElement;
import io.gitlab.jfronny.commons.serialize.emulated.DataElementSerializer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class TypeAdapters {
@SerializerFor(targets = {boolean.class, Boolean.class})
public static class BooleanTypeAdapter extends TypeAdapter<Boolean> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Boolean value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Boolean deserialize(Reader reader) throws TEx, MalformedDataException {
if (reader.peek() == Token.STRING) {
// special casing for boolean strings
String value = reader.nextString();
if (value.equalsIgnoreCase("true")) {
return true;
} else if (value.equalsIgnoreCase("false")) {
return false;
} else {
throw new MalformedDataException("Expected boolean, got " + value);
}
}
return reader.nextBoolean();
}
}
@SerializerFor(targets = {byte.class, Byte.class})
public static class ByteTypeAdapter extends TypeAdapter<Byte> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Byte value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Byte deserialize(Reader reader) throws TEx, MalformedDataException {
int value;
try {
value = reader.nextInt();
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
// Up to 255 to support unsigned values
if (value < Byte.MIN_VALUE || value > 255) {
throw new MalformedDataException("Value " + value + " is out of range for byte");
}
return (byte) value;
}
}
@SerializerFor(targets = {short.class, Short.class})
public static class ShortTypeAdapter extends TypeAdapter<Short> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Short value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Short deserialize(Reader reader) throws TEx, MalformedDataException {
int value;
try {
value = reader.nextInt();
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
// Up to 65535 to support unsigned values
if (value < Short.MIN_VALUE || value > 65535) {
throw new MalformedDataException("Value " + value + " is out of range for short");
}
return (short) value;
}
}
@SerializerFor(targets = {int.class, Integer.class})
public static class IntegerTypeAdapter extends TypeAdapter<Integer> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Integer value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Integer deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return reader.nextInt();
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = {long.class, Long.class})
public static class LongTypeAdapter extends TypeAdapter<Long> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Long value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Long deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return reader.nextLong();
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = {float.class, Float.class})
public static class FloatTypeAdapter extends TypeAdapter<Float> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Float value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Float deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return (float) reader.nextDouble();
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = {double.class, Double.class})
public static class DoubleTypeAdapter extends TypeAdapter<Double> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Double value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Double deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return reader.nextDouble();
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = {char.class, Character.class})
public static class CharacterTypeAdapter extends TypeAdapter<Character> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Character value, Writer writer) throws TEx, MalformedDataException {
writer.value(String.valueOf(value));
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Character deserialize(Reader reader) throws TEx, MalformedDataException {
String value = reader.nextString();
if (value.length() != 1) {
throw new MalformedDataException("Expected single character, got " + value);
}
return value.charAt(0);
}
}
@SerializerFor(targets = String.class)
public static class StringTypeAdapter extends TypeAdapter<String> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(String value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> String deserialize(Reader reader) throws TEx, MalformedDataException {
return reader.nextString();
}
}
@SerializerFor(targets = BitSet.class)
public static class BitSetTypeAdapter extends TypeAdapter<BitSet> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(BitSet value, Writer writer) throws TEx {
writer.beginArray();
for (int i = 0; i < value.length(); i++) {
writer.value(value.get(i) ? 1 : 0);
}
writer.endArray();
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> BitSet deserialize(Reader reader) throws TEx, MalformedDataException {
BitSet bitset = new BitSet();
reader.beginArray();
int i = 0;
Token tokenType = reader.peek();
while (tokenType != Token.END_ARRAY) {
boolean set;
switch (tokenType) {
case NUMBER:
case STRING:
int intValue = reader.nextInt();
if (intValue == 0) {
set = false;
} else if (intValue == 1) {
set = true;
} else {
throw new MalformedDataException(
"Invalid bitset value "
+ intValue
+ ", expected 0 or 1; at path "
+ reader.getPreviousPath());
}
break;
case BOOLEAN:
set = reader.nextBoolean();
break;
default:
throw new MalformedDataException(
"Invalid bitset value type: " + tokenType + "; at path " + reader.getPath());
}
if (set) {
bitset.set(i);
}
++i;
tokenType = reader.peek();
}
reader.endArray();
return bitset;
}
}
@SerializerFor(targets = AtomicInteger.class)
public static class AtomicIntegerTypeAdapter extends TypeAdapter<AtomicInteger> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(AtomicInteger value, Writer writer) throws TEx {
writer.value(value.get());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> AtomicInteger deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return new AtomicInteger(reader.nextInt());
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = AtomicBoolean.class)
public static class AtomicBooleanTypeAdapter extends TypeAdapter<AtomicBoolean> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(AtomicBoolean value, Writer writer) throws TEx {
writer.value(value.get());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> AtomicBoolean deserialize(Reader reader) throws TEx, MalformedDataException {
return new AtomicBoolean(reader.nextBoolean());
}
}
@SerializerFor(targets = AtomicIntegerArray.class)
public static class AtomicIntegerArrayTypeAdapter extends TypeAdapter<AtomicIntegerArray> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(AtomicIntegerArray value, Writer writer) throws TEx {
writer.beginArray();
for (int i = 0; i < value.length(); i++) {
writer.value(value.get(i));
}
writer.endArray();
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> AtomicIntegerArray deserialize(Reader reader) throws TEx, MalformedDataException {
if (reader.isLenient() && reader.peek() != Token.BEGIN_ARRAY) {
// Coerce
return new AtomicIntegerArray(new int[]{reader.nextInt()});
}
reader.beginArray();
int length = 0;
while (reader.hasNext()) {
reader.nextInt();
length++;
}
reader.endArray();
AtomicIntegerArray array = new AtomicIntegerArray(length);
reader.beginArray();
for (int i = 0; i < length; i++) {
array.set(i, reader.nextInt());
}
reader.endArray();
return array;
}
}
@SerializerFor(targets = BigDecimal.class)
public static class BigDecimalTypeAdapter extends TypeAdapter<BigDecimal> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(BigDecimal value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> BigDecimal deserialize(Reader reader) throws TEx, MalformedDataException {
String value = reader.nextString();
try {
return NumberLimits.parseBigDecimal(value);
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = BigInteger.class)
public static class BigIntegerTypeAdapter extends TypeAdapter<BigInteger> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(BigInteger value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> BigInteger deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return NumberLimits.parseBigInteger(reader.nextString());
} catch (NumberFormatException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = Number.class)
public static class NumberTypeAdapter extends TypeAdapter<Number> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Number value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Number deserialize(Reader reader) throws TEx, MalformedDataException {
return reader.nextNumber();
}
}
@SerializerFor(targets = LazilyParsedNumber.class)
public static class LazilyParsedNumberTypeAdapter extends TypeAdapter<LazilyParsedNumber> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(LazilyParsedNumber value, Writer writer) throws TEx, MalformedDataException {
writer.value(value);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> LazilyParsedNumber deserialize(Reader reader) throws TEx, MalformedDataException {
if (reader.peek() == Token.NUMBER) {
Number number = reader.nextNumber();
if (number instanceof LazilyParsedNumber l) return l;
return new LazilyParsedNumber(number.toString());
}
// Legacy compatibility with Gson
return new LazilyParsedNumber(reader.nextString());
}
}
@SerializerFor(targets = StringBuilder.class)
public static class StringBuilderTypeAdapter extends TypeAdapter<StringBuilder> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(StringBuilder value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.toString());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> StringBuilder deserialize(Reader reader) throws TEx, MalformedDataException {
return new StringBuilder(reader.nextString());
}
}
@SerializerFor(targets = StringBuffer.class)
public static class StringBufferTypeAdapter extends TypeAdapter<StringBuffer> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(StringBuffer value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.toString());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> StringBuffer deserialize(Reader reader) throws TEx, MalformedDataException {
return new StringBuffer(reader.nextString());
}
}
@SerializerFor(targets = URL.class)
public static class URLTypeAdapter extends TypeAdapter<URL> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(URL value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.toExternalForm());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> URL deserialize(Reader reader) throws TEx, MalformedDataException {
try {
String str = reader.nextString();
if (str.equals("null")) return null;
return new URI(str).toURL();
} catch (Exception e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = URI.class)
public static class URITypeAdapter extends TypeAdapter<URI> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(URI value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.toString());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> URI deserialize(Reader reader) throws TEx, MalformedDataException {
try {
String str = reader.nextString();
if (str.equals("null")) return null;
return new URI(str);
} catch (Exception e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = InetAddress.class, hierarchical = true)
public static class InetAddressTypeAdapter extends TypeAdapter<InetAddress> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(InetAddress value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.getHostAddress());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> InetAddress deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return InetAddress.getByName(reader.nextString());
} catch (Exception e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = UUID.class)
public static class UUIDTypeAdapter extends TypeAdapter<UUID> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(UUID value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.toString());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> UUID deserialize(Reader reader) throws TEx, MalformedDataException {
try {
return UUID.fromString(reader.nextString());
} catch (IllegalArgumentException e) {
throw new MalformedDataException(e);
}
}
}
@SerializerFor(targets = Currency.class)
public static class CurrencyTypeAdapter extends TypeAdapter<Currency> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Currency value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.getCurrencyCode());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Currency deserialize(Reader reader) throws TEx, MalformedDataException {
return Currency.getInstance(reader.nextString());
}
}
@SerializerFor(targets = Calendar.class)
public static class CalendarTypeAdapter extends TypeAdapter<Calendar> {
private static final String YEAR = "year";
private static final String MONTH = "month";
private static final String DAY_OF_MONTH = "dayOfMonth";
private static final String HOUR_OF_DAY = "hourOfDay";
private static final String MINUTE = "minute";
private static final String SECOND = "second";
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Calendar value, Writer writer) throws TEx, MalformedDataException {
if (value == null) {
writer.nullValue();
return;
}
writer.beginObject();
writer.name(YEAR);
writer.value(value.get(Calendar.YEAR));
writer.name(MONTH);
writer.value(value.get(Calendar.MONTH));
writer.name(DAY_OF_MONTH);
writer.value(value.get(Calendar.DAY_OF_MONTH));
writer.name(HOUR_OF_DAY);
writer.value(value.get(Calendar.HOUR_OF_DAY));
writer.name(MINUTE);
writer.value(value.get(Calendar.MINUTE));
writer.name(SECOND);
writer.value(value.get(Calendar.SECOND));
writer.endObject();
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Calendar deserialize(Reader reader) throws TEx, MalformedDataException {
reader.beginObject();
int year = 0;
int month = 0;
int dayOfMonth = 0;
int hourOfDay = 0;
int minute = 0;
int second = 0;
while (reader.peek() != Token.END_OBJECT) {
String name = reader.nextName();
int value = reader.nextInt();
switch (name) {
case YEAR:
year = value;
break;
case MONTH:
month = value;
break;
case DAY_OF_MONTH:
dayOfMonth = value;
break;
case HOUR_OF_DAY:
hourOfDay = value;
break;
case MINUTE:
minute = value;
break;
case SECOND:
second = value;
break;
default:
// Ignore unknown JSON property
}
}
reader.endObject();
return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
}
}
@SerializerFor(targets = Locale.class)
public static class LocaleTypeAdapter extends TypeAdapter<Locale> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Locale value, Writer writer) throws TEx, MalformedDataException {
writer.value(value == null ? null : value.toString());
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Locale deserialize(Reader reader) throws TEx, MalformedDataException {
String locale = reader.nextString();
StringTokenizer tokenizer = new StringTokenizer(locale, "_");
String language = null;
String country = null;
String variant = null;
if (tokenizer.hasMoreElements()) {
language = tokenizer.nextToken();
}
if (tokenizer.hasMoreElements()) {
country = tokenizer.nextToken();
}
if (tokenizer.hasMoreElements()) {
variant = tokenizer.nextToken();
}
if (country == null && variant == null) {
return new Locale(language);
} else if (variant == null) {
return new Locale(language, country);
} else {
return new Locale(language, country, variant);
}
}
}
@SerializerFor(targets = DataElement.class, hierarchical = true)
public static class DataElementTypeAdapter extends TypeAdapter<DataElement> {
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(DataElement value, Writer writer) throws TEx, MalformedDataException {
DataElementSerializer.serialize(value, writer);
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> DataElement deserialize(Reader reader) throws TEx, MalformedDataException {
return DataElementSerializer.deserialize(reader);
}
}
}