All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters Maven / Gradle / Ivy

/*
 * Copyright 2016-2020 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  DynamoDBTypeConverter getConverter(Class sourceType, Class targetType) {
        final Scalar source = Scalar.of(sourceType), target = Scalar.of(targetType);
        final Converter toSource = source.getConverter(sourceType, target.type());
        final Converter toTarget = target.getConverter(targetType, source.type());
        return new DynamoDBTypeConverter() {
            @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
             Converter getConverter(Class sourceType, Class targetType) {
                if (sourceType.isEnum() && STRING.map.isAssignableFrom(targetType)) {
                    return (Converter)new ToEnum.FromString(sourceType);
                }
                return super.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.
         */
         Converter getConverter(Class sourceType, Class targetType) {
            return map.getConverter(targetType);
        }

        /**
         * Converts the target instance using the standard type-conversions.
         */
        @SuppressWarnings("unchecked")
        final  S convert(Object o) {
            return getConverter(this.type(), (Class)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  Class type() {
            return (Class)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 {
             DynamoDBTypeConverter,List> join(final DynamoDBTypeConverter scalar) {
                return new DynamoDBTypeConverter,List>() {
                    @Override
                    public final List convert(final List o) {
                        return LIST.convert(o, scalar);
                    }
                    @Override
                    public final List unconvert(final List o) {
                        return LIST.unconvert(o, scalar);
                    }
                };
            }

             List convert(Collection o, DynamoDBTypeConverter scalar) {
                final List vector = new ArrayList(o.size());
                for (final T t : o) {
                    vector.add(scalar.convert(t));
                }
                return vector;
            }

             List unconvert(Collection o, DynamoDBTypeConverter scalar) {
                final List vector = new ArrayList(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 {
             DynamoDBTypeConverter,Map> join(final DynamoDBTypeConverter scalar) {
                return new DynamoDBTypeConverter,Map>() {
                    @Override
                    public final Map convert(final Map o) {
                        return MAP.convert(o, scalar);
                    }
                    @Override
                    public final Map unconvert(final Map o) {
                        return MAP.unconvert(o, scalar);
                    }
                };
            }

             Map convert(Map o, DynamoDBTypeConverter scalar) {
                final Map vector = new LinkedHashMap();
                for (final Map.Entry t : o.entrySet()) {
                    vector.put(t.getKey(), scalar.convert(t.getValue()));
                }
                return vector;
            }

             Map unconvert(Map o, DynamoDBTypeConverter scalar) {
                final Map vector = new LinkedHashMap();
                for (final Map.Entry 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 {
             DynamoDBTypeConverter,Collection> join(final DynamoDBTypeConverter target) {
                return new DynamoDBTypeConverter,Collection>() {
                    @Override
                    public List convert(final Collection o) {
                        return LIST.convert(o, target);
                    }
                    @Override
                    public Collection unconvert(final List o) {
                        return SET.unconvert(o, target);
                    }
                };
            }

             Set unconvert(Collection o, DynamoDBTypeConverter scalar) {
                final Set vector = new LinkedHashSet();
                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,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  ConverterMap with(Class targetType, Converter converter) {
            put(targetType, converter);
            return this;
        }

        private boolean isAssignableFrom(Class type) {
            return type.isPrimitive() ? primitiveType == type : referenceType.isAssignableFrom(type);
        }

        @SuppressWarnings("unchecked")
        private  Converter getConverter(Class targetType) {
            for (final Map.Entry,Converter> entry : entrySet()) {
                if (entry.getKey().isAssignableFrom(targetType)) {
                    return (Converter)entry.getValue();
                }
            }
            if (isAssignableFrom(targetType)) {
                return (Converter)ToObject.FromObject;
            }
            throw new DynamoDBMappingException(
                "type [" + targetType + "] is not supported; no conversion from " + referenceType
            );
        }
    }

    /**
     * {@link BigDecimal} conversion functions.
     */
    private static abstract class ToBigDecimal extends Converter {
        private static final ToBigDecimal FromString = new ToBigDecimal() {
            @Override
            public final BigDecimal convert(final String o) {
                return new BigDecimal(o);
            }
        };
    }

    /**
     * {@link BigInteger} conversion functions.
     */
    private static abstract class ToBigInteger extends Converter {
        private static final ToBigInteger FromString = new ToBigInteger() {
            @Override
            public final BigInteger convert(final String o) {
                return new BigInteger(o);
            }
        };
    }

    /**
     * {@link Boolean} conversion functions.
     */
    private static abstract class ToBoolean extends Converter {
        private static final ToBoolean FromString = new ToBoolean() {
            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 extends Converter {
        private static final ToByte FromNumber = new ToByte() {
            @Override
            public final Byte convert(final Number o) {
                return o.byteValue();
            }
        };

        private static final ToByte FromString = new ToByte() {
            @Override
            public final Byte convert(final String o) {
                return Byte.valueOf(o);
            }
        };
    }

    /**
     * {@link byte} array conversion functions.
     */
    private static abstract class ToByteArray extends Converter {
        private static final ToByteArray FromByteBuffer = new ToByteArray() {
            @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 FromString = new ToByteArray() {
            @Override
            public final byte[] convert(final String o) {
                return o.getBytes(Charset.forName("UTF-8"));
            }
        };
    }

    /**
     * {@link ByteBuffer} conversion functions.
     */
    private static abstract class ToByteBuffer extends Converter {
        private static final ToByteBuffer FromByteArray = new ToByteBuffer() {
            @Override
            public final ByteBuffer convert(final byte[] o) {
                return ByteBuffer.wrap(o);
            }
        };

        private static final ToByteBuffer FromUuid = new ToByteBuffer() {
            @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 extends Converter {
        private static final ToCalendar FromDate = new ToCalendar() {
            @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 extends Converter {
        private static final ToCharacter FromString = new ToCharacter() {
            @Override
            public final Character convert(final String o) {
                return Character.valueOf(o.charAt(0));
            }
        };
    }

    /**
     * {@link Currency} conversion functions.
     */
    private static abstract class ToCurrency extends Converter {
        private static final ToCurrency FromString = new ToCurrency() {
            @Override
            public final Currency convert(final String o) {
                return Currency.getInstance(o);
            }
        };
    }

    /**
     * {@link Date} conversion functions.
     */
    private static abstract class ToDate extends Converter {
        private static final ToDate FromCalendar = new ToDate() {
            @Override
            public final Date convert(final Calendar o) {
                return o.getTime();
            }
        };

        private static final ToDate FromDateTime = new ToDate() {
            @Override
            public final Date convert(final DateTime o) {
                return o.toDate();
            }
        };

        private static final ToDate FromLong = new ToDate() {
            @Override
            public final Date convert(final Long o) {
                return new Date(o);
            }
        };

        private static final ToDate FromString = new ToDate() {
            @Override
            public final Date convert(final String o) {
                return DateUtils.parseISO8601Date(o);
            }
        };
    }

    /**
     * {@link DateTime} conversion functions.
     */
    private static abstract class ToDateTime extends Converter {
        private static final ToDateTime FromDate = new ToDateTime() {
            public final DateTime convert(final Date o) {
                return new DateTime(o);
            }
        };
    }

    /**
     * {@link Double} conversion functions.
     */
    private static abstract class ToDouble extends Converter {
        private static final ToDouble FromNumber = new ToDouble() {
            @Override
            public final Double convert(final Number o) {
                return o.doubleValue();
            }
        };

        private static final ToDouble FromString = new ToDouble() {
            @Override
            public final Double convert(final String o) {
                return Double.valueOf(o);
            }
        };
    }

    /**
     * {@link Enum} from {@link String}
     */
    private static abstract class ToEnum,T> extends Converter {
        private static final class FromString> extends ToEnum {
            private final Class sourceType;
            private FromString(final Class 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 extends Converter {
        private static final ToFloat FromNumber = new ToFloat() {
            @Override
            public final Float convert(final Number o) {
                return o.floatValue();
            }
        };

        private static final ToFloat FromString = new ToFloat() {
            @Override
            public final Float convert(final String o) {
                return Float.valueOf(o);
            }
        };
    }

    /**
     * {@link Integer} conversion functions.
     */
    private static abstract class ToInteger extends Converter {
        private static final ToInteger FromNumber = new ToInteger() {
            @Override
            public final Integer convert(final Number o) {
                return o.intValue();
            }
        };

        private static final ToInteger FromString = new ToInteger() {
            @Override
            public final Integer convert(final String o) {
                return Integer.valueOf(o);
            }
        };
    }

    /**
     * {@link Locale} conversion functions.
     */
    private static abstract class ToLocale extends Converter {
        private static final ToLocale FromString = new ToLocale() {
            @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 extends Converter {
        private static final ToLong FromDate = new ToLong() {
            @Override
            public final Long convert(final Date o) {
                return o.getTime();
            }
        };

        private static final ToLong FromNumber = new ToLong() {
            @Override
            public final Long convert(final Number o) {
                return o.longValue();
            }
        };

        private static final ToLong FromString = new ToLong() {
            @Override
            public final Long convert(final String o) {
                return Long.valueOf(o);
            }
        };
    }

    /**
     * {@link Short} conversion functions.
     */
    private static abstract class ToShort extends Converter {
        private static final ToShort FromNumber = new ToShort() {
            @Override
            public final Short convert(final Number o) {
                return o.shortValue();
            }
        };

        private static final ToShort FromString = new ToShort() {
            @Override
            public final Short convert(final String o) {
                return Short.valueOf(o);
            }
        };
    }

    /**
     * {@link String} conversion functions.
     */
    private static abstract class ToString extends Converter {
        private static final ToString FromBoolean = new ToString() {
            @Override
            public final String convert(final Boolean o) {
                return Boolean.TRUE.equals(o) ? "1" : "0";
            }
        };

        private static final ToString FromByteArray = new ToString() {
            @Override
            public final String convert(final byte[] o) {
                return new String(o, Charset.forName("UTF-8"));
            }
        };

        private static final ToString FromDate = new ToString() {
            @Override
            public final String convert(final Date o) {
                return DateUtils.formatISO8601Date(o);
            }
        };

        private static final ToString FromEnum = new ToString() {
            @Override
            public final String convert(final Enum o) {
                return o.name();
            }
        };

        private static final ToString FromLocale = new ToString() {
            @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 FromNumber = new ToString() {
            @Override
            public final String convert(final Number o) {
                return o.toString();
            }
        };

        private static final ToString FromTimeZone = new ToString() {
            @Override
            public final String convert(final TimeZone o) {
                return o.getID();
            }
        };

        private static final ToString FromObject = new ToString() {
            @Override
            public final String convert(final Object o) {
                return o.toString();
            }
        };
    }

    /**
     * {@link TimeZone} conversion functions.
     */
    private static abstract class ToTimeZone extends Converter {
        private static final ToTimeZone FromString = new ToTimeZone() {
            @Override
            public final TimeZone convert(final String o) {
                return TimeZone.getTimeZone(o);
            }
        };
    }

    /**
     * {@link java.net.URL} conversion functions.
     */
    private static abstract class ToUrl extends Converter {
        private static final ToUrl FromString = new ToUrl() {
            @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 extends Converter {
        private static final ToUri FromString = new ToUri() {
            @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 extends Converter {
        private static final ToUuid FromByteBuffer = new ToUuid() {
            @Override
            public final java.util.UUID convert(final ByteBuffer o) {
                return new java.util.UUID(o.getLong(), o.getLong());
            }
        };

        private static final ToUuid FromString = new ToUuid() {
            @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 extends Converter {
        private static final ToObject FromObject = new ToObject() {
            @Override
            public final Object convert(final Object o) {
                return o;
            }
        };
    }

    /**
     * One-way type-converter.
     */
    static abstract class Converter {
        final  Converter join(final Converter target) {
            final Converter source = this;
            return new Converter() {
                @Override
                public S convert(final U o) {
                    return source.convert(target.convert(o));
                }
            };
        }
        public abstract S convert(T o);
    }

}