
com.github.helenusdriver.driver.impl.FieldInfoImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of impl Show documentation
Show all versions of impl Show documentation
JPA-like syntax for annotating POJO classes for persistence via Cassandra's Java driver - Implementation
The newest version!
/*
* Copyright (C) 2015-2015 The Helenus Driver Project Authors.
*
* 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.github.helenusdriver.driver.impl;
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.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.text.WordUtils;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.exceptions.InvalidTypeException;
import com.github.helenusdriver.commons.lang3.reflect.ReflectionUtils;
import com.github.helenusdriver.driver.ColumnPersistenceException;
import com.github.helenusdriver.driver.ObjectConversionException;
import com.github.helenusdriver.driver.info.ClassInfo;
import com.github.helenusdriver.driver.info.FieldInfo;
import com.github.helenusdriver.driver.info.TableInfo;
import com.github.helenusdriver.persistence.ClusteringKey;
import com.github.helenusdriver.persistence.Column;
import com.github.helenusdriver.persistence.DataType;
import com.github.helenusdriver.persistence.Index;
import com.github.helenusdriver.persistence.Mandatory;
import com.github.helenusdriver.persistence.PartitionKey;
import com.github.helenusdriver.persistence.Persisted;
import com.github.helenusdriver.persistence.Persister;
import com.github.helenusdriver.persistence.SuffixKey;
import com.github.helenusdriver.persistence.Table;
import com.github.helenusdriver.persistence.TypeKey;
/**
* The FieldInfo
class caches all the field information needed by
* the class ClassInfo.
*
* @copyright 2015-2015 The Helenus Driver Project Authors
*
* @author The Helenus Driver Project Authors
* @version 1 - Jan 19, 2015 - paouelle, vasu - Creation
*
* @param The type of POJO represented by this field
*
* @since 1.0
*/
@lombok.ToString(exclude={"cinfo", "tinfo"})
public class FieldInfoImpl implements FieldInfo {
/**
* Holds the class for the POJO.
*
* @author vasu
*/
private final Class clazz;
/**
* Holds the class info for the POJO.
*
* @author paouelle
*/
private final ClassInfoImpl cinfo;
/**
* Holds the table information for this field. Can be null
if
* the field is only used as a keyspace suffix.
*
* @author vasu
*/
public final TableInfoImpl tinfo;
/**
* Holds the declaring class for this field
*
* @author vasu
*/
private final Class> declaringClass;
/**
* Holds the reflection field represented by this field info object
*
* @author vasu
*/
private final Field field;
/**
* Holds the name for this field.
*
* @author paouelle
*/
private final String name;
/**
* Holds the type for this field.
*
* @author paouelle
*/
private final Class> type;
/**
* This variable is used to cache Column annotation
*
* @author vasu
*/
private final Column column;
/**
* Holds the persisted annotation for this field. If null
then
* there is no persister required and standard mapping to Cassandra data
* types should be used.
*
* @author paouelle
*/
private final Persisted persisted;
/**
* Holds the persister to use to encode/decode the value in this field
* (or the elements of the collection). If null
then there is
* no persister required and standard mapping to Cassandra data types should
* be used.
*
* @author paouelle
*/
private final Persister, ?> persister;
/**
* Suffix annotation for this field if any.
*
* @author vasu
*/
private final SuffixKey suffix;
/**
* Flag indicating if the field is mandatory (i.e. cannot be null
).
*
* @author paouelle
*/
private final boolean mandatory;
/**
* Index annotation for the field if any..
*
* @author paouelle
*/
private final Index index;
/**
* PartitionKey annotation for the field if any.
*
* @author paouelle
*/
private final PartitionKey partitionKey;
/**
* ClusteringKey annotation for the field if any.
*
* @author paouelle
*/
private final ClusteringKey clusteringKey;
/**
* TypeKey annotation for the field if any.
*
* @author paouelle
*/
private final TypeKey typeKey;
/**
* Element type of the set when this field is a multi-key.
*
* @author paouelle
*/
private final Class> multiKeyType;
/**
* Holds the data type definition for this field (if it is a column).
*
* @author paouelle
*/
private final DataTypeImpl.Definition definition;
/**
* Holds the data decoder for this field (if it is a column).
*
* @author paouelle
*/
private final DataDecoder> decoder;
/**
* Flag indicating if the field is final.
*
* @author paouelle
*/
private final boolean isFinal;
/**
* Holds the final value for the field if defined final.
*
* @author paouelle
*/
private final Object finalValue;
/**
* Holds the getter method to retrieve the value of this field from an
* instance.
*
* @author vasu
*/
private final Method getter;
/**
* Holds the setter method to update the value for this field from an
* instance.
*
* @author vasu
*/
private final Method setter;
/**
* Flag indicating if this is the last key in the partition or the cluster.
*
* @author paouelle
*/
private volatile boolean isLast = false;
/**
* Instantiates a new FieldInfo
object for a root element pojo
* class.
*
* @author paouelle
*
* @param cinfo the non-null
class info for the POJO root element
* @param tinfo the non-null
table info from the POJO root element
* @param field the non-null
field to copy
*/
FieldInfoImpl(
RootClassInfoImpl cinfo,
TableInfoImpl tinfo,
FieldInfoImpl extends T> field
) {
this.clazz = cinfo.getObjectClass();
this.cinfo = cinfo;
this.tinfo = tinfo;
this.declaringClass = field.declaringClass;
this.field = field.field;
this.name = field.name;
this.type = field.type;
this.column = field.column;
this.persisted = field.persisted;
this.persister = field.persister;
this.suffix = field.suffix;
this.mandatory = field.mandatory;
this.index = field.index;
this.partitionKey = field.partitionKey;
this.clusteringKey = field.clusteringKey;
this.typeKey = field.typeKey;
this.multiKeyType = field.multiKeyType;
this.definition = field.definition;
this.decoder = field.decoder;
this.isFinal = field.isFinal;
this.finalValue = field.finalValue;
this.getter = field.getter;
this.setter = field.setter;
this.isLast = field.isLast;
}
/**
* Instantiates a new FieldInfo
object not part of a defined
* table.
*
* @author vasu
*
* @param cinfo the non-null
class info for the POJO
* @param field the non-null
field to create an info object for
* @throws IllegalArgumentException if unable to find a getter or setter
* method for the field of if improperly annotated
*/
FieldInfoImpl(ClassInfoImpl cinfo, Field field) {
this.clazz = cinfo.getObjectClass();
this.cinfo = cinfo;
this.tinfo = null;
this.declaringClass = field.getDeclaringClass();
this.field = field;
field.setAccessible(true); // make it accessible in case we need to
this.isFinal = Modifier.isFinal(field.getModifiers());
this.name = field.getName();
this.type = field.getType();
this.column = null;
this.persisted = null;
this.persister = null;
this.suffix = field.getAnnotation(SuffixKey.class);
this.mandatory = true; // keyspace suffixes are mandatory fields
this.index = null; // we don't care about this for keyspace suffixes
this.partitionKey = null; // we don't care about this for keyspace suffixes
this.clusteringKey = null; // we don't care about this for keyspace suffixes
this.typeKey = null; // we don't care about this for keyspace suffixes
this.multiKeyType = null; // we don't care about this for keyspace suffixes
this.definition = null; // we don't care about this for keyspace suffixes
this.decoder = null; // we don't care about this for keyspace suffixes
this.getter = findGetterMethod(declaringClass);
this.setter = findSetterMethod(declaringClass);
this.finalValue = findFinalValue();
}
/**
* Instantiates a new FieldInfo
object as a column part of a
* defined table.
*
* @author vasu
*
* @param tinfo the table info for the field
* @param field the non-null
field to create an info object for
* @throws IllegalArgumentException if unable to find a getter or setter
* method for the field of if improperly annotated
*/
FieldInfoImpl(TableInfoImpl tinfo, Field field) {
this.clazz = tinfo.getObjectClass();
this.cinfo = (ClassInfoImpl)tinfo.getClassInfo();
this.tinfo = tinfo;
this.declaringClass = field.getDeclaringClass();
this.field = field;
field.setAccessible(true); // make it accessible in case we need to
this.isFinal = Modifier.isFinal(field.getModifiers());
this.name = field.getName();
this.type = field.getType();
this.persisted = field.getAnnotation(Persisted.class);
if (persisted != null) {
org.apache.commons.lang3.Validate.isTrue(
(persisted.as() != DataType.INFERRED) && !persisted.as().isCollection(),
"@Persisted annotation cannot be of type '%s': %s.%s",
persisted.as(),
declaringClass.getName(),
field.getName()
);
this.persister = newPersister();
} else {
this.persister = null;
}
this.suffix = field.getAnnotation(SuffixKey.class);
this.mandatory = field.getAnnotation(Mandatory.class) != null;
final Map columns
= ReflectionUtils.getAnnotationsByType(String.class, Column.class, field);
final Map indexes
= ReflectionUtils.getAnnotationsByType(String.class, Index.class, field);
final Map partitionKeys
= ReflectionUtils.getAnnotationsByType(String.class, PartitionKey.class, field);
final Map clusteringKeys
= ReflectionUtils.getAnnotationsByType(String.class, ClusteringKey.class, field);
final Map typeKeys
= ReflectionUtils.getAnnotationsByType(String.class, TypeKey.class, field);
org.apache.commons.lang3.Validate.isTrue(
!(!indexes.isEmpty() && columns.isEmpty()),
"field must be annotated with @Column if it is annotated with @Index: %s.%s",
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(!partitionKeys.isEmpty() && columns.isEmpty()),
"field must be annotated with @Column if it is annotated with @PartitionKey: %s.%s",
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(!clusteringKeys.isEmpty() && columns.isEmpty()),
"field must be annotated with @Column if it is annotated with @ClusteringKey: %s.%s",
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(!typeKeys.isEmpty() && columns.isEmpty()),
"field must be annotated with @Column if it is annotated with @TypeKey: %s.%s",
declaringClass.getName(),
field.getName()
);
// Note: while searching for the matching table, uses the name from the
// table annotation instead of the one returned by getName() as the later
// might have been cleaned and hence would not match what was defined in
// the POJO
final String tname = tinfo.getTable().name();
Column column = columns.get(tname);
Index index = indexes.get(tname);
PartitionKey partitionKey = partitionKeys.get(tname);
ClusteringKey clusteringKey = clusteringKeys.get(tname);
TypeKey typeKey = typeKeys.get(tname);
if (column == null) { // fallback to special Table.ALL name
column = columns.get(Table.ALL);
}
this.column = column;
if (index == null) { // fallback to special Table.ALL name
index = indexes.get(Table.ALL);
}
this.index = index;
if (partitionKey == null) { // fallback to special Table.ALL name
partitionKey = partitionKeys.get(Table.ALL);
}
this.partitionKey = partitionKey;
if (clusteringKey == null) { // fallback to special Table.ALL name
clusteringKey = clusteringKeys.get(Table.ALL);
}
this.clusteringKey = clusteringKey;
if (typeKey == null) { // fallback to special Table.ALL name
typeKey = typeKeys.get(Table.ALL);
}
this.typeKey = typeKey;
if (isColumn()) {
this.definition = DataTypeImpl.inferDataTypeFrom(field);
this.decoder = definition.getDecoder(
field, isMandatory() || isPartitionKey() || isClusteringKey()
);
if (((clusteringKey != null) || (partitionKey != null))
&& (definition.getType() == DataType.SET)) {
final Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
final ParameterizedType ptype = (ParameterizedType)type;
this.multiKeyType = ReflectionUtils.getRawClass(
ptype.getActualTypeArguments()[0]
); // sets will always have 1 argument
} else {
throw new IllegalArgumentException(
"unable to determine the element type of multi-field in table '"
+ tname
+ "': "
+ declaringClass.getName()
+ "."
+ field.getName()
);
}
} else {
this.multiKeyType = null;
}
} else {
this.definition = null;
this.decoder = null;
this.multiKeyType = null;
}
this.getter = findGetterMethod(declaringClass);
this.setter = findSetterMethod(declaringClass);
this.finalValue = findFinalValue();
// validate some stuff
org.apache.commons.lang3.Validate.isTrue(
!(isIndex() && !isColumn()),
"field in table '%s' must be annotated with @Column if it is annotated with @Index: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(isPartitionKey() && isClusteringKey()),
"field in table '%s' must not be annotated with @ClusteringKey if it is annotated with @PartitionKey: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(isPartitionKey() && !isColumn()),
"field in table '%s' must be annotated with @Column if it is annotated with @PartitionKey: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(isClusteringKey() && !isColumn()),
"field in table '%s' must be annotated with @Column if it is annotated with @ClusteringKey: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(isTypeKey() && !isColumn()),
"field in table '%s' must be annotated with @Column if it is annotated with @TypeKey: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(isTypeKey() && !String.class.equals(getType())),
"field in table '%s' must be a String if it is annotated with @TypeKey: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(isTypeKey() && isFinal()),
"field in table '%s' must not be final if it is annotated with @TypeKey: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
org.apache.commons.lang3.Validate.isTrue(
!(isTypeKey()
&& !(cinfo instanceof RootClassInfoImpl)
&& !(cinfo instanceof TypeClassInfoImpl)),
"field in table '%s' must not be annotated with @TypeKey if class is annotated with @Entity: %s.%s",
tname,
declaringClass.getName(),
field.getName()
);
if (isColumn() && definition.isCollection()) {
org.apache.commons.lang3.Validate.isTrue(
!((isClusteringKey() || isPartitionKey()) && (multiKeyType == null)),
"field in table '%s' cannot be '%s' if it is annotated with @ClusteringKey or @PartitionKey: %s.%s",
tname,
definition,
declaringClass.getName(),
field.getName()
);
}
}
/**
* Instantiates a new persister object based on the @Persisted annotation.
*
* @author paouelle
*
* @return a non-null
persister object corresponding to the
* @Persisted annotation
* @throws IllegalArgumentException if unable to instantiate a persister or
* the persister is not compatible with the @Persisted annotation
*/
private Persister, ?> newPersister() {
final Persister, ?> persister;
if (persisted.arguments().length == 0) { // use default ctor
try {
persister = persisted.using().newInstance();
} catch (IllegalAccessException|InstantiationException e) {
throw new IllegalArgumentException(
"unable to instantiate persister: " + persisted.using().getName(), e
);
}
} else { // use a String[] ctor
try {
persister = persisted.using().getConstructor(String[].class).newInstance(
(Object)persisted.arguments()
);
} catch (NoSuchMethodException|IllegalAccessException|InstantiationException e) {
throw new IllegalArgumentException(
"unable to instantiate persister: "
+ persisted.using().getName()
+ ", using arguments: "
+ Arrays.toString(persisted.arguments()), e
);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(
"unable to instantiate persister: "
+ persisted.using().getName()
+ ", using arguments: "
+ Arrays.toString(persisted.arguments()), e.getTargetException()
);
}
}
org.apache.commons.lang3.Validate.isTrue(
persister.getDecodedClass() != null,
"@Persisted annotation's persister must be defined with a decoded class: %s",
persisted.using().getName()
);
org.apache.commons.lang3.Validate.isTrue(
persister.getPersistedClass() == persisted.as().CLASS,
"@Persisted annotation's persister must be defined with persisted class '%s': %s",
persisted.as().CLASS.getName(),
persisted.using().getName()
);
return persister;
}
/**
* Finds the getter method from the declaring class suitable to get a
* reference to the field.
*
* @author paouelle
*
* @param declaringClass the non-null
class declaring the field
* @return the getter method for the field or null
if none found
* @throws IllegalArgumentException if unable to find a suitable getter
*/
private Method findGetterMethod(Class> declaringClass) {
Method getter = findGetterMethod(declaringClass, "get");
if ((getter == null) && (type == Boolean.class) || (type == Boolean.TYPE)) {
// try for "is"
getter = findGetterMethod(declaringClass, "is");
}
return getter;
}
/**
* Finds the getter method from the declaring class suitable to get a
* reference to the field.
*
* @author paouelle
*
* @param declaringClass the non-null
class declaring the field
* @param prefix the non-null
getter prefix to use
* @return the getter method for the field or null
if none found
* @throws IllegalArgumentException if unable to find a suitable getter
*/
private Method findGetterMethod(Class> declaringClass, String prefix) {
final String mname = prefix + WordUtils.capitalize(name, '_', '-');
try {
final Method m = declaringClass.getDeclaredMethod(mname);
final int mods = m.getModifiers();
if (Modifier.isAbstract(mods) || Modifier.isStatic(mods)) {
return null;
}
final Class> wtype = ClassUtils.primitiveToWrapper(type);
final Class> wrtype = ClassUtils.primitiveToWrapper(m.getReturnType());
org.apache.commons.lang3.Validate.isTrue(
wtype.isAssignableFrom(wrtype),
"expecting getter for field '%s' with return type: %s",
field,
type.getName()
);
m.setAccessible(true);
return m;
} catch (NoSuchMethodException e) {
return null;
}
}
/**
* Finds the setter method from the declaring class suitable to set a
* value for the field.
*
* @author paouelle
*
* @param declaringClass the non-null
class declaring the field
* @return the setter method for the field or null
if none found
* @throws IllegalArgumentException if unable to find a suitable setter
*/
private Method findSetterMethod(Class> declaringClass) {
final String mname = "set" + WordUtils.capitalize(name, '_', '-');
try {
final Method m = declaringClass.getDeclaredMethod(mname, type);
final int mods = m.getModifiers();
if (Modifier.isAbstract(mods) || Modifier.isStatic(mods)) {
return null;
}
org.apache.commons.lang3.Validate.isTrue(
m.getParameterCount() == 1,
"expecting setter for field '%s' with one parameter",
field
);
final Class> wtype = ClassUtils.primitiveToWrapper(type);
final Class> wptype = ClassUtils.primitiveToWrapper(m.getParameterTypes()[0]);
org.apache.commons.lang3.Validate.isTrue(
wtype.isAssignableFrom(wptype),
"expecting setter for field '%s' with parameter type: %s",
field,
type.getName()
);
m.setAccessible(true);
return m;
} catch (NoSuchMethodException e) {
return null;
}
}
/**
* If the field is defined as final then finds and encode its default value.
*
* @author paouelle
*
* @return the encoded default value for this field or null
if the
* field is not defined as final
* @throws IllegalArgumentException if the field is final and we are unable to
* instantiate a dummy version of the pojo or access the field's final
* value or again we failed to encode it
* encode its default value
*/
private Object findFinalValue() {
if (isFinal) {
Object val;
try {
val = cinfo.getDefaultValue(field);
} catch (IllegalArgumentException e) {
// final field was not introspected by class info (declared class
// must not be annotated with @Entity)
// instantiates a dummy version and access its value
try {
// find default ctor even if private
final Constructor ctor = clazz.getDeclaredConstructor();
ctor.setAccessible(true); // in case it was private
final T t = ctor.newInstance();
val = field.get(t);
} catch (NoSuchMethodException|IllegalAccessException|InstantiationException ee) {
throw new IllegalArgumentException(
"unable to instantiate object: " + clazz.getName(), ee
);
} catch (InvocationTargetException ee) {
final Throwable t = ee.getTargetException();
if (t instanceof Error) {
throw (Error)t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else { // we don't expect any of those
throw new IllegalArgumentException(
"unable to instantiate object: " + clazz.getName(), t
);
}
}
}
if (persister != null) { // must encode it using the persister
final String fname = declaringClass.getName() + "." + name;
try {
val = definition.encode(val, persisted, persister, fname);
} catch (IOException e) {
throw new IllegalArgumentException(
"failed to encode final field '"
+ fname
+ "' to "
+ persisted.as().CQL
+ "' with persister: "
+ persister.getClass().getName(),
e
);
}
}
return val;
}
return null;
}
/**
* Marks this field as being the last key in the partition or the cluster.
*
* @author paouelle
*/
void setLast() {
this.isLast = true;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getObjectClass()
*/
@Override
public Class getObjectClass() {
return clazz;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getDeclaringClass()
*/
@Override
public Class> getDeclaringClass() {
return declaringClass;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getClassInfo()
*/
@Override
public ClassInfo getClassInfo() {
return cinfo;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getTableInfo()
*/
@Override
public TableInfo getTableInfo() {
return tinfo;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getName()
*/
@Override
public String getName() {
return name;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getType()
*/
@Override
public Class> getType() {
return type;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isColumn()
*/
@Override
public boolean isColumn() {
return column != null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getColumnName()
*/
@Override
public String getColumnName() {
return (column != null) ? column.name() : null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getSuffixKeyName()
*/
@Override
public String getSuffixKeyName() {
return (suffix != null) ? suffix.name() : null;
}
/**
* Gets the column data type for this field.
*
* @author paouelle
*
* @return the column data type for this field if it is annotated as a
* column; null
otherwise
*/
public DataTypeImpl.Definition getDataType() {
return definition;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isSuffixKey()
*/
@Override
public boolean isSuffixKey() {
return suffix != null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getSuffixKey()
*/
@Override
public SuffixKey getSuffixKey() {
return suffix;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isMandatory()
*/
@Override
public boolean isMandatory() {
return mandatory;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isIndex()
*/
@Override
public boolean isIndex() {
return index != null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getIndex()
*/
@Override
public Index getIndex() {
return index;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isCounter()
*/
@Override
public boolean isCounter() {
return (definition != null) ? definition.getType() == DataType.COUNTER : false;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isLast()
*/
@Override
public boolean isLast() {
return isLast;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isPartitionKey()
*/
@Override
public boolean isPartitionKey() {
return partitionKey != null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getPartitionKey()
*/
@Override
public PartitionKey getPartitionKey() {
return partitionKey;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isClusteringKey()
*/
@Override
public boolean isClusteringKey() {
return clusteringKey != null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getClusteringKey()
*/
@Override
public ClusteringKey getClusteringKey() {
return clusteringKey;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isTypeKey()
*/
@Override
public boolean isTypeKey() {
return typeKey != null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getClusteringKey()
*/
@Override
public TypeKey getTypeKey() {
return typeKey;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isMultiKey()
*/
@Override
public boolean isMultiKey() {
return (multiKeyType != null);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isPersisted()
*/
@Override
public boolean isPersisted() {
return (persister != null);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#getAnnotation(java.lang.Class)
*/
@Override
public A getAnnotation(Class annotationClass) {
return field.getAnnotation(annotationClass);
}
/**
* Validate the provided value for this field.
*
* @author paouelle
*
* @param value the value to be validated
* @throws IllegalArgumentException if the specified value is not of the
* right type or is null
when the field is mandatory
*/
public void validateValue(Object value) {
if (value == null) {
org.apache.commons.lang3.Validate.isTrue(
!isMandatory(),
"invalid null value for mandatory column '%s'",
getColumnName()
);
org.apache.commons.lang3.Validate.isTrue(
!isPartitionKey() && !isClusteringKey(),
"invalid null value for primary key column '%s'",
getColumnName()
);
org.apache.commons.lang3.Validate.isTrue(
!isTypeKey(),
"invalid null value for type key column '%s'",
getColumnName()
);
}
if (isColumn()) {
if (value != null) {
if (!((definition.getType() == DataType.BLOB) && !isPersisted() // persisted columns will be serialized later
? byte[].class : type).isInstance(value)) {
if (isMultiKey()) {
// in such case, the value can also be an element of the set
if (!multiKeyType.isInstance(value)) {
throw new IllegalArgumentException(
"invalid value for column '"
+ getColumnName()
+ "'; expecting class '"
+ multiKeyType.getName()
+ "' or '"
+ type.getName()
+ "' but found '"
+ value.getClass().getName()
+ "'"
);
}
} else {
throw new IllegalArgumentException(
"invalid value for column '"
+ getColumnName()
+ "'; expecting class '"
+ type.getName()
+ "' but found '"
+ value.getClass().getName()
+ "'"
);
}
}
}
} else {
org.apache.commons.lang3.Validate.isTrue(
type.isInstance(value),
"invalid value for suffix '%s'; expecting class '%s' but found '%s'",
this.getSuffixKey().name(),
type.getName(),
(value != null) ? value.getClass().getName() : "null"
);
}
}
/**
* Validate the provided element value for this collection field.
*
* @author paouelle
*
* @param value the element value to be validated
* @param type the collection data type of the column to validate
* @throws IllegalArgumentException if the specified value is not of the
* right type or is null
when the field is mandatory
*/
public void validateCollectionValue(DataType type, Object value) {
if (persister != null) { // will be persisted anyway so no need to check
return;
}
final DataType dtype = definition.getType();
org.apache.commons.lang3.Validate.isTrue(
dtype.isCollection(),
"column '%s' is not a collection",
getColumnName()
);
org.apache.commons.lang3.Validate.isTrue(
type == dtype,
"column '%s' is not a %s",
getColumnName(),
type.name().toLowerCase()
);
if (value == null) {
org.apache.commons.lang3.Validate.isTrue(
!isPartitionKey() && !isClusteringKey(),
"invalid null element value for primary key column '%s'",
getColumnName()
);
org.apache.commons.lang3.Validate.isTrue(
!isTypeKey(),
"invalid null element value for type key column '%s'",
getColumnName()
);
}
final DataType etype = definition.getElementType();
org.apache.commons.lang3.Validate.isTrue(
DataTypeImpl.isInstance(etype, value),
"invalid element value for column '%s'; expecting type '%s': %s",
getColumnName(),
etype.name().toLowerCase(),
value
);
}
/**
* Validate the provided mapping key/value for this map field.
*
* @author paouelle
*
* @param key the mapping key to be validated
* @param value the mapping value to be validated
* @throws IllegalArgumentException if the specified key/value are not
* of the right mapping types or the value is null
* when the column is mandatory
*/
public void validateMapKeyValue(Object key, Object value) {
validateCollectionValue(DataType.MAP, value);
final DataType ktype = definition.getArgumentTypes().get(0); // #0 is the data type for the key
org.apache.commons.lang3.Validate.isTrue(
DataTypeImpl.isInstance(ktype, key),
"invalid element key for column '%s'; expecting type '%s': %s",
getColumnName(),
ktype.name().toLowerCase(),
key
);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.info.FieldInfo#isFinal()
*/
@Override
public boolean isFinal() {
return isFinal;
}
/**
* Gets the final value for the field if it is defined as final.
*
* @author paouelle
*
* @return the final value for the field if defined as final; null
* otherwise
*/
public Object getFinalValue() {
return finalValue;
}
/**
* Retrieves the field's value from the specified POJO.
*
* @author paouelle
*
* @param object the POJO from which to retrieve the field's value
* @return the POJO's field value
* @throws NullPointerException if object
is null
* @throws ColumnPersistenceException if unable to persist the field's value
*/
public Object getValue(T object) {
return getValue(Object.class, object);
}
/**
* Retrieves the non-encoded field's value from the specified POJO.
*
* @author paouelle
*
* @param object the POJO from which to retrieve the field's value
* @return the POJO's field non-encoded value
* @throws NullPointerException if object
is null
*/
public Object getNonEncodedValue(T object) {
return getNonEncodedValue(Object.class, object);
}
/**
* Retrieves the field's value from the specified POJO.
*
* @author paouelle
*
* @param clazz the class for the expected value
* @param object the POJO from which to retrieve the field's value
* @return the POJO's field value
* @throws NullPointerException if object
is null
* @throws ClassCastException if the field value from the given object
* cannot be type casted to the specified class
* @throws ColumnPersistenceException if unable to persist the field's value
*/
public Object getValue(Class> clazz, T object) {
return encodeValue(getNonEncodedValue(clazz, object));
}
/**
* Retrieves the field's non-encoded value from the specified POJO.
*
* @author paouelle
*
* @param clazz the class for the expected value
* @param object the POJO from which to retrieve the field's value
* @return the POJO's field non-encoded value
* @throws NullPointerException if object
is null
* @throws ClassCastException if the field value from the given object
* cannot be type casted to the specified class
*/
public Object getNonEncodedValue(Class> clazz, T object) {
org.apache.commons.lang3.Validate.notNull(object, "invalid null object");
Object val;
try {
if (getter != null) {
val = clazz.cast(getter.invoke(object));
} else { // get it from field directly
val = clazz.cast(field.get(object));
}
} catch (IllegalAccessException e) { // should not happen
throw new IllegalStateException(declaringClass.getName(), e);
} catch (InvocationTargetException e) {
final Throwable t = e.getTargetException();
if (t instanceof Error) {
throw (Error)t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else { // we don't expect any of those
throw new IllegalStateException(declaringClass.getName(), t);
}
}
if (isTypeKey()) { // this is the type key
final String type;
// the value is fixed by the schema so get it from the type class info
if (cinfo instanceof TypeClassInfoImpl) {
type = ((TypeClassInfoImpl)cinfo).getType();
} else { // must be a RootClassInfoImpl
type = ((RootClassInfoImpl)cinfo).getType(cinfo.getObjectClass()).getType();
}
if (!type.equals(val)) { // force value to the type and re-update in pojo
setValue(object, type);
val = clazz.cast(type);
}
}
return val;
}
/**
* Encodes the specified value based on any configured persister.
*
* @author paouelle
*
* @param val the value to be encoded
* @return the corresponding encoded value or val
if no encoding
* was required
* @throws ColumnPersistenceException if unable to persist the field's value
*/
public Object encodeValue(Object val) {
if (persister != null) { // must encode it using the persister
final String fname = declaringClass.getName() + "." + name;
try {
val = definition.encode(val, persisted, persister, fname);
} catch (IOException e) {
throw new ColumnPersistenceException(
declaringClass,
name,
"failed to encode field '"
+ fname
+ "' to "
+ persisted.as().CQL
+ " with persister: "
+ persister.getClass().getName(),
e
);
}
}
return val;
}
/**
* Encodes the specified value as an element based on any configured persister.
*
* @author paouelle
*
* @param val the value to be encoded
* @return the corresponding encoded value or val
if no encoding
* was required
* @throws ColumnPersistenceException if unable to persist the field's value
*/
public Object encodeElementValue(Object val) {
if (persister != null) { // must encode it using the persister
final String fname = declaringClass.getName() + "." + name;
try {
val = definition.encodeElement(val, persisted, persister, fname);
} catch (IOException e) {
throw new ColumnPersistenceException(
declaringClass,
name,
"failed to encode field '"
+ fname
+ "' to "
+ persisted.as().CQL
+ " with persister: "
+ persister.getClass().getName(),
e
);
}
}
return val;
}
/**
* Sets the field's value in the specified POJO with the given value.
*
* @author paouelle
*
* @param object the POJO in which to set the field's value
* @param value the value to set the field with
* @throws NullPointerException if object
is null
* or if the column is a primary key or mandatory and
* value
is null
*/
public void setValue(T object, Object value) {
org.apache.commons.lang3.Validate.notNull(object, "invalid null object");
// nothing to update if field was declared final and no setter provided
// this is in case a field defined as a collection is final but a setter
// method is provided to replace the content of the collection instead
// of the whole field
if (isFinal && (setter == null)) {
return;
}
if (value == null) {
org.apache.commons.lang3.Validate.isTrue(
!isMandatory(),
"invalid null value for mandatory column '%s'",
getColumnName()
);
org.apache.commons.lang3.Validate.isTrue(
!isPartitionKey() && !isClusteringKey(),
"invalid null value for primary key column '%s'",
getColumnName()
);
org.apache.commons.lang3.Validate.isTrue(
!isTypeKey(),
"invalid null value for type key column '%s'",
getColumnName()
);
}
try {
if (setter != null) {
setter.invoke(object, value);
} else { // set it in field directly
field.set(object, value);
}
} catch (IllegalAccessException e) { // should not happen
throw new IllegalStateException(e);
} catch (InvocationTargetException e) {
final Throwable t = e.getTargetException();
if (t instanceof Error) {
throw (Error)t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else { // we don't expect any of those
throw new IllegalStateException(t);
}
}
}
/**
* Decodes the field's value based on the given row.
*
* @author paouelle
*
* @param row the row where the column encoded value is defined
* @return the decoded value for this field from the given row
* @throws NullPointerException if row
is null
* @throws ObjectConversionException if unable to decode the column or if the
* column is not defined in the given row
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public Object decodeValue(Row row) {
org.apache.commons.lang3.Validate.notNull(row, "invalid null row");
// check if the column is defined in the row
if (!row.getColumnDefinitions().contains(getColumnName())) {
if (isPartitionKey()) {
throw new ObjectConversionException(
clazz,
row,
"missing partition key '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
if (isClusteringKey()) {
throw new ObjectConversionException(
clazz,
row,
"missing clustering key '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
if (isTypeKey()) {
throw new ObjectConversionException(
clazz,
row,
"missing type key '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
if (isMandatory()) {
throw new ObjectConversionException(
clazz,
row,
"missing mandatory column '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
throw new ObjectConversionException(
clazz,
row,
"missing column '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
Object val;
try {
// if we have a persister then we need to decode to persisted.as() first
val = decoder.decode(
row,
getColumnName(),
(persister != null) ? (Class)persisted.as().CLASS : (Class)this.type
);
} catch (IllegalArgumentException|InvalidTypeException e) {
throw new ObjectConversionException(
clazz,
row,
"unable to decode value for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'",
e
);
}
if (persister != null) { // must decode it using the persister
final String fname = declaringClass.getName() + "." + name;
try {
val = definition.decode(val, persisted, persister, fname);
} catch (Exception e) {
throw new ObjectConversionException(
clazz,
row,
"unable to decode persisted "
+ persisted.as().CQL
+ " for field '"
+ fname
+ "' with persister: "
+ persister.getClass().getName(),
e
);
}
}
return val;
}
/**
* Decodes and sets the field's value in the specified POJO based on the given
* row.
*
* @author paouelle
*
* @param object the POJO in which to set the field's decoded value
* @param row the row where the column encoded value is defined
* @throws NullPointerException if object
or row
is
* null
* @throws ObjectConversionException if unable to decode the column and store
* the corresponding value into the POJO object or if the column is
* a primary key, type key, or mandatory and not defined in the given
* row
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public void decodeAndSetValue(T object, Row row) {
org.apache.commons.lang3.Validate.notNull(object, "invalid null object");
org.apache.commons.lang3.Validate.notNull(row, "invalid null row");
// check if the column is defined in the row
if (!row.getColumnDefinitions().contains(getColumnName())) {
if (isPartitionKey()) {
throw new ObjectConversionException(
clazz,
row,
"missing partition key '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
if (isClusteringKey()) {
throw new ObjectConversionException(
clazz,
row,
"missing clustering key '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
if (isTypeKey()) {
throw new ObjectConversionException(
clazz,
row,
"missing type key '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
if (isMandatory()) {
throw new ObjectConversionException(
clazz,
row,
"missing mandatory column '"
+ getColumnName()
+ "' from result set for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'"
);
}
// not defined in the row so skip it
return;
}
Object val;
try {
// if we have a persister then we need to decode to persisted.as() first
val = decoder.decode(
row,
getColumnName(),
(persister != null) ? (Class)persisted.as().CLASS : (Class)this.type
);
} catch (IllegalArgumentException|InvalidTypeException e) {
throw new ObjectConversionException(
clazz,
row,
"unable to decode value for field '"
+ declaringClass.getName()
+ "."
+ name
+ "'",
e
);
}
if (persister != null) { // must decode it using the persister
final String fname = declaringClass.getName() + "." + name;
try {
val = definition.decode(val, persisted, persister, fname);
} catch (Exception e) {
throw new ObjectConversionException(
clazz,
row,
"unable to decode persisted "
+ persisted.as().CQL
+ " for field '"
+ fname
+ "' with persister: "
+ persister.getClass().getName(),
e
);
}
}
try {
setValue(object, val);
} catch (NullPointerException|IllegalArgumentException e) {
throw new ObjectConversionException(
clazz,
row,
"unable to set field '"
+ declaringClass.getName()
+ "."
+ name
+ "' with: "
+ val,
e
);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy