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

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

Go to download

The AWS SDK for Java with support for OSGi. The AWS SDK for Java provides Java APIs for building software on AWS' cost-effective, scalable, and reliable infrastructure products. The AWS Java SDK allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon AutoScaling, etc).

There is a newer version: 1.11.60
Show newest version
/*
 * Copyright 2014-2016 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.ArgumentMarshaller.BinaryAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.BinarySetAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.BooleanAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.ListAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.MapAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.NumberAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.NumberSetAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.StringAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.DynamoDBAttributeType;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.BooleanSetToNumberSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.BooleanToBooleanMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.BooleanToNumberMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteArraySetToBinarySetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteArrayToBinaryMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteBufferSetToBinarySetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteBufferToBinaryMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CalendarSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CalendarToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CollectionToListMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CustomMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.DateSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.DateToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.MapToMapMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.NumberSetToNumberSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.NumberToNumberMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToMapMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.S3LinkToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.StringSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.StringToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.UUIDSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigDecimalSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigDecimalUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigIntegerSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigIntegerUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BooleanSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BooleanUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteArraySetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteArrayUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteBufferSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteBufferUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.CalendarSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.CalendarUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.CustomUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DateSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DateUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DoubleSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DoubleUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.FloatSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.FloatUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.IntegerSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.IntegerUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ListUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.LongSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.LongUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.MapUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.NullableUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ObjectSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ObjectUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.S3LinkUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ShortSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ShortUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.StringSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.StringUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.UUIDSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.UUIDUnmarshaller;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * Pre-defined strategies for mapping between Java types and DynamoDB types.
 */
public final class ConversionSchemas {

    private static final Log LOGGER =
            LogFactory.getLog(ConversionSchemas.class);

