
com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-java-sdk-dynamodb Show documentation
Show all versions of aws-java-sdk-dynamodb Show documentation
The AWS Java SDK for Amazon DynamoDB module holds the client classes that are used for communicating with Amazon DynamoDB Service
/*
* Copyright 2016-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.annotation.SdkInternalApi;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.Id;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.Reflect;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardAnnotationMaps.FieldMap;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardParameterTypes.ParamType;
import com.amazonaws.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Reflection assistant for {@link DynamoDBMapper}
*/
@SdkInternalApi
final class StandardBeanProperties {
/**
* Gets the bean properties for a given class.
* @param clazz The class.
* @return The bean properties.
*/
static final Map> of(final Class clazz) {
return ((Beans)Beans.CACHE).of(clazz);
}
/**
* Cache of {@link Bean} mappings by class type.
*/
static final class Beans {
private static final Beans,?> CACHE = new Beans();
private final ConcurrentMap,Map>> cache = new ConcurrentHashMap,Map>>();
public final Map> of(final Class clazz) {
if (!cache.containsKey(clazz)) {
cache.putIfAbsent(clazz, new Builder(clazz, false).build());
}
return cache.get(clazz);
}
}
/**
* Holds the reflection bean properties for a given property.
*/
static final class Bean implements Reflect {
private final MethodReflect reflect;
private final FieldMap annotations;
private final ParamType type;
private final Id id;
/**
* Constructs an object property mapping for the specified method.
* @param reflect The reflection property.
* @param annotations The annotations.
* @param id The field identifier.
*/
private Bean(final MethodReflect reflect, final FieldMap annotations, final Id id) {
this.type = StandardParameterTypes.of(reflect.getter.getGenericReturnType());
this.annotations = annotations;
this.reflect = reflect;
this.id = id;
}
/**
* Gets the ID.
* @return The ID.
*/
final Id id() {
return this.id;
}
/**
* Gets the annotations.
* @return The annotations.
*/
final FieldMap annotations() {
return this.annotations;
}
/**
* Gets the parameter type.
* @return The parameter type.
*/
final ParamType type() {
return this.type;
}
/**
* Gets the property's value type.
* @return The value type.
*/
final Reflect reflect() {
return this.reflect;
}
/**
* Gets the getter method for this property.
* @return The getter method.
*/
final Method getter() {
return reflect.getter;
}
/**
* Gets the setter method for this property.
* @return The setter method.
*/
final Method setter() {
if (reflect.setter == null) {
throw new DynamoDBMappingException("no access to public/one-argument setter for " + reflect.getter);
}
return reflect.setter;
}
/**
* {@inheritDoc}
*/
@Override
public V get(final T object) {
return reflect.get(object);
}
/**
* {@inheritDoc}
*/
@Override
public void set(final T object, final V value) {
reflect.set(object, value);
}
}
/**
* Get/set reflection operations.
*/
private static class MethodReflect implements Reflect {
private final Method getter, setter;
/**
* Constructs a new method reflection property.
* @param getter The getter method.
*/
private MethodReflect(final Method getter) {
this.setter = declaredSetterOf(getter);
this.getter = getter;
}
/**
* Gets the target type.
* @return The target type.
*/
public final Class targetType() {
return (Class)getter.getReturnType();
}
/**
* {@inheritDoc}
*/
@Override
public V get(final T object) {
try {
return (V)getter.invoke(object);
} catch (final Exception e) {
throw new DynamoDBMappingException("could not invoke " + getter + " on " + object.getClass(), e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void set(T object, final V value) {
try {
setter.invoke(object, value);
} catch (final Exception e) {
throw new DynamoDBMappingException("could not invoke " + setter + " on " + object.getClass(), e);
}
}
}
/**
* Get/set reflection operations with a declaring property.
*/
private static final class DeclaringMethodReflect extends MethodReflect {
private final MethodReflect declaring;
/**
* Constructs a new declaring method reflection property.
* @param getter The getter method.
* @param declaring The declaring reflection property.
*/
private DeclaringMethodReflect(final Method getter, final MethodReflect declaring) {
super(getter);
this.declaring = declaring;
}
/**
* {@inheritDoc}
*/
@Override
public final V get(final T object) {
final T declaringObject = declaring.get(object);
if (declaringObject == null) {
return null;
}
return super.get(declaringObject);
}
/**
* {@inheritDoc}
*/
@Override
public final void set(final T object, final V value) {
T declaringObject = declaring.get(object);
if (declaringObject == null) {
try {
declaringObject = declaring.targetType().newInstance();
} catch (final Exception e) {
throw new DynamoDBMappingException("could not instantiate " + declaring.targetType(), e);
}
declaring.set(object, declaringObject);
}
super.set(declaringObject, value);
}
}
/**
* {@link Bean} properties builder.
*/
static final class Builder {
private final Map> map = new LinkedHashMap>();
private final Class targetType;
private final boolean inherited;
/**
* Constructs a new builder instance.
* @param targetType The object type.
* @param inherited Indicates if we should ignore table/document restrictions.
*/
Builder(final Class targetType, final boolean inherited) {
this.targetType = targetType;
this.inherited = inherited;
}
/**
* Builds the bean properties mapping.
* @return The built bean properties mapping.
*/
public Map> build() {
for (final Method m : this.targetType.getMethods()) {
if (!isGetter(m)) {
continue;
} else if (!inherited && m.getDeclaringClass() != this.targetType) {
if (!StandardAnnotationMaps.of(m.getDeclaringClass()).typed()) {
continue;
}
}
flatten(new MethodReflect(m), null);
}
return Collections.unmodifiableMap(this.map);
}
/**
* Flattens or adds the bean to the mapping.
* @param bean The bean property.
* @param name The attribute name override.
*/
private void flatten(final MethodReflect reflect, String name) {
final FieldMap annotations = StandardAnnotationMaps.of(reflect.getter);
if (annotations.ignore() != null) {
return;
}
final Id id = new Id(this.targetType, name == null ? annotations.attributeName() : name);
if (annotations.flattened() == null) {
if (this.map.put(id.name(), new Bean(reflect, annotations, id)) != null) {
throw new DynamoDBMappingException(id.format("duplicate attribute name"));
}
} else {
final Map attributes = annotations.attributes();
for (final Method m : reflect.targetType().getMethods()) {
if (isGetter(m) && (name = attributes.remove(nameOf(m, null))) != null) {
flatten(new DeclaringMethodReflect(m, (MethodReflect)reflect), name);
}
}
if (!attributes.isEmpty()) { //<- this should be empty by now
throw new DynamoDBMappingException(id.format("contains unknown flattened attribute(s): " + attributes));
}
}
}
}
/**
* Returns true if the method is a valid getter property.
* @param method The getter method.
* @return True if a getter method, false otherwise.
*/
static final boolean isGetter(final Method method) {
if (!method.getName().startsWith("get") && !method.getName().startsWith("is")) {
return false;
} else if (method.getParameterTypes().length != 0) {
return false;
} else if (method.getReturnType() == Void.TYPE) {
return false;
} else if (method.isBridge()) {
return false;
} else if (method.isSynthetic()) {
return false;
} else if (method.getDeclaringClass() == Object.class) {
return false;
} else {
return true;
}
}
/**
* Gets the property name of the specified getter method.
* @param getter The getter method.
* @param prefix An optional prefix to apply (for setter method).
* @return The property name.
*/
static final String nameOf(final Method getter, final String prefix) {
String name = getter.getName();
name = name.substring(name.startsWith("is") ? "is".length() : "get".length());
if (name.length() == 0) {
throw new DynamoDBMappingException("getter must begin with 'get' or 'is', and contain at least one character: " + getter);
} else if (prefix == null) {
return StringUtils.lowerCase(name.substring(0, 1)) + name.substring(1);
} else {
return prefix + name;
}
}
/**
* Gets the declared field from the getter method.
* @param getter The getter method.
* @return The field, or null if none.
*/
static final Field declaredFieldOf(final Method getter) {
final String name = nameOf(getter, null);
try {
return getter.getDeclaringClass().getDeclaredField(name);
} catch (final SecurityException e) {
throw new DynamoDBMappingException("no access to field " + name + " for " + getter, e);
} catch (final NoSuchFieldException no) {}
return null;
}
/**
* Gets the declared setter from the getter method.
* @param getter The getter method.
* @return The setter, or null if none.
*/
static final Method declaredSetterOf(final Method getter) {
final String name = nameOf(getter, "set");
try {
return getter.getDeclaringClass().getMethod(name, getter.getReturnType());
} catch (final Exception no) {}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy