Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.oneandone.troilus.BeanMapper Maven / Gradle / Ivy
/*
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* 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://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 net.oneandone.troilus;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
/**
* bean mapper
*
*/
class BeanMapper {
private final LoadingCache, PropertiesMapper> propertiesMapperCache = CacheBuilder.newBuilder()
.build(new PropertiesMapperLoader());
private static final class PropertiesMapper {
private final Class> clazz;
private final ImmutableMap propertyWriters;
private final ImmutableMap propertyReaders;
public PropertiesMapper(ImmutableMap propertyReaders, ImmutableMap propertyWriters, Class> clazz) {
this.propertyReaders = propertyReaders;
this.propertyWriters = propertyWriters;
this.clazz = clazz;
}
public ImmutableMap> toValues(Object entity, ImmutableSet namesToMap) {
Map> values = Maps.newHashMap();
for (Entry entry : propertyReaders.entrySet()) {
if (namesToMap.isEmpty() || namesToMap.contains(entry.getKey())) {
Map.Entry> pair = entry.getValue().readProperty(entity);
values.put(pair.getKey(), pair.getValue());
}
}
return ImmutableMap.copyOf(values);
}
@SuppressWarnings("unchecked")
public T fromValues(PropertiesSource datasource, ImmutableSet namesToMap) {
try {
T bean = newInstance((Constructor) clazz.getDeclaredConstructor());
for (Entry entry : propertyWriters.entrySet()) {
if (namesToMap.isEmpty() || namesToMap.contains(entry.getKey())) {
entry.getValue().writeProperty(bean, datasource);
}
}
return bean;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private T newInstance(final Constructor constructor) {
try {
return (T) constructor.newInstance();
} catch (ReflectiveOperationException e) {
AccessController.doPrivileged(new SetConstructorAccessible<>(constructor));
try {
return (T) constructor.newInstance();
} catch (ReflectiveOperationException e2) {
throw new RuntimeException(e);
}
}
}
}
private static final class SetConstructorAccessible implements PrivilegedAction {
private final Constructor constructor;
public SetConstructorAccessible(Constructor constructor) {
this.constructor = constructor;
}
@Override
public Object run() {
constructor.setAccessible(true);
return null;
}
}
private static final class SetFieldAccessible implements PrivilegedAction {
private final Field field;
public SetFieldAccessible(Field field) {
this.field = field;
}
@Override
public Object run() {
field.setAccessible(true);
return null;
}
}
/**
* @param entity the entity to map
* @param namesToMap the properties names to consider
* @return the extracted name-value pairs
*/
public ImmutableMap> toValues(Object entity, ImmutableSet namesToMap) {
return getPropertiesMapper(entity.getClass()).toValues(entity, namesToMap);
}
/**
* @param clazz the object type
* @param datasource the data source to fetch the property values
* @param propertyNames the property names to be considered
* @return the object instance
*/
public T fromValues(Class> clazz, PropertiesSource datasource, ImmutableSet propertyNames) {
return getPropertiesMapper(clazz).fromValues(datasource, propertyNames);
}
private PropertiesMapper getPropertiesMapper(Class> clazz) {
try {
return propertiesMapperCache.get(clazz);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
private static final class PropertiesMapperLoader extends CacheLoader, PropertiesMapper> {
@Override
public PropertiesMapper load(Class> clazz) throws Exception {
// readers
Map propertyReaders = Maps.newHashMap();
propertyReaders.putAll(fetchJEEFieldReaders(ImmutableSet.copyOf(clazz.getFields())));
propertyReaders.putAll(fetchJEEFieldReaders(ImmutableSet.copyOf(clazz.getDeclaredFields())));
propertyReaders.putAll(fetchCassandraMapperFieldReaders(ImmutableSet.copyOf(clazz.getFields())));
propertyReaders.putAll(fetchCassandraMapperFieldReaders(ImmutableSet.copyOf(clazz.getDeclaredFields())));
propertyReaders.putAll(fetchFieldReaders(ImmutableSet.copyOf(clazz.getFields())));
propertyReaders.putAll(fetchFieldReaders(ImmutableSet.copyOf(clazz.getDeclaredFields())));
// writers
Map propertyWriters = Maps.newHashMap();
propertyWriters.putAll(fetchJEEFieldWriters(ImmutableSet.copyOf(clazz.getFields())));
propertyWriters.putAll(fetchJEEFieldWriters(ImmutableSet.copyOf(clazz.getDeclaredFields())));
propertyWriters.putAll(fetchCassandraMapperFieldWriters(ImmutableSet.copyOf(clazz.getFields())));
propertyWriters.putAll(fetchCassandraMapperFieldWriters(ImmutableSet.copyOf(clazz.getDeclaredFields())));
propertyWriters.putAll(fetchFieldWriters(ImmutableSet.copyOf(clazz.getFields())));
propertyWriters.putAll(fetchFieldWriters(ImmutableSet.copyOf(clazz.getDeclaredFields())));
return new PropertiesMapper(ImmutableMap.copyOf(propertyReaders), ImmutableMap.copyOf(propertyWriters), clazz);
}
private static ImmutableMap fetchFieldReaders(ImmutableSet beanFields) {
Map propertyReaders = Maps.newHashMap();
for (Field beanField : beanFields) {
final net.oneandone.troilus.Field field = beanField.getAnnotation(net.oneandone.troilus.Field.class);
if (field != null) {
propertyReaders.put(field.name(), new PropertyReader(field.name(), beanField));
}
}
return ImmutableMap.copyOf(propertyReaders);
}
private static ImmutableMap fetchJEEFieldReaders(ImmutableSet beanFields) {
Map propertyReaders = Maps.newHashMap();
for (Field beanField : beanFields) {
for (Annotation annotation : beanField.getAnnotations()) {
if (annotation.annotationType().getName().equals("javax.persistence.Column")) {
for (Method attributeMethod : annotation.annotationType().getDeclaredMethods()) {
if (attributeMethod.getName().equalsIgnoreCase("name")) {
try {
final String columnName = (String) attributeMethod.invoke(annotation);
if (columnName != null) {
propertyReaders.put(columnName, new PropertyReader(columnName, beanField));
}
break;
} catch (ReflectiveOperationException ignore) { }
}
}
}
}
}
return ImmutableMap.copyOf(propertyReaders);
}
private static ImmutableMap fetchCassandraMapperFieldReaders(ImmutableSet beanFields) {
Map propertyReaders = Maps.newHashMap();
for (Field beanField : beanFields) {
for (Annotation annotation : beanField.getAnnotations()) {
if (annotation.annotationType().getName().equals("com.datastax.driver.mapping.annotations.Field")) {
for (Method attributeMethod : annotation.annotationType().getDeclaredMethods()) {
if (attributeMethod.getName().equalsIgnoreCase("name")) {
try {
final String columnName = (String) attributeMethod.invoke(annotation);
if (columnName != null) {
propertyReaders.put(columnName, new PropertyReader(columnName, beanField));
}
break;
} catch (ReflectiveOperationException ignore) { }
}
}
}
}
}
return ImmutableMap.copyOf(propertyReaders);
}
private Map fetchFieldWriters(ImmutableSet beanFields) {
Map propertyWriters = Maps.newHashMap();
for (Field beanField : beanFields) {
final net.oneandone.troilus.Field field = beanField.getAnnotation(net.oneandone.troilus.Field.class);
if (field != null) {
propertyWriters.put(field.name(), new PropertyWriter(field.name(), beanField));
}
}
return ImmutableMap.copyOf(propertyWriters);
}
private Map fetchJEEFieldWriters(ImmutableSet beanFields) {
Map propertyWriters = Maps.newHashMap();
for (Field beanField : beanFields) {
for (Annotation annotation : beanField.getAnnotations()) {
if (annotation.annotationType().getName().equals("javax.persistence.Column")) {
for (Method attributeMethod : annotation.annotationType().getDeclaredMethods()) {
if (attributeMethod.getName().equalsIgnoreCase("name")) {
try {
String columnName = (String) attributeMethod.invoke(annotation);
propertyWriters.put(columnName, new PropertyWriter(columnName, beanField));
} catch (ReflectiveOperationException ignore) { }
}
}
}
}
}
return ImmutableMap.copyOf(propertyWriters);
}
private Map fetchCassandraMapperFieldWriters(ImmutableSet beanFields) {
Map propertyWriters = Maps.newHashMap();
for (Field beanField : beanFields) {
for (Annotation annotation : beanField.getAnnotations()) {
if (annotation.annotationType().getName().equals("com.datastax.driver.mapping.annotations.Field")) {
for (Method attributeMethod : annotation.annotationType().getDeclaredMethods()) {
if (attributeMethod.getName().equalsIgnoreCase("name")) {
try {
String columnName = (String) attributeMethod.invoke(annotation);
propertyWriters.put(columnName, new PropertyWriter(columnName, beanField));
} catch (ReflectiveOperationException ignore) { }
}
}
}
}
}
return ImmutableMap.copyOf(propertyWriters);
}
}
private static class PropertyReader {
private final String fieldName;
private final java.lang.reflect.Field field;
private final OptionalWrapper optionalWrapper;
public PropertyReader(String fieldName, java.lang.reflect.Field field) {
this.fieldName = fieldName;
this.field = field;
AccessController.doPrivileged(new SetFieldAccessible(field));
if (Optional.class.isAssignableFrom(field.getType())) {
this.optionalWrapper = new GuavaOptionalWrapper();
} else if (field.getType().getName().equals("java.util.Optional")) {
getActualTypeArgument(field.getType(), 0);
this.optionalWrapper = new JavaOptionalWrapper();
} else {
this.optionalWrapper = new NonOptionalWrapper();
}
}
public Entry> readProperty(Object bean) {
Object value = null;
try {
value = field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) { }
return Maps.immutableEntry(fieldName, optionalWrapper.wrap(value));
}
private static interface OptionalWrapper {
Optional wrap(Object obj);
}
private static final class NonOptionalWrapper implements OptionalWrapper {
public Optional wrap(Object obj) {
return Optional.fromNullable(obj);
}
}
private static final class GuavaOptionalWrapper implements OptionalWrapper {
@SuppressWarnings("unchecked")
public Optional wrap(Object obj) {
if (obj == null) {
return Optional.absent();
} else {
return (Optional) obj;
}
}
}
private static final class JavaOptionalWrapper implements OptionalWrapper {
private final Method meth;
public JavaOptionalWrapper() {
try {
meth = Class.forName("java.util.Optional").getMethod("get");
} catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public Optional wrap(Object obj) {
try {
Object o = meth.invoke(obj);
return Optional.fromNullable(o);
} catch (InvocationTargetException | IllegalAccessException | SecurityException e) {
return Optional.absent();
}
}
}
}
private static Type getActualTypeArgument(Type type, int argIndex) {
if (type instanceof ParameterizedType) {
ParameterizedType paramizedType = (ParameterizedType) type;
Type[] types = paramizedType.getActualTypeArguments();
if ((types != null) && (types.length > argIndex)) {
return types[argIndex];
}
}
return Object.class;
}
private static class PropertyWriter {
private final String fieldName;
private final java.lang.reflect.Field field;
private final OptionalWrapper optionalWrapper;
private Class> javaOptionalClass;
public PropertyWriter(String fieldName, java.lang.reflect.Field field) {
this.fieldName = fieldName;
this.field = field;
AccessController.doPrivileged(new SetFieldAccessible(field));
if (Optional.class.isAssignableFrom(field.getType())) {
this.optionalWrapper = new GuavaOptionalWrapper();
} else if (field.getType().getName().equals("java.util.Optional")) {
this.optionalWrapper = new JavaOptionalWrapper();
} else {
this.optionalWrapper = new NonOptionalWrapper();
}
Class> cl = null;
try {
cl = Class.forName("java.util.Optional");
} catch (ClassNotFoundException | RuntimeException e) { }
javaOptionalClass = cl;
}
void writeProperty(Object bean, PropertiesSource datasource) {
Optional optionalValue = readValue(field.getType(), datasource);
if (optionalValue == null) {
return;
}
try {
field.set(bean, optionalWrapper.unwrap(optionalValue));
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Optional readValue(Class> clazz, PropertiesSource datasource) {
Optional value = Optional.absent();
Type type = field.getGenericType();
if (isOptional(clazz)) {
type = getActualTypeArgument(type, 0);
}
if (ImmutableSet.class.isAssignableFrom(clazz)) {
value = datasource.read(fieldName, (Class) getActualTypeArgument(type, 0));
if (value.isPresent()) {
return Optional.of(ImmutableSet.copyOf((Collection) value.get()));
}
} else if (ImmutableList.class.isAssignableFrom(clazz)) {
value = datasource.read(fieldName, (Class) getActualTypeArgument(type, 0));
if (value.isPresent()) {
return Optional.of(ImmutableList.copyOf((Collection) value.get()));
}
} else if (ImmutableMap.class.isAssignableFrom(clazz)) {
value = datasource.read(fieldName, (Class) getActualTypeArgument(type, 0), (Class) getActualTypeArgument(field.getGenericType(), 1));
if (value.isPresent()) {
return Optional.of(ImmutableMap.copyOf((Map) value.get()));
}
} else {
value = datasource.read(fieldName, (Class) type);
}
return value;
}
private boolean isOptional(Class> clazz) {
return Optional.class.isAssignableFrom(clazz) || ((javaOptionalClass != null) && (javaOptionalClass.isAssignableFrom(clazz)));
}
private static interface OptionalWrapper {
Object unwrap(Optional obj);
}
private static final class NonOptionalWrapper implements OptionalWrapper {
public Object unwrap(Optional obj) {
return obj.orNull();
}
}
private static final class GuavaOptionalWrapper implements OptionalWrapper {
public Object unwrap(Optional obj) {
return Optional.fromNullable(emptyToNull(obj.orNull()));
}
}
private static Object emptyToNull(Object obj) {
if (obj == null) {
return null;
}
if (List.class.isAssignableFrom(obj.getClass())) {
if (((List>) obj).isEmpty()) {
obj = null;
}
} else if (Set.class.isAssignableFrom(obj.getClass())) {
if (((Set>) obj).isEmpty()) {
obj = null;
}
} else if (Map.class.isAssignableFrom(obj.getClass())) {
if (((Map, ?>) obj).isEmpty()) {
obj = null;
}
} else if (byte[].class.isAssignableFrom(obj.getClass())) {
if (((byte[]) obj).length == 0) {
obj = null;
}
}
return obj;
}
private static final class JavaOptionalWrapper implements OptionalWrapper {
private final Method meth;
public JavaOptionalWrapper() {
try {
meth = Class.forName("java.util.Optional").getDeclaredMethod("ofNullable", Object.class);
} catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public Object unwrap(Optional obj) {
try {
Object o = emptyToNull(obj.orNull());
return meth.invoke(null, o);
} catch (InvocationTargetException | IllegalAccessException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
}
}