    /**
     * The V1 schema mapping, which retains strict backwards compatibility with
     * the original DynamoDB data model. In particular, it marshals Java
     * Booleans as DynamoDB Numbers rather than the newer Boolean type, and does
     * not support marshaling Lists or Maps. It can unmarshal
     * values written in newer formats to ease migration.
     * 

* Use me if you have other code still using an old version of the SDK that * does not understand the new List and Map types and want to ensure that * you don't accidentally start writing values using these types. */ public static final ConversionSchema V1 = v1Builder("V1ConversionSchema").build(); /** * A V2 conversion schema which retains backwards compatibility with the * V1 conversion schema for existing DynamoDB types, but adds the ability * to marshall recursive structures using the new List and Map types. This * is currently the default conversion schema. */ public static final ConversionSchema V2_COMPATIBLE = v2CompatibleBuilder( "V2CompatibleConversionSchema").build(); /** * The native V2 conversion schema. This schema breaks compatibility with * older versions of the mapper that only support the V1 schema by * storing booleans as native DynamoDB Booleans rather than as a 1 or 0 * in a DynamoDB Number. Switching to the V2 schema will prevent older * versions of the mapper from reading items you write that contain * booleans. */ public static final ConversionSchema V2 = v2Builder("V2ConversionSchema").build(); static final ConversionSchema DEFAULT = V2_COMPATIBLE; /** * A ConversionSchema builder that defaults to building {@link #V1}. */ public static Builder v1Builder(String name) { return new Builder(name, V1MarshallerSet.marshallers(), V1MarshallerSet.setMarshallers(), StandardUnmarshallerSet.unmarshallers(), StandardUnmarshallerSet.setUnmarshallers()); } /** * A ConversionSchema builder that defaults to building {@link #V2_COMPATIBLE}. */ public static Builder v2CompatibleBuilder(String name) { return new Builder(name, V2CompatibleMarshallerSet.marshallers(), V2CompatibleMarshallerSet.setMarshallers(), StandardUnmarshallerSet.unmarshallers(), StandardUnmarshallerSet.setUnmarshallers()); } /** * A ConversionSchema builder that defaults to building {@link #V2}. */ public static Builder v2Builder(String name) { return new Builder(name, V2MarshallerSet.marshallers(), V2MarshallerSet.setMarshallers(), StandardUnmarshallerSet.unmarshallers(), StandardUnmarshallerSet.setUnmarshallers()); } public static class Builder { private final String name; private final List> marshallers; private final List> setMarshallers; private final List> unmarshallers; private final List> setUnmarshallers; Builder(String name, List> marshallers, List> setMarshallers, List> unmarshallers, List> setUnmarshallers) { this.name = name; this.marshallers = marshallers; this.setMarshallers = setMarshallers; this.unmarshallers = unmarshallers; this.setUnmarshallers = setUnmarshallers; } /** * Adds marshaling of a type to the schema. Types are in LIFO order, so the last type added * will be the first matched. */ public Builder addFirstType(Class clazz, ArgumentMarshaller marshaller, ArgumentUnmarshaller unmarshaller) { this.marshallers.add(0, Pair.of(clazz, marshaller)); this.unmarshallers.add(0, Pair.of(clazz, unmarshaller)); return this; } /** * Adds marshaling of a Set of a type to the schema. Types are in LIFO order, so the last * type added will be the first matched. */ public Builder addFirstSetType(Class clazz, ArgumentMarshaller marshaller, ArgumentUnmarshaller unmarshaller) { this.setMarshallers.add(0, Pair.of(clazz, marshaller)); this.setUnmarshallers.add(0, Pair.of(clazz, unmarshaller)); return this; } public ConversionSchema build() { return new StandardConversionSchema(name, new AbstractMarshallerSet(marshallers, setMarshallers), new StandardUnmarshallerSet(unmarshallers, setUnmarshallers)); } } static class StandardConversionSchema implements ConversionSchema { private final String name; private final MarshallerSet marshallers; private final UnmarshallerSet unmarshallers; public StandardConversionSchema( String name, MarshallerSet marshallers, UnmarshallerSet unmarshallers) { this.name = name; this.marshallers = new CachingMarshallerSet( new AnnotationAwareMarshallerSet(marshallers)); this.unmarshallers = new CachingUnmarshallerSet( new AnnotationAwareUnmarshallerSet(unmarshallers)); } @Override public ItemConverter getConverter(Dependencies dependencies) { S3ClientCache s3cc = dependencies.get(S3ClientCache.class); return new StandardItemConverter( marshallers, unmarshallers, DynamoDBMappingsRegistry.instance(), s3cc); } @Override public String toString() { return name; } } static class StandardItemConverter implements ItemConverter { private final MarshallerSet marshallerSet; private final UnmarshallerSet unmarshallerSet; private final DynamoDBMappingsRegistry registry; private final S3ClientCache s3cc; public StandardItemConverter( MarshallerSet marshallerSet, UnmarshallerSet unmarshallerSet, DynamoDBMappingsRegistry registry, S3ClientCache s3cc) { this.marshallerSet = marshallerSet; this.unmarshallerSet = unmarshallerSet; this.registry = registry; this.s3cc = s3cc; } @Override public DynamoDBMapperFieldModel getFieldModel(Method getter) { ArgumentMarshaller marshaller = getMarshaller(getter); DynamoDBAttributeType attributeType = null; if (marshaller instanceof StringAttributeMarshaller) { attributeType = DynamoDBAttributeType.S; } else if (marshaller instanceof NumberAttributeMarshaller) { attributeType = DynamoDBAttributeType.N; } else if (marshaller instanceof BinaryAttributeMarshaller) { attributeType = DynamoDBAttributeType.B; } else if (marshaller instanceof StringSetAttributeMarshaller) { attributeType = DynamoDBAttributeType.SS; } else if (marshaller instanceof NumberSetAttributeMarshaller) { attributeType = DynamoDBAttributeType.NS; } else if (marshaller instanceof BinarySetAttributeMarshaller) { attributeType = DynamoDBAttributeType.BS; } else if (marshaller instanceof BooleanAttributeMarshaller) { attributeType = DynamoDBAttributeType.BOOL; } else if (marshaller instanceof ListAttributeMarshaller) { attributeType = DynamoDBAttributeType.L; } else if (marshaller instanceof MapAttributeMarshaller) { attributeType = DynamoDBAttributeType.M; } else { throw new DynamoDBMappingException( "Unrecognized marshaller type for " + getter + ": " + marshaller); } // Note, generating the attribute name using this method is not // actually correct for @DynamoDBFlattened attributes, however, // its the best that can be done given only the method. The // proper way to get this information is using the model factory. final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter); return new DynamoDBMapperFieldModel(annotations.attributeName(), attributeType, marshaller); } @Override public AttributeValue convert(Method getter, Object object) { if (object == null) { return null; } ArgumentMarshaller marshaller = getMarshaller(getter); return marshaller.marshall(object); } @Override public Map convert(Object object) { if (object == null) { return null; } Class clazz = object.getClass(); Map result = new HashMap(); final DynamoDBMappingsRegistry.Mappings mappings = registry.mappingsOf(clazz); for (final DynamoDBMappingsRegistry.Mapping mapping : mappings.getMappings()) { Object getterResult = mapping.bean().get(object); if (getterResult != null) { AttributeValue value = convert(mapping.bean().getter(), getterResult); if (value != null) { result.put(mapping.getAttributeName(), value); } } } return result; } private ArgumentMarshaller getMarshaller(Method getter) { ArgumentMarshaller marshaller = marshallerSet.getMarshaller(getter); marshaller = augment(getter.getGenericReturnType(), marshaller); return marshaller; } private ArgumentMarshaller getMemberMarshaller(Type type) { ArgumentMarshaller marshaller = marshallerSet.getMemberMarshaller(type); marshaller = augment(type, marshaller); return marshaller; } private ArgumentMarshaller augment( Type type, ArgumentMarshaller marshaller) { if (marshaller instanceof CollectionToListMarshaller) { return getCollectionToListMarshaller(type); } if (marshaller instanceof MapToMapMarshaller) { return getMapToMapMarshaller(type); } if (marshaller instanceof ObjectToMapMarshaller) { return getObjectToMapMarshaller(type); } return marshaller; } private ArgumentMarshaller getCollectionToListMarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the " + "Collection type " + type + ", which is not " + "parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 1) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the " + "Collection type " + type + "; unexpected number of " + "type arguments."); } ArgumentMarshaller memberMarshaller = getMemberMarshaller(args[0]); return new CollectionToListMarshaller(memberMarshaller); } private ArgumentMarshaller getMapToMapMarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 2) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + "; unexpected number of type " + "arguments."); } if (args[0] != String.class) { throw new DynamoDBMappingException( "Only Map is supported."); } ArgumentMarshaller memberMarshaller = getMemberMarshaller(args[1]); return new MapToMapMarshaller(memberMarshaller); } private ArgumentMarshaller getObjectToMapMarshaller(Type type) { Type localType = type; if (localType instanceof ParameterizedType) { localType = ((ParameterizedType) localType).getRawType(); } if (!(localType instanceof Class)) { throw new DynamoDBMappingException( "Cannot convert " + type + " to a class"); } Class clazz = (Class) localType; if (!StandardAnnotationMaps.of(clazz).has(DynamoDBDocument.class)) { throw new DynamoDBMappingException( "Cannot marshall type " + type + " without a custom marshaler or @DynamoDBDocument " + "annotation."); } return new ObjectToMapMarshaller(this); } @Override public Object unconvert( Method getter, Method setter, AttributeValue value) { ArgumentUnmarshaller unmarshaller = getUnmarshaller(getter, setter); return unmarshall(unmarshaller, setter, value); } @Override public T unconvert( Class clazz, Map value) { T result = createObject(clazz); if (value == null || value.isEmpty()) { return result; } final DynamoDBMappingsRegistry.Mappings mappings = registry.mappingsOf(clazz); for (final DynamoDBMappingsRegistry.Mapping mapping : mappings.getMappings()) { String attributeName = mapping.getAttributeName(); AttributeValue av = value.get(attributeName); if (av != null) { ArgumentUnmarshaller unmarshaller = getUnmarshaller(mapping.bean().getter(), mapping.bean().setter()); Object unmarshalled = unmarshall(unmarshaller, mapping.bean().setter(), av); mapping.bean().set(result, unmarshalled); } } return result; } private ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { ArgumentUnmarshaller unmarshaller = unmarshallerSet.getUnmarshaller(getter, setter); unmarshaller = augment( setter.getGenericParameterTypes()[0], unmarshaller); return new NullableUnmarshaller(unmarshaller); } private ArgumentUnmarshaller getMemberUnmarshaller(Type type) { ArgumentUnmarshaller unmarshaller = unmarshallerSet.getMemberUnmarshaller(type); unmarshaller = augment(type, unmarshaller); return new NullableUnmarshaller(unmarshaller); } private ArgumentUnmarshaller augment( Type type, ArgumentUnmarshaller unmarshaller) { // Inject our s3 client cache if it's an S3LinkUnmarshaller. if (unmarshaller instanceof S3LinkUnmarshaller) { return new S3LinkUnmarshaller(s3cc); } // Inject an appropriate member-type unmarshaller if it's a list, // object-set, or map unmarshaller. if (unmarshaller instanceof ObjectSetUnmarshaller) { return getObjectSetUnmarshaller(type); } if (unmarshaller instanceof ListUnmarshaller) { return getListUnmarshaller(type); } if (unmarshaller instanceof MapUnmarshaller) { return getMapUnmarshaller(type); } // Inject ourselves to recursively unmarshall things if it's an // ObjectUnmarshaller. if (unmarshaller instanceof ObjectUnmarshaller) { return getObjectUnmarshaller(type); } return unmarshaller; } private ArgumentUnmarshaller getObjectSetUnmarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Set " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 1) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Set " + "type " + type + "; unexpected number of type " + "arguments."); } ArgumentUnmarshaller memberUnmarshaller = getMemberUnmarshaller(args[0]); return new ObjectSetUnmarshaller(memberUnmarshaller); } private ArgumentUnmarshaller getListUnmarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the List " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 1) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the List " + "type " + type + "; unexpected number of type " + "arguments."); } ArgumentUnmarshaller memberUnmarshaller = getMemberUnmarshaller(args[0]); return new ListUnmarshaller(memberUnmarshaller); } private ArgumentUnmarshaller getMapUnmarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 2) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + "; unexpected number of type " + "arguments."); } if (args[0] != String.class) { throw new DynamoDBMappingException( "Only Map is supported."); } ArgumentUnmarshaller memberUnmarshaller = getMemberUnmarshaller(args[1]); return new MapUnmarshaller(memberUnmarshaller); } private ArgumentUnmarshaller getObjectUnmarshaller(Type type) { Type localType = type; if (localType instanceof ParameterizedType) { localType = ((ParameterizedType) type).getRawType(); } if (!(localType instanceof Class)) { throw new DynamoDBMappingException( "Cannot convert " + type + " to a class"); } Class clazz = (Class) localType; if (!StandardAnnotationMaps.of(clazz).has(DynamoDBDocument.class)) { throw new DynamoDBMappingException( "Cannot unmarshall to type " + type + " without a custom marshaler or @DynamoDBDocument " + "annotation."); } return new ObjectUnmarshaller(this, clazz); } private static Object unmarshall( ArgumentUnmarshaller unmarshaller, Method setter, AttributeValue value) { unmarshaller.typeCheck(value, setter); try { return unmarshaller.unmarshall(value); } catch (IllegalArgumentException e) { throw new DynamoDBMappingException( "Couldn't unmarshall value " + value + " for " + setter, e); } catch (ParseException e) { throw new DynamoDBMappingException( "Error attempting to parse date string " + value + " for " + setter, e); } } private static T createObject(Class clazz) { try { return clazz.newInstance(); } catch (InstantiationException e) { throw new DynamoDBMappingException( "Failed to instantiate new instance of class", e); } catch (IllegalAccessException e) { throw new DynamoDBMappingException( "Failed to instantiate new instance of class", e); } } } static interface MarshallerSet { ArgumentMarshaller getMarshaller(Method getter); ArgumentMarshaller getMemberMarshaller(Type memberType); } static interface UnmarshallerSet { ArgumentUnmarshaller getUnmarshaller(Method getter, Method setter); ArgumentUnmarshaller getMemberUnmarshaller(Type memberType); } static final class V2MarshallerSet { private static List> marshallers() { List> list = new ArrayList>(); // Use the new V2 boolean marshallers. addStandardDateMarshallers(list); addV2BooleanMarshallers(list); addStandardNumberMarshallers(list); addStandardStringMarshallers(list); addStandardBinaryMarshallers(list); addStandardS3LinkMarshallers(list); // Add marshallers for the new list and map types. list.add(Pair.of(List.class, CollectionToListMarshaller.instance())); list.add(Pair.of(Map.class, MapToMapMarshaller.instance())); // Make sure I'm last since I'll catch anything. list.add(Pair.of(Object.class, ObjectToMapMarshaller.instance())); return list; } private static List> setMarshallers() { List> list = new ArrayList>(); // No more Set -> NS or Set -> SS marshallers addStandardDateSetMarshallers(list); addStandardNumberSetMarshallers(list); addStandardStringSetMarshallers(list); addStandardBinarySetMarshallers(list); // Make sure I'm last since I'll catch anything. list.add(Pair.of( Object.class, CollectionToListMarshaller.instance())); return list; } } static final class V2CompatibleMarshallerSet { private static List> marshallers() { List> list = new ArrayList>(); // Keep the old v1 boolean marshallers for compatibility. addStandardDateMarshallers(list); addV1BooleanMarshallers(list); addStandardNumberMarshallers(list); addStandardStringMarshallers(list); addStandardBinaryMarshallers(list); addStandardS3LinkMarshallers(list); // Add marshallers for the new list and map types. list.add(Pair.of(List.class, CollectionToListMarshaller.instance())); list.add(Pair.of(Map.class, MapToMapMarshaller.instance())); // Make sure I'm last since I'll catch anything. list.add(Pair.of(Object.class, ObjectToMapMarshaller.instance())); return list; } private static List> setMarshallers() { List> list = new ArrayList>(); addStandardDateSetMarshallers(list); addV1BooleanSetMarshallers(list); addStandardNumberSetMarshallers(list); addStandardStringSetMarshallers(list); addStandardBinarySetMarshallers(list); // If all else fails, fall back to this default marshaler to // retain backwards-compatible behavior. list.add(Pair.of(Object.class, ObjectSetToStringSetMarshaller.instance())); return list; } } static final class V1MarshallerSet { private static List> marshallers() { List> list = new ArrayList>(); addStandardDateMarshallers(list); addV1BooleanMarshallers(list); addStandardNumberMarshallers(list); addStandardStringMarshallers(list); addStandardBinaryMarshallers(list); addStandardS3LinkMarshallers(list); return list; } private static List> setMarshallers() { List> list = new ArrayList>(); addStandardDateSetMarshallers(list); addV1BooleanSetMarshallers(list); addStandardNumberSetMarshallers(list); addStandardStringSetMarshallers(list); addStandardBinarySetMarshallers(list); // If all else fails, fall back to this default marshaler to // retain backwards-compatible behavior. list.add(Pair.of(Object.class, ObjectSetToStringSetMarshaller.instance())); return list; } } private static void addStandardDateMarshallers( List> list) { list.add(Pair.of(Date.class, DateToStringMarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarToStringMarshaller.instance())); } private static void addV1BooleanMarshallers( List> list) { list.add(Pair.of(Boolean.class, BooleanToNumberMarshaller.instance())); list.add(Pair.of(boolean.class, BooleanToNumberMarshaller.instance())); } private static void addV2BooleanMarshallers( List> list) { list.add(Pair.of(Boolean.class, BooleanToBooleanMarshaller.instance())); list.add(Pair.of(boolean.class, BooleanToBooleanMarshaller.instance())); } private static void addStandardNumberMarshallers( List> list) { list.add(Pair.of(Number.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(byte.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(short.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(int.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(long.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(float.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(double.class, NumberToNumberMarshaller.instance())); } private static void addStandardStringMarshallers( List> list) { list.add(Pair.of(String.class, StringToStringMarshaller.instance())); list.add(Pair.of(UUID.class, ObjectToStringMarshaller.instance())); } private static void addStandardBinaryMarshallers( List> list) { list.add(Pair.of(ByteBuffer.class, ByteBufferToBinaryMarshaller.instance())); list.add(Pair.of(byte[].class, ByteArrayToBinaryMarshaller.instance())); } private static void addStandardS3LinkMarshallers( List> list) { list.add(Pair.of(S3Link.class, S3LinkToStringMarshaller.instance())); } private static void addStandardDateSetMarshallers( List> list) { list.add(Pair.of(Date.class, DateSetToStringSetMarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarSetToStringSetMarshaller.instance())); } private static void addStandardNumberSetMarshallers( List> list) { list.add(Pair.of(Number.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(byte.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(short.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(int.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(long.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(float.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(double.class, NumberSetToNumberSetMarshaller.instance())); } private static void addStandardStringSetMarshallers( List> list) { list.add(Pair.of(String.class, StringSetToStringSetMarshaller.instance())); list.add(Pair.of(UUID.class, UUIDSetToStringSetMarshaller.instance())); } private static void addStandardBinarySetMarshallers( List> list) { list.add(Pair.of(ByteBuffer.class, ByteBufferSetToBinarySetMarshaller.instance())); list.add(Pair.of(byte[].class, ByteArraySetToBinarySetMarshaller.instance())); } private static void addV1BooleanSetMarshallers( List> list) { list.add(Pair.of(Boolean.class, BooleanSetToNumberSetMarshaller.instance())); list.add(Pair.of(boolean.class, BooleanSetToNumberSetMarshaller.instance())); } private static class AbstractMarshallerSet implements MarshallerSet { private final List> marshallers; private final List> setMarshallers; public AbstractMarshallerSet( List> marshallers, List> setMarshallers) { this.marshallers = marshallers; this.setMarshallers = setMarshallers; } @Override public ArgumentMarshaller getMarshaller(Method getter) { Class returnType = getter.getReturnType(); if (Set.class.isAssignableFrom(returnType)) { Class memberType = unwrapGenericSetParam(getter.getGenericReturnType()); return getSet(getter, memberType); } else { return getScalar(getter, returnType); } } @Override public ArgumentMarshaller getMemberMarshaller(Type memberType) { Class clazz = ReflectionUtils.resolveClass(memberType); if (Set.class.isAssignableFrom(clazz)) { Class setMemberType = unwrapGenericSetParam(memberType); return getSet(null, setMemberType); } else { return getScalar(null, clazz); } } private ArgumentMarshaller getScalar(Method getter, Class type) { ArgumentMarshaller marshaller = find(type, marshallers); if (marshaller == null) { String className = "?"; String methodName = "?"; if (getter != null) { className = getter.getDeclaringClass().toString(); methodName = getter.getName(); } throw new DynamoDBMappingException( "Cannot marshall return type " + type + " of method " + className + "." + methodName + " without a custom marshaler."); } return marshaller; } private ArgumentMarshaller getSet(Method getter, Class memberType) { ArgumentMarshaller marshaller = find(memberType, setMarshallers); if (marshaller == null) { String className = "?"; String methodName = "?"; if (getter != null) { className = getter.getDeclaringClass().toString(); methodName = getter.getName(); } throw new DynamoDBMappingException( "Cannot marshall return type Set<" + memberType + "> of method " + className + "." + methodName + " without a custom marshaller."); } return marshaller; } } static class StandardUnmarshallerSet implements UnmarshallerSet { private final List> unmarshallers; private final List> setUnmarshallers; public StandardUnmarshallerSet() { this(unmarshallers(), setUnmarshallers()); } public StandardUnmarshallerSet( List> unmarshallers, List> setUnmarshallers) { this.unmarshallers = unmarshallers; this.setUnmarshallers = setUnmarshallers; } private static List> unmarshallers() { List> list = new ArrayList>(); list.add(Pair.of(double.class, DoubleUnmarshaller.instance())); list.add(Pair.of(Double.class, DoubleUnmarshaller.instance())); list.add(Pair.of(BigDecimal.class, BigDecimalUnmarshaller.instance())); list.add(Pair.of(BigInteger.class, BigIntegerUnmarshaller.instance())); list.add(Pair.of(int.class, IntegerUnmarshaller.instance())); list.add(Pair.of(Integer.class, IntegerUnmarshaller.instance())); list.add(Pair.of(float.class, FloatUnmarshaller.instance())); list.add(Pair.of(Float.class, FloatUnmarshaller.instance())); list.add(Pair.of(byte.class, ByteUnmarshaller.instance())); list.add(Pair.of(Byte.class, ByteUnmarshaller.instance())); list.add(Pair.of(long.class, LongUnmarshaller.instance())); list.add(Pair.of(Long.class, LongUnmarshaller.instance())); list.add(Pair.of(short.class, ShortUnmarshaller.instance())); list.add(Pair.of(Short.class, ShortUnmarshaller.instance())); list.add(Pair.of(boolean.class, BooleanUnmarshaller.instance())); list.add(Pair.of(Boolean.class, BooleanUnmarshaller.instance())); list.add(Pair.of(Date.class, DateUnmarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarUnmarshaller.instance())); list.add(Pair.of(ByteBuffer.class, ByteBufferUnmarshaller.instance())); list.add(Pair.of(byte[].class, ByteArrayUnmarshaller.instance())); list.add(Pair.of(S3Link.class, S3LinkUnmarshaller.instance())); list.add(Pair.of(UUID.class, UUIDUnmarshaller.instance())); list.add(Pair.of(String.class, StringUnmarshaller.instance())); list.add(Pair.of(List.class, ListUnmarshaller.instance())); list.add(Pair.of(Map.class, MapUnmarshaller.instance())); // Make sure I'm last since I'll catch all other types. list.add(Pair.of(Object.class, ObjectUnmarshaller.instance())); return list; } private static List> setUnmarshallers() { List> list = new ArrayList>(); list.add(Pair.of(double.class, DoubleSetUnmarshaller.instance())); list.add(Pair.of(Double.class, DoubleSetUnmarshaller.instance())); list.add(Pair.of(BigDecimal.class, BigDecimalSetUnmarshaller.instance())); list.add(Pair.of(BigInteger.class, BigIntegerSetUnmarshaller.instance())); list.add(Pair.of(int.class, IntegerSetUnmarshaller.instance())); list.add(Pair.of(Integer.class, IntegerSetUnmarshaller.instance())); list.add(Pair.of(float.class, FloatSetUnmarshaller.instance())); list.add(Pair.of(Float.class, FloatSetUnmarshaller.instance())); list.add(Pair.of(byte.class, ByteSetUnmarshaller.instance())); list.add(Pair.of(Byte.class, ByteSetUnmarshaller.instance())); list.add(Pair.of(long.class, LongSetUnmarshaller.instance())); list.add(Pair.of(Long.class, LongSetUnmarshaller.instance())); list.add(Pair.of(short.class, ShortSetUnmarshaller.instance())); list.add(Pair.of(Short.class, ShortSetUnmarshaller.instance())); list.add(Pair.of(boolean.class, BooleanSetUnmarshaller.instance())); list.add(Pair.of(Boolean.class, BooleanSetUnmarshaller.instance())); list.add(Pair.of(Date.class, DateSetUnmarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarSetUnmarshaller.instance())); list.add(Pair.of(ByteBuffer.class, ByteBufferSetUnmarshaller.instance())); list.add(Pair.of(byte[].class, ByteArraySetUnmarshaller.instance())); list.add(Pair.of(UUID.class, UUIDSetUnmarshaller.instance())); list.add(Pair.of(String.class, StringSetUnmarshaller.instance())); // Make sure I'm last since I'll catch all other types. list.add(Pair.of(Object.class, ObjectSetUnmarshaller.instance())); return list; } @Override public ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { if (setter.getParameterTypes().length != 1) { throw new DynamoDBMappingException( "Expected exactly one agument to " + setter); } Class paramType = setter.getParameterTypes()[0]; if (Set.class.isAssignableFrom(paramType)) { paramType = unwrapGenericSetParam( setter.getGenericParameterTypes()[0]); return getSet(setter, paramType); } else { return getScalar(setter, paramType); } } @Override public ArgumentUnmarshaller getMemberUnmarshaller(Type memberType) { Class clazz = ReflectionUtils.resolveClass(memberType); if (Set.class.isAssignableFrom(clazz)) { Class setMemberType = unwrapGenericSetParam(memberType); return getSet(null, setMemberType); } else { return getScalar(null, clazz); } } private ArgumentUnmarshaller getSet(Method setter, Class paramType) { ArgumentUnmarshaller unmarshaller = find(paramType, setUnmarshallers); String className = "?"; String methodName = "?"; if (setter != null) { className = setter.getDeclaringClass().toString(); methodName = setter.getName(); } if (unmarshaller == null) { throw new DynamoDBMappingException( "Cannot unmarshall to parameter type Set<" + paramType + "> of method " + className + "." + methodName + " without a custom " + "unmarshaler."); } return unmarshaller; } private ArgumentUnmarshaller getScalar(Method setter, Class type) { ArgumentUnmarshaller unmarshaller = find(type, unmarshallers); String className = "?"; String methodName = "?"; if (setter != null) { className = setter.getDeclaringClass().toString(); methodName = setter.getName(); } if (unmarshaller == null) { throw new DynamoDBMappingException( "Cannot unmarshall to parameter type " + type + "of method " + className + "." + methodName + " without a custom unmarshaler."); } return unmarshaller; } } private static Class unwrapGenericSetParam(Type setType) { if (!(setType instanceof ParameterizedType)) { LOGGER.warn("Set type " + setType + " is not a " + "ParameterizedType, using default marshaler and " + "unmarshaler!"); return Object.class; } ParameterizedType ptype = (ParameterizedType) setType; Type[] arguments = ptype.getActualTypeArguments(); if (arguments.length != 1) { LOGGER.warn("Set type " + setType + " does not have exactly one " + "type argument, using default marshaler and " + "unmarshaler!"); return Object.class; } if (arguments[0].toString().equals("byte[]")) { return byte[].class; } else { return (Class) arguments[0]; } } private static T find(Class needle, List> haystack) { for (Pair pair : haystack) { if (pair.key.isAssignableFrom(needle)) { return pair.value; } } return null; } private static class Pair { public static Pair of ( Class key, ArgumentMarshaller value) { return new Pair(key, value); } public static Pair of ( Class key, ArgumentUnmarshaller value) { return new Pair(key, value); } public final Class key; public final T value; private Pair(Class key, T value) { this.key = key; this.value = value; } } static class AnnotationAwareMarshallerSet implements MarshallerSet { private final MarshallerSet wrapped; public AnnotationAwareMarshallerSet(MarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentMarshaller getMarshaller(Method getter) { final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter); if (annotations.marshalling() != null) { return new CustomMarshaller(annotations.marshalling().marshallerClass()); } else if (annotations.nativeBoolean() != null) { return BooleanToBooleanMarshaller.instance(); } return wrapped.getMarshaller(getter); } @Override public ArgumentMarshaller getMemberMarshaller(Type memberType) { return wrapped.getMemberMarshaller(memberType); } } static class AnnotationAwareUnmarshallerSet implements UnmarshallerSet { private final UnmarshallerSet wrapped; public AnnotationAwareUnmarshallerSet(UnmarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter); if (annotations.marshalling() != null) { return new CustomUnmarshaller(getter.getReturnType(), annotations.marshalling().marshallerClass()); } return wrapped.getUnmarshaller(getter, setter); } @Override public ArgumentUnmarshaller getMemberUnmarshaller(Type c) { return wrapped.getMemberUnmarshaller(c); } } static class CachingMarshallerSet implements MarshallerSet { private final Map cache = new HashMap(); private final Map memberCache = new HashMap(); private final MarshallerSet wrapped; public CachingMarshallerSet(MarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentMarshaller getMarshaller(Method getter) { synchronized (cache) { ArgumentMarshaller marshaler = cache.get(getter); if (marshaler != null) { return marshaler; } marshaler = wrapped.getMarshaller(getter); cache.put(getter, marshaler); return marshaler; } } @Override public ArgumentMarshaller getMemberMarshaller(Type memberType) { synchronized (memberCache) { ArgumentMarshaller marshaller = memberCache.get(memberType); if (marshaller != null) { return marshaller; } marshaller = wrapped.getMemberMarshaller(memberType); memberCache.put(memberType, marshaller); return marshaller; } } } static class CachingUnmarshallerSet implements UnmarshallerSet { private final Map cache = new HashMap(); private final Map memberCache = new HashMap(); private final UnmarshallerSet wrapped; public CachingUnmarshallerSet(UnmarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { synchronized (cache) { ArgumentUnmarshaller unmarshaler = cache.get(getter); if (unmarshaler != null) { return unmarshaler; } unmarshaler = wrapped.getUnmarshaller(getter, setter); cache.put(getter, unmarshaler); return unmarshaler; } } @Override public ArgumentUnmarshaller getMemberUnmarshaller(Type memberType) { synchronized (memberCache) { ArgumentUnmarshaller unmarshaller = memberCache.get(memberType); if (unmarshaller != null) { return unmarshaller; } unmarshaller = wrapped.getMemberUnmarshaller(memberType); memberCache.put(memberType, unmarshaller); return unmarshaller; } } } ConversionSchemas() { throw new UnsupportedOperationException(); } }