/*
* 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.datamodeling.DynamoDBMapperFieldModel.DynamoDBAttributeType;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardAnnotationMaps.TypedMap;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Scalar;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Vector;
import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* Generic type helper.
*/
@SdkInternalApi
final class ConvertibleType<T> {
private final DynamoDBTypeConverter<?,T> typeConverter;
private final DynamoDBAttributeType attributeType;
private final ConvertibleType<T>[] params;
private final Class<T> targetType;
@Deprecated
private final Method getter, setter;
/**
* Constructs a new parameter type.
*/
@SuppressWarnings("unchecked")
private ConvertibleType(Type genericType, TypedMap<T> annotations, Method getter) {
this.typeConverter = annotations.typeConverter();
this.attributeType = annotations.attributeType();
if (typeConverter != null) {
final ConvertibleType<T> target = ConvertibleType.<T>of(typeConverter);
this.targetType = target.targetType;
this.params = target.params;
} else if (genericType instanceof ParameterizedType) {
final Type[] paramTypes = ((ParameterizedType)genericType).getActualTypeArguments();
this.targetType = annotations.targetType();
this.params = new ConvertibleType[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
this.params[i] = ConvertibleType.<T>of(paramTypes[i]);
}
} else {
this.targetType = annotations.targetType();
this.params = new ConvertibleType[0];
}
this.setter = getter == null ? null : StandardBeanProperties.MethodReflect.setterOf(getter);
this.getter = getter;
}
/**
* Gets the target custom type-converter.
*/
final <S> DynamoDBTypeConverter<S,T> typeConverter() {
return (DynamoDBTypeConverter<S,T>)this.typeConverter;
}
/**
* Gets the overriding attribute type.
*/
final DynamoDBAttributeType attributeType() {
return this.attributeType;
}
/**
* Gets the getter method.
*/
@Deprecated
final Method getter() {
return this.getter;
}
/**
* Gets the setter method.
*/
@Deprecated
final Method setter() {
return this.setter;
}
/**
* Gets the scalar parameter types.
*/
final <t> ConvertibleType<t> param(final int index) {
return this.params.length > index ? (ConvertibleType<t>)this.params[index] : null;
}
/**
* Returns true if the types match.
*/
final boolean is(ScalarAttributeType scalarAttributeType, Vector vector) {
return param(0) != null && param(0).is(scalarAttributeType) && is(vector);
}
/**
* Returns true if the types match.
*/
final boolean is(ScalarAttributeType scalarAttributeType) {
return Scalar.of(targetType()).is(scalarAttributeType);
}
/**
* Returns true if the types match.
*/
final boolean is(Scalar scalar) {
return scalar.is(targetType());
}
/**
* Returns true if the types match.
*/
final boolean is(Vector vector) {
return vector.is(targetType());
}
/**
* Returns true if the types match.
*/
final boolean is(Class<?> type) {
return type.isAssignableFrom(targetType());
}
/**
* Gets the raw scalar type.
*/
final Class<T> targetType() {
return this.targetType;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder(targetType().getSimpleName());
if (this.params.length > 0) {
builder.append("<");
for (int i = 0; i < this.params.length; i++) {
builder.append(i == 0 ? "" : ",").append(this.params[i]);
}
builder.append(">");
}
return builder.toString();
}
/**
* Returns the conversion type for the method and annotations.
*/
static <T> ConvertibleType<T> of(Method getter, TypedMap<T> annotations) {
return new ConvertibleType<T>(getter.getGenericReturnType(), annotations, getter);
}
/**
* Returns the conversion type for the converter.
*/
private static <T> ConvertibleType<T> of(final DynamoDBTypeConverter<?,T> converter) {
final Class<?> clazz = converter.getClass();
if (!clazz.isInterface()) {
for (Class<?> c = clazz; Object.class != c; c = c.getSuperclass()) {
for (final Type genericType : c.getGenericInterfaces()) {
final ConvertibleType<T> type = ConvertibleType.<T>of(genericType);
if (type.is(DynamoDBTypeConverter.class)) {
if (type.params.length == 2 && type.param(0).targetType() != Object.class) {
return type.param(0);
}
}
}
}
final ConvertibleType<T> type = ConvertibleType.<T>of(clazz.getGenericSuperclass());
if (type.is(DynamoDBTypeConverter.class)) {
if (type.params.length > 0 && type.param(0).targetType() != Object.class) {
return type.param(0);
}
}
}
throw new DynamoDBMappingException("could not resolve type of " + clazz);
}
/**
* Returns the conversion type for the generic type.
*/
private static <T> ConvertibleType<T> of(Type genericType) {
final Class<T> targetType;
if (genericType instanceof Class) {
targetType = (Class<T>)genericType;
} else if (genericType instanceof ParameterizedType) {
targetType = (Class<T>)((ParameterizedType)genericType).getRawType();
} else if (genericType.toString().equals("byte[]")) {
targetType = (Class<T>)byte[].class;
} else {
targetType = (Class<T>)Object.class;
}
final TypedMap<T> annotations = StandardAnnotationMaps.<T>of(targetType);
return new ConvertibleType<T>(genericType, annotations, null);
}
}