blob: 561075ec7d3e92fb9870094cacee867d980aae01 [file]
/*
* Copyright (C) 2012-2015 DataStax Inc.
*
* 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 com.datastax.driver.mapping;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.reflect.TypeToken;
import com.datastax.driver.core.ConsistencyLevel;
/**
* An {@link EntityMapper} implementation that use reflection to read and write fields
* of an entity.
*/
class ReflectionMapper<T> extends EntityMapper<T> {
private static ReflectionFactory factory = new ReflectionFactory();
private ReflectionMapper(Class<T> entityClass, String keyspace, String table, ConsistencyLevel writeConsistency, ConsistencyLevel readConsistency) {
super(entityClass, keyspace, table, writeConsistency, readConsistency);
}
public static Factory factory() {
return factory;
}
@Override
public T newEntity() {
try {
return entityClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Can't create an instance of " + entityClass.getName());
}
}
private static class LiteralMapper<T> extends ColumnMapper<T> {
private final Method readMethod;
private final Method writeMethod;
private LiteralMapper(Field field, int position, PropertyDescriptor pd, AtomicInteger columnCounter) {
super(field, position, columnCounter);
this.readMethod = pd.getReadMethod();
this.writeMethod = pd.getWriteMethod();
}
@Override
public Object getValue(T entity) {
try {
return readMethod.invoke(entity);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Could not get field '" + fieldName + "'");
} catch (Exception e) {
throw new IllegalStateException("Unable to access getter for '" + fieldName + "' in " + entity.getClass().getName(), e);
}
}
@Override
public void setValue(Object entity, Object value) {
try {
writeMethod.invoke(entity, value);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Could not set field '" + fieldName + "' to value '" + value + "'");
} catch (Exception e) {
throw new IllegalStateException("Unable to access setter for '" + fieldName + "' in " + entity.getClass().getName(), e);
}
}
}
private static class EnumMapper<T> extends LiteralMapper<T> {
private final EnumType enumType;
private final Map<String, Object> fromString;
private EnumMapper(Field field, int position, PropertyDescriptor pd, EnumType enumType, AtomicInteger columnCounter) {
super(field, position, pd, columnCounter);
this.enumType = enumType;
if (enumType == EnumType.STRING) {
fromString = new HashMap<String, Object>(fieldType.getRawType().getEnumConstants().length);
for (Object constant : fieldType.getRawType().getEnumConstants())
fromString.put(constant.toString().toLowerCase(), constant);
} else {
fromString = null;
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getValue(T entity) {
Object value = super.getValue(entity);
switch (enumType) {
case STRING:
return (value == null) ? null : value.toString();
case ORDINAL:
return (value == null) ? null : ((Enum)value).ordinal();
}
throw new AssertionError();
}
@Override
public void setValue(Object entity, Object value) {
Object converted = null;
switch (enumType) {
case STRING:
converted = fromString.get(value.toString().toLowerCase());
break;
case ORDINAL:
converted = fieldType.getRawType().getEnumConstants()[(Integer)value];
break;
}
super.setValue(entity, converted);
}
@Override
public TypeToken<Object> getPivotType() {
return enumType.pivotType;
}
}
private static class ReflectionFactory implements Factory {
public <T> EntityMapper<T> create(Class<T> entityClass, String keyspace, String table, ConsistencyLevel writeConsistency, ConsistencyLevel readConsistency) {
return new ReflectionMapper<T>(entityClass, keyspace, table, writeConsistency, readConsistency);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> ColumnMapper<T> createColumnMapper(Class<T> entityClass, Field field, int position, MappingManager mappingManager, AtomicInteger columnCounter) {
String fieldName = field.getName();
try {
PropertyDescriptor pd = new PropertyDescriptor(fieldName, field.getDeclaringClass());
if (field.getType().isEnum()) {
return new EnumMapper<T>(field, position, pd, AnnotationParser.enumType(field), columnCounter);
}
for (Class<?> udt : TypeMappings.findUDTs(field.getGenericType()))
mappingManager.getUDTCodec(udt);
return new LiteralMapper<T>(field, position, pd, columnCounter);
} catch (IntrospectionException e) {
throw new IllegalArgumentException("Cannot find matching getter and setter for field '" + fieldName + "'");
}
}
}
}