/* * 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 com.datastax.driver.core.TypeCodec; import com.datastax.driver.mapping.annotations.*; import com.google.common.reflect.TypeToken; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * Default implementation of {@link MappedProperty}. */ class DefaultMappedProperty<T> implements MappedProperty<T> { static <T> DefaultMappedProperty<T> create(Class<?> mappedClass, String propertyName, String mappedName, Field field, Method getter, Method setter, Map<Class<? extends Annotation>, Annotation> annotations) { @SuppressWarnings("unchecked") TypeToken<T> propertyType = (TypeToken<T>) inferType(field, getter); boolean partitionKey = annotations.containsKey(PartitionKey.class); boolean clusteringColumn = annotations.containsKey(ClusteringColumn.class); boolean computed = annotations.containsKey(Computed.class); int position = inferPosition(annotations); @SuppressWarnings("unchecked") Class<? extends TypeCodec<T>> codecClass = (Class<? extends TypeCodec<T>>) getCustomCodecClass(annotations); return new DefaultMappedProperty<T>( mappedClass, propertyName, mappedName, propertyType, partitionKey, clusteringColumn, computed, position, codecClass, field, getter, setter); } private final Class<?> mappedClass; private final String propertyName; private final TypeToken<T> propertyType; private final String mappedName; private final boolean partitionKey; private final boolean clusteringColumn; private final boolean computed; private final int position; private final TypeCodec<T> customCodec; private final Field field; private final Method getter; private final Method setter; private DefaultMappedProperty( Class<?> mappedClass, String propertyName, String mappedName, TypeToken<T> propertyType, boolean partitionKey, boolean clusteringColumn, boolean computed, int position, Class<? extends TypeCodec<T>> codecClass, Field field, Method getter, Method setter) { checkArgument(propertyName != null && !propertyName.isEmpty()); checkArgument(mappedName != null && !mappedName.isEmpty()); checkNotNull(propertyType); this.mappedClass = mappedClass; this.propertyName = propertyName; this.mappedName = mappedName; this.propertyType = propertyType; this.partitionKey = partitionKey; this.clusteringColumn = clusteringColumn; this.computed = computed; this.position = position; this.customCodec = codecClass == null || codecClass.equals(Defaults.NoCodec.class) ? null : ReflectionUtils.newInstance(codecClass); this.field = field; this.getter = getter; this.setter = setter; } @Override public String getPropertyName() { return propertyName; } @Override public TypeToken<T> getPropertyType() { return propertyType; } @Override public String getMappedName() { return mappedName; } @Override public int getPosition() { return position; } @Override public TypeCodec<T> getCustomCodec() { return customCodec; } @Override public boolean isComputed() { return computed; } @Override public boolean isPartitionKey() { return partitionKey; } @Override public boolean isClusteringColumn() { return clusteringColumn; } @SuppressWarnings("unchecked") @Override public T getValue(Object entity) { try { // try getter first, if available, otherwise direct field access if (getter != null && getter.isAccessible()) return (T) getter.invoke(entity); else return (T) checkNotNull(field).get(entity); } catch (Exception e) { throw new IllegalArgumentException("Unable to read property '" + getPropertyName() + "' in " + entity.getClass(), e); } } @Override public void setValue(Object entity, T value) { try { // try setter first, if available, otherwise direct field access if (setter != null && setter.isAccessible()) setter.invoke(entity, value); else checkNotNull(field).set(entity, value); } catch (Exception e) { throw new IllegalArgumentException("Unable to write property '" + getPropertyName() + "' in " + entity.getClass(), e); } } @Override public String toString() { return mappedClass.getSimpleName() + "." + getPropertyName(); } private static TypeToken<?> inferType(Field field, Method getter) { if (getter != null) return TypeToken.of(getter.getGenericReturnType()); else return TypeToken.of(checkNotNull(field).getGenericType()); } private static int inferPosition(Map<Class<? extends Annotation>, Annotation> annotations) { if (annotations.containsKey(PartitionKey.class)) { return ((PartitionKey) annotations.get(PartitionKey.class)).value(); } if (annotations.containsKey(ClusteringColumn.class)) { return ((ClusteringColumn) annotations.get(ClusteringColumn.class)).value(); } return -1; } private static Class<? extends TypeCodec<?>> getCustomCodecClass(Map<Class<? extends Annotation>, Annotation> annotations) { Column column = (Column) annotations.get(Column.class); if (column != null) return column.codec(); com.datastax.driver.mapping.annotations.Field udtField = (com.datastax.driver.mapping.annotations.Field) annotations.get(com.datastax.driver.mapping.annotations.Field.class); if (udtField != null) return udtField.codec(); return null; } }