/* * 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.services.dynamodbv2.datamodeling.StandardTypeConverters.Vector; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; /** * {@link DynamoDBTypeConverter} factory and supporting classes. * * <p>To override standard type-conversions,</p> * <pre class="brush: java"> * DynamoDBMapperConfig config = DynamoDBMapperConfig.builder() * .withTypeConverterFactory(DynamoDBTypeConverterFactory.standard().override() * .with(String.class, MyObject.class, new StringToMyObjectConverter()) * .build()) * .build(); * </pre> * <p>Then, on the property, specify the attribute binding,</p> * <pre class="brush: java"> * @DynamoDBTyped(DynamoDBAttributeType.S) * public MyObject getMyObject() * </pre> * * @see com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig */ public abstract class DynamoDBTypeConverterFactory { /** * Gets the type-converter matching the target conversion type. * @param <S> The DynamoDB standard type. * @param <T> The object's field/property type. * @param sourceType The source conversion type. * @param targetType The target conversion type. * @return The type-converter, or null if no match. */ public abstract <S,T> DynamoDBTypeConverter<S,T> getConverter(Class<S> sourceType, Class<T> targetType); /** * Creates a type-converter factory builder using this factory as defaults. */ public final Builder override() { return new Builder(this); } /** * Returns the standard type-converter factory. To override, the factory, * @see DynamoDBTypeConverterFactory#override */ public static final DynamoDBTypeConverterFactory standard() { return StandardTypeConverters.factory(); } /** * Builder for overriding type-converters. */ public static final class Builder { private final ConverterMap overrides = new ConverterMap(); private final DynamoDBTypeConverterFactory defaults; private Builder(DynamoDBTypeConverterFactory defaults) { this.defaults = defaults; } public <S,T> Builder with(Class<S> sourceType, Class<T> targetType, DynamoDBTypeConverter<? extends S,? extends T> converter) { if (Vector.SET.is(sourceType) || Vector.LIST.is(sourceType) || Vector.MAP.is(sourceType)) { throw new DynamoDBMappingException("type [" + sourceType + "] is not supported" + "; type-converter factory only supports scalar conversions"); } overrides.put(sourceType, targetType, converter); return this; } public DynamoDBTypeConverterFactory build() { return new OverrideFactory(defaults, overrides); } } /** * A delegating {@link DynamoDBTypeConverterFactory}. */ public static class DelegateFactory extends DynamoDBTypeConverterFactory { private final DynamoDBTypeConverterFactory delegate; public DelegateFactory(DynamoDBTypeConverterFactory delegate) { this.delegate = delegate; } @Override public <S,T> DynamoDBTypeConverter<S,T> getConverter(Class<S> sourceType, Class<T> targetType) { return delegate.<S,T>getConverter(sourceType, targetType); } } /** * Delegate factory to allow selected types to be overriden. */ private static class OverrideFactory extends DelegateFactory { private final ConverterMap overrides; public OverrideFactory(DynamoDBTypeConverterFactory defaults, ConverterMap overrides) { super(defaults); this.overrides = overrides; } @Override public <S,T> DynamoDBTypeConverter<S,T> getConverter(Class<S> sourceType, Class<T> targetType) { DynamoDBTypeConverter<S,T> converter = overrides.<S,T>get(sourceType, targetType); if (converter == null) { converter = super.<S,T>getConverter(sourceType, targetType); } return converter; } } /** * Map of source and target pairs to the converter. */ private static final class ConverterMap extends LinkedHashMap<Key<?,?>,DynamoDBTypeConverter<?,?>> { private static final long serialVersionUID = -1L; public <S,T> void put(Class<S> sourceType, Class<T> targetType, DynamoDBTypeConverter<? extends S,? extends T> converter) { put(Key.of(sourceType, targetType), converter); } @SuppressWarnings("unchecked") public <S,T> DynamoDBTypeConverter<S,T> get(Class<S> sourceType, Class<T> targetType) { for (final Entry<Key<?,?>,DynamoDBTypeConverter<?,?>> entry : entrySet()) { if (entry.getKey().isAssignableFrom(sourceType, targetType)) { return (DynamoDBTypeConverter<S,T>)entry.getValue(); } } return null; } } /** * Source and target conversion type pair. */ private static final class Key<S,T> extends SimpleImmutableEntry<Class<S>,Class<T>> { private static final long serialVersionUID = -1L; private Key(Class<S> sourceType, Class<T> targetType) { super(sourceType, targetType); } public boolean isAssignableFrom(Class<?> sourceType, Class<?> targetType) { return getKey().isAssignableFrom(sourceType) && getValue().isAssignableFrom(targetType); } public static <S,T> Key<S,T> of(Class<S> sourceType, Class<T> targetType) { return new Key<S,T>(sourceType, targetType); } } }