/*
* Copyright 2016-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://aws.amazon.com/apache2.0
*
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and
* limitations under the License.
*/
package com.amazonaws.services.dynamodbv2.datamodeling;
import com.amazonaws.annotation.SdkInternalApi;
import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;
import com.amazonaws.util.DateUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.regex.Pattern;
import org.joda.time.DateTime;
/**
* Type conversions.
*
* @see com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter
*/
@SdkInternalApi
final class StandardTypeConverters extends DynamoDBTypeConverterFactory {
/**
* Standard scalar type-converter factory.
*/
private static final DynamoDBTypeConverterFactory FACTORY = new StandardTypeConverters();
static DynamoDBTypeConverterFactory factory() {
return StandardTypeConverters.FACTORY;
}
/**
* {@inheritDoc}
*/
@Override
public <S,T> DynamoDBTypeConverter<S,T> getConverter(Class<S> sourceType, Class<T> targetType) {
final Scalar source = Scalar.of(sourceType), target = Scalar.of(targetType);
final Converter<S,T> toSource = source.getConverter(sourceType, target.<T>type());
final Converter<T,S> toTarget = target.getConverter(targetType, source.<S>type());
return new DynamoDBTypeConverter<S,T>() {
@Override
public final S convert(final T o) {
return toSource.convert(o);
}
@Override
public final T unconvert(final S o) {
return toTarget.convert(o);
}
};
}
/**
* Standard scalar types.
*/
static enum Scalar {
/**
* {@link BigDecimal}
*/
BIG_DECIMAL(ScalarAttributeType.N, new ConverterMap(BigDecimal.class, null)
.with(Number.class, ToBigDecimal.FromString.join(ToString.FromNumber))
.with(String.class, ToBigDecimal.FromString)
),
/**
* {@link BigInteger}
*/
BIG_INTEGER(ScalarAttributeType.N, new ConverterMap(BigInteger.class, null)
.with(Number.class, ToBigInteger.FromString.join(ToString.FromNumber))
.with(String.class, ToBigInteger.FromString)
),
/**
* {@link Boolean}
*/
BOOLEAN(ScalarAttributeType.N, new ConverterMap(Boolean.class, Boolean.TYPE)
.with(Number.class, ToBoolean.FromString.join(ToString.FromNumber))
.with(String.class, ToBoolean.FromString)
),
/**
* {@link Byte}
*/
BYTE(ScalarAttributeType.N, new ConverterMap(Byte.class, Byte.TYPE)
.with(Number.class, ToByte.FromNumber)
.with(String.class, ToByte.FromString)
),
/**
* {@link Byte} array
*/
BYTE_ARRAY(ScalarAttributeType.B, new ConverterMap(byte[].class, null)
.with(ByteBuffer.class, ToByteArray.FromByteBuffer)
.with(String.class, ToByteArray.FromString)
),
/**
* {@link ByteBuffer}
*/
BYTE_BUFFER(ScalarAttributeType.B, new ConverterMap(ByteBuffer.class, null)
.with(byte[].class, ToByteBuffer.FromByteArray)
.with(String.class, ToByteBuffer.FromByteArray.join(ToByteArray.FromString))
.with(java.util.UUID.class, ToByteBuffer.FromUuid)
),
/**
* {@link Calendar}
*/
CALENDAR(ScalarAttributeType.S, new ConverterMap(Calendar.class, null)
.with(Date.class, ToCalendar.FromDate)
.with(DateTime.class, ToCalendar.FromDate.join(ToDate.FromDateTime))
.with(Long.class, ToCalendar.FromDate.join(ToDate.FromLong))
.with(String.class, ToCalendar.FromDate.join(ToDate.FromString))
),
/**
* {@link Character}
*/
CHARACTER(ScalarAttributeType.S, new ConverterMap(Character.class, Character.TYPE)
.with(String.class, ToCharacter.FromString)
),
/**
* {@link Currency}
*/
CURRENCY(ScalarAttributeType.S, new ConverterMap(Currency.class, null)
.with(String.class, ToCurrency.FromString)
),
/**
* {@link Date}
*/
DATE(ScalarAttributeType.S, new ConverterMap(Date.class, null)
.with(Calendar.class, ToDate.FromCalendar)
.with(DateTime.class, ToDate.FromDateTime)
.with(Long.class, ToDate.FromLong)
.with(String.class, ToDate.FromString)
),
/**
* {@link DateTime}
*/
DATE_TIME(/*ScalarAttributeType.S*/null, new ConverterMap(DateTime.class, null)
.with(Calendar.class, ToDateTime.FromDate.join(ToDate.FromCalendar))
.with(Date.class, ToDateTime.FromDate)
.with(Long.class, ToDateTime.FromDate.join(ToDate.FromLong))
.with(String.class, ToDateTime.FromDate.join(ToDate.FromString))
),
/**
* {@link Double}
*/
DOUBLE(ScalarAttributeType.N, new ConverterMap(Double.class, Double.TYPE)
.with(Number.class, ToDouble.FromNumber)
.with(String.class, ToDouble.FromString)
),
/**
* {@link Float}
*/
FLOAT(ScalarAttributeType.N, new ConverterMap(Float.class, Float.TYPE)
.with(Number.class, ToFloat.FromNumber)
.with(String.class, ToFloat.FromString)
),
/**
* {@link Integer}
*/
INTEGER(ScalarAttributeType.N, new ConverterMap(Integer.class, Integer.TYPE)
.with(Number.class, ToInteger.FromNumber)
.with(String.class, ToInteger.FromString)
),
/**
* {@link Locale}
*/
LOCALE(ScalarAttributeType.S, new ConverterMap(Locale.class, null)
.with(String.class, ToLocale.FromString)
),
/**
* {@link Long}
*/
LONG(ScalarAttributeType.N, new ConverterMap(Long.class, Long.TYPE)
.with(Date.class, ToLong.FromDate)
.with(DateTime.class, ToLong.FromDate.join(ToDate.FromDateTime))
.with(Calendar.class, ToLong.FromDate.join(ToDate.FromCalendar))
.with(Number.class, ToLong.FromNumber)
.with(String.class, ToLong.FromString)
),
/**
* {@link S3Link}
*/
S3_LINK(ScalarAttributeType.S, new ConverterMap(S3Link.class, null)),
/**
* {@link Short}
*/
SHORT(ScalarAttributeType.N, new ConverterMap(Short.class, Short.TYPE)
.with(Number.class, ToShort.FromNumber)
.with(String.class, ToShort.FromString)
),
/**
* {@link String}
*/
STRING(ScalarAttributeType.S, new ConverterMap(String.class, null)
.with(Boolean.class, ToString.FromBoolean)
.with(byte[].class, ToString.FromByteArray)
.with(ByteBuffer.class, ToString.FromByteArray.join(ToByteArray.FromByteBuffer))
.with(Calendar.class, ToString.FromDate.join(ToDate.FromCalendar))
.with(Date.class, ToString.FromDate)
.with(Enum.class, ToString.FromEnum)
.with(Locale.class, ToString.FromLocale)
.with(TimeZone.class, ToString.FromTimeZone)
.with(Object.class, ToString.FromObject)
),
/**
* {@link TimeZone}
*/
TIME_ZONE(ScalarAttributeType.S, new ConverterMap(TimeZone.class, null)
.with(String.class, ToTimeZone.FromString)
),
/**
* {@link java.net.URL}
*/
URL(ScalarAttributeType.S, new ConverterMap(java.net.URL.class, null)
.with(String.class, ToUrl.FromString)
),
/**
* {@link java.net.URI}
*/
URI(ScalarAttributeType.S, new ConverterMap(java.net.URI.class, null)
.with(String.class, ToUri.FromString)
),
/**
* {@link java.util.UUID}
*/
UUID(ScalarAttributeType.S, new ConverterMap(java.util.UUID.class, null)
.with(ByteBuffer.class, ToUuid.FromByteBuffer)
.with(String.class, ToUuid.FromString)
),
/**
* {@link Object}; default must be last
*/
DEFAULT(null, new ConverterMap(Object.class, null)) {
@Override
<S,T> Converter<S,T> getConverter(Class<S> sourceType, Class<T> targetType) {
if (sourceType.isEnum() && STRING.map.isAssignableFrom(targetType)) {
return (Converter<S,T>)new ToEnum.FromString(sourceType);
}
return super.<S,T>getConverter(sourceType, targetType);
}
};
/**
* The scalar attribute type.
*/
private final ScalarAttributeType scalarAttributeType;
/**
* The mapping of conversion functions for this scalar.
*/
private final ConverterMap map;
/**
* Constructs a new scalar with the specified conversion mappings.
*/
private Scalar(ScalarAttributeType scalarAttributeType, ConverterMap map) {
this.scalarAttributeType = scalarAttributeType;
this.map = map;
}
/**
* Returns the function to convert from the specified target class to
* this scalar type.
*/
<S,T> Converter<S,T> getConverter(Class<S> sourceType, Class<T> targetType) {
return map.<S,T>getConverter(targetType);
}
/**
* Converts the target instance using the standard type-conversions.
*/
@SuppressWarnings("unchecked")
final <S> S convert(Object o) {
return getConverter(this.<S>type(), (Class<Object>)o.getClass()).convert(o);
}
/**
* Determines if the scalar is of the specified scalar attribute type.
*/
final boolean is(final ScalarAttributeType scalarAttributeType) {
return this.scalarAttributeType == scalarAttributeType;
}
/**
* Determines if the class represented by this scalar is either the
* same as or a supertype of the specified target type.
*/
final boolean is(final Class<?> type) {
return this.map.isAssignableFrom(type);
}
/**
* Returns the primary reference type.
*/
@SuppressWarnings("unchecked")
final <S> Class<S> type() {
return (Class<S>)this.map.referenceType;
}
/**
* Returns the first matching scalar, which may be the same as or a
* supertype of the specified target class.
*/
static Scalar of(Class<?> type) {
for (final Scalar scalar : Scalar.values()) {
if (scalar.is(type)) {
return scalar;
}
}
return DEFAULT;
}
}
/**
* Standard vector types.
*/
static abstract class Vector {
/**
* {@link List}
*/
static final ToList LIST = new ToList();
static final class ToList extends Vector {
<S,T> DynamoDBTypeConverter<List<S>,List<T>> join(final DynamoDBTypeConverter<S,T> scalar) {
return new DynamoDBTypeConverter<List<S>,List<T>>() {
@Override
public final List<S> convert(final List<T> o) {
return LIST.<S,T>convert(o, scalar);
}
@Override
public final List<T> unconvert(final List<S> o) {
return LIST.<S,T>unconvert(o, scalar);
}
};
}
<S,T> List<S> convert(Collection<T> o, DynamoDBTypeConverter<S,T> scalar) {
final List<S> vector = new ArrayList<S>(o.size());
for (final T t : o) {
vector.add(scalar.convert(t));
}
return vector;
}
<S,T> List<T> unconvert(Collection<S> o, DynamoDBTypeConverter<S,T> scalar) {
final List<T> vector = new ArrayList<T>(o.size());
for (final S s : o) {
vector.add(scalar.unconvert(s));
}
return vector;
}
@Override
boolean is(final Class<?> type) {
return List.class.isAssignableFrom(type);
}
}
/**
* {@link Map}
*/
static final ToMap MAP = new ToMap();
static final class ToMap extends Vector {
<K,S,T> DynamoDBTypeConverter<Map<K,S>,Map<K,T>> join(final DynamoDBTypeConverter<S,T> scalar) {
return new DynamoDBTypeConverter<Map<K,S>,Map<K,T>>() {
@Override
public final Map<K,S> convert(final Map<K,T> o) {
return MAP.<K,S,T>convert(o, scalar);
}
@Override
public final Map<K,T> unconvert(final Map<K,S> o) {
return MAP.<K,S,T>unconvert(o, scalar);
}
};
}
<K,S,T> Map<K,S> convert(Map<K,T> o, DynamoDBTypeConverter<S,T> scalar) {
final Map<K,S> vector = new LinkedHashMap<K,S>();
for (final Map.Entry<K,T> t : o.entrySet()) {
vector.put(t.getKey(), scalar.convert(t.getValue()));
}
return vector;
}
<K,S,T> Map<K,T> unconvert(Map<K,S> o, DynamoDBTypeConverter<S,T> scalar) {
final Map<K,T> vector = new LinkedHashMap<K,T>();
for (final Map.Entry<K,S> s : o.entrySet()) {
vector.put(s.getKey(), scalar.unconvert(s.getValue()));
}
return vector;
}
boolean is(final Class<?> type) {
return Map.class.isAssignableFrom(type);
}
}
/**
* {@link Set}
*/
static final ToSet SET = new ToSet();
static final class ToSet extends Vector {
<S,T> DynamoDBTypeConverter<List<S>,Collection<T>> join(final DynamoDBTypeConverter<S,T> target) {
return new DynamoDBTypeConverter<List<S>,Collection<T>>() {
@Override
public List<S> convert(final Collection<T> o) {
return LIST.<S,T>convert(o, target);
}
@Override
public Collection<T> unconvert(final List<S> o) {
return SET.<S,T>unconvert(o, target);
}
};
}
<S,T> Set<T> unconvert(Collection<S> o, DynamoDBTypeConverter<S,T> scalar) {
final Set<T> vector = new LinkedHashSet<T>();
for (final S s : o) {
if (vector.add(scalar.unconvert(s)) == false) {
throw new DynamoDBMappingException("duplicate value (" + s + ")");
}
}
return vector;
}
boolean is(final Class<?> type) {
return Set.class.isAssignableFrom(type);
}
}
/**
* Determines if the class represented by this vector is either the
* same as or a supertype of the specified target type.
*/
abstract boolean is(Class<?> type);
}
/**
* Converter map.
*/
private static class ConverterMap extends LinkedHashMap<Class<?>,Converter<?,?>> {
private static final long serialVersionUID = -1L;
private final Class<?> referenceType, primitiveType;
private ConverterMap(Class<?> referenceType, Class<?> primitiveType) {
this.referenceType = referenceType;
this.primitiveType = primitiveType;
}
private <S,T> ConverterMap with(Class<T> targetType, Converter<S,T> converter) {
put(targetType, converter);
return this;
}
private boolean isAssignableFrom(Class<?> type) {
return type.isPrimitive() ? primitiveType == type : referenceType.isAssignableFrom(type);
}
@SuppressWarnings("unchecked")
private <S,T> Converter<S,T> getConverter(Class<T> targetType) {
for (final Map.Entry<Class<?>,Converter<?,?>> entry : entrySet()) {
if (entry.getKey().isAssignableFrom(targetType)) {
return (Converter<S,T>)entry.getValue();
}
}
if (isAssignableFrom(targetType)) {
return (Converter<S,T>)ToObject.FromObject;
}
throw new DynamoDBMappingException(
"type [" + targetType + "] is not supported; no conversion from " + referenceType
);
}
}
/**
* {@link BigDecimal} conversion functions.
*/
private static abstract class ToBigDecimal<T> extends Converter<BigDecimal,T> {
private static final ToBigDecimal<String> FromString = new ToBigDecimal<String>() {
@Override
public final BigDecimal convert(final String o) {
return new BigDecimal(o);
}
};
}
/**
* {@link BigInteger} conversion functions.
*/
private static abstract class ToBigInteger<T> extends Converter<BigInteger,T> {
private static final ToBigInteger<String> FromString = new ToBigInteger<String>() {
@Override
public final BigInteger convert(final String o) {
return new BigInteger(o);
}
};
}
/**
* {@link Boolean} conversion functions.
*/
private static abstract class ToBoolean<T> extends Converter<Boolean,T> {
private static final ToBoolean<String> FromString = new ToBoolean<String>() {
private final Pattern N0 = Pattern.compile("(?i)[N0]");
private final Pattern Y1 = Pattern.compile("(?i)[Y1]");
@Override
public final Boolean convert(final String o) {
return N0.matcher(o).matches() ? Boolean.FALSE : Y1.matcher(o).matches() ? Boolean.TRUE : Boolean.valueOf(o);
}
};
}
/**
* {@link Byte} conversion functions.
*/
private static abstract class ToByte<T> extends Converter<Byte,T> {
private static final ToByte<Number> FromNumber = new ToByte<Number>() {
@Override
public final Byte convert(final Number o) {
return o.byteValue();
}
};
private static final ToByte<String> FromString = new ToByte<String>() {
@Override
public final Byte convert(final String o) {
return Byte.valueOf(o);
}
};
}
/**
* {@link byte} array conversion functions.
*/
private static abstract class ToByteArray<T> extends Converter<byte[],T> {
private static final ToByteArray<ByteBuffer> FromByteBuffer = new ToByteArray<ByteBuffer>() {
@Override
public final byte[] convert(final ByteBuffer o) {
if (o.hasArray()) {
return o.array();
}
final byte[] value = new byte[o.remaining()];
o.get(value);
return value;
}
};
private static final ToByteArray<String> FromString = new ToByteArray<String>() {
@Override
public final byte[] convert(final String o) {
return o.getBytes(Charset.forName("UTF-8"));
}
};
}
/**
* {@link ByteBuffer} conversion functions.
*/
private static abstract class ToByteBuffer<T> extends Converter<ByteBuffer,T> {
private static final ToByteBuffer<byte[]> FromByteArray = new ToByteBuffer<byte[]>() {
@Override
public final ByteBuffer convert(final byte[] o) {
return ByteBuffer.wrap(o);
}
};
private static final ToByteBuffer<java.util.UUID> FromUuid = new ToByteBuffer<java.util.UUID>() {
@Override
public final ByteBuffer convert(final java.util.UUID o) {
final ByteBuffer value = ByteBuffer.allocate(16);
value.putLong(o.getMostSignificantBits()).putLong(o.getLeastSignificantBits());
value.position(0);
return value;
}
};
}
/**
* {@link Calendar} conversion functions.
*/
private static abstract class ToCalendar<T> extends Converter<Calendar,T> {
private static final ToCalendar<Date> FromDate = new ToCalendar<Date>() {
@Override
public final Calendar convert(final Date o) {
final Calendar value = Calendar.getInstance();
value.setTime(o);
return value;
}
};
}
/**
* {@link Character} conversion functions.
*/
private static abstract class ToCharacter<T> extends Converter<Character,T> {
private static final ToCharacter<String> FromString = new ToCharacter<String>() {
@Override
public final Character convert(final String o) {
return Character.valueOf(o.charAt(0));
}
};
}
/**
* {@link Currency} conversion functions.
*/
private static abstract class ToCurrency<T> extends Converter<Currency,T> {
private static final ToCurrency<String> FromString = new ToCurrency<String>() {
@Override
public final Currency convert(final String o) {
return Currency.getInstance(o);
}
};
}
/**
* {@link Date} conversion functions.
*/
private static abstract class ToDate<T> extends Converter<Date,T> {
private static final ToDate<Calendar> FromCalendar = new ToDate<Calendar>() {
@Override
public final Date convert(final Calendar o) {
return o.getTime();
}
};
private static final ToDate<DateTime> FromDateTime = new ToDate<DateTime>() {
@Override
public final Date convert(final DateTime o) {
return o.toDate();
}
};
private static final ToDate<Long> FromLong = new ToDate<Long>() {
@Override
public final Date convert(final Long o) {
return new Date(o);
}
};
private static final ToDate<String> FromString = new ToDate<String>() {
@Override
public final Date convert(final String o) {
return DateUtils.parseISO8601Date(o);
}
};
}
/**
* {@link DateTime} conversion functions.
*/
private static abstract class ToDateTime<T> extends Converter<DateTime,T> {
private static final ToDateTime<Date> FromDate = new ToDateTime<Date>() {
public final DateTime convert(final Date o) {
return new DateTime(o);
}
};
}
/**
* {@link Double} conversion functions.
*/
private static abstract class ToDouble<T> extends Converter<Double,T> {
private static final ToDouble<Number> FromNumber = new ToDouble<Number>() {
@Override
public final Double convert(final Number o) {
return o.doubleValue();
}
};
private static final ToDouble<String> FromString = new ToDouble<String>() {
@Override
public final Double convert(final String o) {
return Double.valueOf(o);
}
};
}
/**
* {@link Enum} from {@link String}
*/
private static abstract class ToEnum<S extends Enum<S>,T> extends Converter<S,T> {
private static final class FromString<S extends Enum<S>> extends ToEnum<S,String> {
private final Class<S> sourceType;
private FromString(final Class<S> sourceType) {
this.sourceType = sourceType;
}
@Override
public final S convert(final String o) {
return Enum.valueOf(sourceType, o);
}
}
}
/**
* {@link Float} conversion functions.
*/
private static abstract class ToFloat<T> extends Converter<Float,T> {
private static final ToFloat<Number> FromNumber = new ToFloat<Number>() {
@Override
public final Float convert(final Number o) {
return o.floatValue();
}
};
private static final ToFloat<String> FromString = new ToFloat<String>() {
@Override
public final Float convert(final String o) {
return Float.valueOf(o);
}
};
}
/**
* {@link Integer} conversion functions.
*/
private static abstract class ToInteger<T> extends Converter<Integer,T> {
private static final ToInteger<Number> FromNumber = new ToInteger<Number>() {
@Override
public final Integer convert(final Number o) {
return o.intValue();
}
};
private static final ToInteger<String> FromString = new ToInteger<String>() {
@Override
public final Integer convert(final String o) {
return Integer.valueOf(o);
}
};
}
/**
* {@link Locale} conversion functions.
*/
private static abstract class ToLocale<T> extends Converter<Locale,T> {
private static final ToLocale<String> FromString = new ToLocale<String>() {
@Override
public final Locale convert(final String o) {
final String[] value = o.split("-", 3);
if (value.length == 3) return new Locale(value[0], value[1], value[2]);
if (value.length == 2) return new Locale(value[0], value[1]);
return new Locale(value[0]); //JDK7+: return Locale.forLanguageTag(o);
}
};
}
/**
* {@link Long} conversion functions.
*/
private static abstract class ToLong<T> extends Converter<Long,T> {
private static final ToLong<Date> FromDate = new ToLong<Date>() {
@Override
public final Long convert(final Date o) {
return o.getTime();
}
};
private static final ToLong<Number> FromNumber = new ToLong<Number>() {
@Override
public final Long convert(final Number o) {
return o.longValue();
}
};
private static final ToLong<String> FromString = new ToLong<String>() {
@Override
public final Long convert(final String o) {
return Long.valueOf(o);
}
};
}
/**
* {@link Short} conversion functions.
*/
private static abstract class ToShort<T> extends Converter<Short,T> {
private static final ToShort<Number> FromNumber = new ToShort<Number>() {
@Override
public final Short convert(final Number o) {
return o.shortValue();
}
};
private static final ToShort<String> FromString = new ToShort<String>() {
@Override
public final Short convert(final String o) {
return Short.valueOf(o);
}
};
}
/**
* {@link String} conversion functions.
*/
private static abstract class ToString<T> extends Converter<String,T> {
private static final ToString<Boolean> FromBoolean = new ToString<Boolean>() {
@Override
public final String convert(final Boolean o) {
return Boolean.TRUE.equals(o) ? "1" : "0";
}
};
private static final ToString<byte[]> FromByteArray = new ToString<byte[]>() {
@Override
public final String convert(final byte[] o) {
return new String(o, Charset.forName("UTF-8"));
}
};
private static final ToString<Date> FromDate = new ToString<Date>() {
@Override
public final String convert(final Date o) {
return DateUtils.formatISO8601Date(o);
}
};
private static final ToString<Enum> FromEnum = new ToString<Enum>() {
@Override
public final String convert(final Enum o) {
return o.name();
}
};
private static final ToString<Locale> FromLocale = new ToString<Locale>() {
@Override
public final String convert(final Locale o) {
final StringBuilder value = new StringBuilder(o.getLanguage());
if (!o.getCountry().isEmpty() || !o.getVariant().isEmpty()) {
value.append("-").append(o.getCountry());
}
if (!o.getVariant().isEmpty()) {
value.append("-").append(o.getVariant());
}
return value.toString(); //JDK7+: return o.toLanguageTag();
}
};
private static final ToString<Number> FromNumber = new ToString<Number>() {
@Override
public final String convert(final Number o) {
return o.toString();
}
};
private static final ToString<TimeZone> FromTimeZone = new ToString<TimeZone>() {
@Override
public final String convert(final TimeZone o) {
return o.getID();
}
};
private static final ToString<Object> FromObject = new ToString<Object>() {
@Override
public final String convert(final Object o) {
return o.toString();
}
};
}
/**
* {@link TimeZone} conversion functions.
*/
private static abstract class ToTimeZone<T> extends Converter<TimeZone,T> {
private static final ToTimeZone<String> FromString = new ToTimeZone<String>() {
@Override
public final TimeZone convert(final String o) {
return TimeZone.getTimeZone(o);
}
};
}
/**
* {@link java.net.URL} conversion functions.
*/
private static abstract class ToUrl<T> extends Converter<java.net.URL,String> {
private static final ToUrl<String> FromString = new ToUrl<String>() {
@Override
public final java.net.URL convert(final String o) {
try {
return new java.net.URL(o);
} catch (final java.net.MalformedURLException e) {
throw new IllegalArgumentException("malformed URL", e);
}
}
};
}
/**
* {@link java.net.URI} conversion functions.
*/
private static abstract class ToUri<T> extends Converter<java.net.URI,T> {
private static final ToUri<String> FromString = new ToUri<String>() {
@Override
public final java.net.URI convert(final String o) {
try {
return new java.net.URI(o);
} catch (final java.net.URISyntaxException e) {
throw new IllegalArgumentException("malformed URI", e);
}
}
};
}
/**
* {@link java.util.UUID} conversion functions.
*/
private static abstract class ToUuid<T> extends Converter<java.util.UUID,T> {
private static final ToUuid<ByteBuffer> FromByteBuffer = new ToUuid<ByteBuffer>() {
@Override
public final java.util.UUID convert(final ByteBuffer o) {
return new java.util.UUID(o.getLong(), o.getLong());
}
};
private static final ToUuid<String> FromString = new ToUuid<String>() {
@Override
public final java.util.UUID convert(final String o) {
return java.util.UUID.fromString(o);
}
};
}
/**
* {@link Object} conversion functions.
*/
private static abstract class ToObject<T> extends Converter<Object,T> {
private static final ToObject<Object> FromObject = new ToObject<Object>() {
@Override
public final Object convert(final Object o) {
return o;
}
};
}
/**
* One-way type-converter.
*/
static abstract class Converter<S,T> {
final <U> Converter<S,U> join(final Converter<T,U> target) {
final Converter<S,T> source = this;
return new Converter<S,U>() {
@Override
public S convert(final U o) {
return source.convert(target.convert(o));
}
};
}
public abstract S convert(T o);
}
}