com.datastax.driver.mapping.ReflectionMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-driver-mapping Show documentation
Show all versions of cassandra-driver-mapping Show documentation
Object mapper for the DataStax CQL Java Driver.
/*
* 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.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.UDTValue;
/**
* An {@link EntityMapper} implementation that use reflection to read and write fields
* of an entity.
*/
class ReflectionMapper extends EntityMapper {
private static ReflectionFactory factory = new ReflectionFactory();
private ReflectionMapper(Class 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 extends ColumnMapper {
private final Method readMethod;
private final Method writeMethod;
private LiteralMapper(Field field, int position, PropertyDescriptor pd, AtomicInteger columnNumber) {
this(field, extractSimpleType(field), position, pd, columnNumber);
}
private LiteralMapper(Field field, DataType type, int position, PropertyDescriptor pd, AtomicInteger columnCounter) {
super(field, type, 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 extends LiteralMapper {
private final EnumType enumType;
private final Map fromString;
private EnumMapper(Field field, int position, PropertyDescriptor pd, EnumType enumType, AtomicInteger columnCounter) {
super(field, enumType == EnumType.STRING ? DataType.text() : DataType.cint(), position, pd, columnCounter);
this.enumType = enumType;
if (enumType == EnumType.STRING) {
fromString = new HashMap(javaType.getEnumConstants().length);
for (Object constant : javaType.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 = javaType.getEnumConstants()[(Integer)value];
break;
}
super.setValue(entity, converted);
}
}
private static class UDTColumnMapper extends LiteralMapper {
private final UDTMapper udtMapper;
private UDTColumnMapper(Field field, int position, PropertyDescriptor pd, UDTMapper udtMapper, AtomicInteger columnCounter) {
super(field, udtMapper.getUserType(), position, pd, columnCounter);
this.udtMapper = udtMapper;
}
@Override
public Object getValue(T entity) {
@SuppressWarnings("unchecked")
U udtEntity = (U)super.getValue(entity);
return udtEntity == null ? null : udtMapper.toUDT(udtEntity);
}
@Override
public void setValue(Object entity, Object value) {
assert value instanceof UDTValue;
UDTValue udtValue = (UDTValue)value;
assert udtValue.getType().equals(udtMapper.getUserType());
super.setValue(entity, udtMapper.toEntity((udtValue)));
}
}
private static class NestedUDTMapper extends LiteralMapper {
private final InferredCQLType inferredCQLType;
public NestedUDTMapper(Field field, int position, PropertyDescriptor pd, InferredCQLType inferredCQLType, AtomicInteger columnCounter) {
super(field, inferredCQLType.dataType, position, pd, columnCounter);
this.inferredCQLType = inferredCQLType;
}
@Override
@SuppressWarnings("unchecked")
public Object getValue(T entity) {
Object valueWithEntities = super.getValue(entity);
return (T)UDTMapper.convertEntitiesToUDTs(valueWithEntities, inferredCQLType);
}
@Override
public void setValue(Object entity, Object valueWithUDTValues) {
super.setValue(entity, UDTMapper.convertUDTsToEntities(valueWithUDTValues, inferredCQLType));
}
}
static DataType extractSimpleType(Field f) {
Type type = f.getGenericType();
assert !(type instanceof ParameterizedType);
if (!(type instanceof Class))
throw new IllegalArgumentException(String.format("Cannot map class %s for field %s", type, f.getName()));
return TypeMappings.getSimpleType((Class>)type, f.getName());
}
private static class ReflectionFactory implements Factory {
public EntityMapper create(Class entityClass, String keyspace, String table, ConsistencyLevel writeConsistency, ConsistencyLevel readConsistency) {
return new ReflectionMapper(entityClass, keyspace, table, writeConsistency, readConsistency);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public ColumnMapper createColumnMapper(Class 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(field, position, pd, AnnotationParser.enumType(field), columnCounter);
}
if (TypeMappings.isMappedUDT(field.getType())) {
UDTMapper> udtMapper = mappingManager.getUDTMapper(field.getType());
return (ColumnMapper) new UDTColumnMapper(field, position, pd, udtMapper, columnCounter);
}
if (field.getGenericType() instanceof ParameterizedType) {
InferredCQLType inferredCQLType = InferredCQLType.from(field, mappingManager);
if (inferredCQLType.containsMappedUDT) {
// We need a specialized mapper to convert UDT instances in the hierarchy.
return (ColumnMapper)new NestedUDTMapper(field, position, pd, inferredCQLType, columnCounter);
} else {
// The default codecs will know how to handle the extracted datatype.
return new LiteralMapper(field, inferredCQLType.dataType, position, pd, columnCounter);
}
}
return new LiteralMapper(field, position, pd, columnCounter);
} catch (IntrospectionException e) {
throw new IllegalArgumentException("Cannot find matching getter and setter for field '" + fieldName + "'");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy