/*
* Ensure that this is not both a @Table(@Persistent) and a @PrimaryKey
*/
if (isTable && isPrimaryKeyClass) {
exceptions.add(new MappingException("Entity cannot be of type Table and PrimaryKey"));
throw exceptions;
}
/*
* Ensure that this is either a @Table(@Persistent) or a @PrimaryKey
*/
if (!isTable && !isPrimaryKeyClass) {
exceptions.add(new MappingException(
"Cassandra entities must have the @Table, @Persistent or @PrimaryKeyClass Annotation"));
throw exceptions;
}
/*
* Parse the properties
*/
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
if (p.isIdProperty()) {
idProperties.add(p);
} else if (p.isCompositePrimaryKey()) {
compositePrimaryKeys.add(p);
} else if (p.isPartitionKeyColumn()) {
partitionKeyColumns.add(p);
primaryKeyColumns.add(p);
} else if (p.isClusterKeyColumn()) {
clusterKeyColumns.add(p);
primaryKeyColumns.add(p);
}
}
});
final int idPropertyCount = idProperties.size();
final int partitionKeyColumnCount = partitionKeyColumns.size();
final int primaryKeyColumnCount = primaryKeyColumns.size();
/*
* Perform rules verification on PrimaryKeyClass
*/
if (isPrimaryKeyClass) {
/*
* Must have at least 1 attribute annotated with @PrimaryKeyColumn
*/
if (primaryKeyColumnCount == 0) {
exceptions.add(new MappingException(String.format(
"composite primary key type [%s] has no fields annotated with @%s", entity.getType().getName(),
PrimaryKeyColumn.class.getSimpleName())));
}
/*
* At least one of the PrimaryKeyColumns must have a type PARTIONED
*/
if (partitionKeyColumnCount == 0) {
exceptions.add(new MappingException(
"At least one of the @PrimaryKeyColumn annotation must have a type of PARTITIONED"));
}
/*
* Cannot have any Id or PrimaryKey Annotations
*/
if (idPropertyCount > 0) {
exceptions.add(new MappingException(
"Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass"));
}
/*
* Ensure that PrimaryKeyColumn is a supported Type.
*/
for (CassandraPersistentProperty p : primaryKeyColumns) {
if (CassandraSimpleTypeHolder.getDataTypeFor(p.getType()) == null) {
exceptions.add(new MappingException("Fields annotated with @PrimaryKeyColumn must be simple CassandraTypes"));
}
}
/*
* Ensure PrimaryKeyClass is Serializable
*/
if (!Serializable.class.isAssignableFrom(thisType)) {
exceptions.add(new MappingException("@PrimaryKeyClass must be Serializable"));
}
/*
* Ensure PrimaryKeyClass only extends Object
*/
if (!thisType.getSuperclass().equals(Object.class)) {
exceptions.add(new MappingException("@PrimaryKeyClass must only extend Object"));
}
/*
* Check that PrimaryKeyClass overrides "boolean equals(Object)"
*/
try {
Method equalsMethod = thisType.getDeclaredMethod("equals", Object.class);
if (equalsMethod == null || !equalsMethod.getDeclaringClass().equals(thisType)) {
throw new NoSuchMethodException();
}
} catch (NoSuchMethodException e) {
String message = "@PrimaryKeyClass should override 'boolean equals(Object)' method and use all @PrimaryKeyColumn fields";
if (strict) {
exceptions.add(new MappingException(message, e));
} else {
log.warn(message);
}
}
/*
* Ensure PrimaryKeyClass overrides "int hashCode()"
*/
try {
Method hashCodeMethod = thisType.getDeclaredMethod("hashCode", (Class<?>[]) null);
if (hashCodeMethod == null || !hashCodeMethod.getDeclaringClass().equals(thisType)) {
throw new NoSuchMethodException();
}
} catch (NoSuchMethodException e) {
String message = "@PrimaryKeyClass should override 'int hashCode()' method and use all @PrimaryKeyColumn fields";
if (strict) {
exceptions.add(new MappingException(message, e));
} else {
log.warn(message);
}
}
}
/*
* Perform rules verification on Table/Persistent
*/
if (isTable) {
/*
* TODO Verify annotation values with CqlIndentifier
*/
/*
* Ensure only one PK or at least one partitioned PKC and not both PK(s) & PKC(s)
*/
if (primaryKeyColumnCount == 0) {
/*
* Can only have one PK.
*/
if (idPropertyCount != 1) {
exceptions
.add(new MappingException(String.format(
"@Table/@Persistent types must have only one @PrimaryKey attribute, if any. Found %s.",
idPropertyCount)));
throw exceptions;
}
/*
* Ensure that Id is a supported Type. At the point there is only 1.
*/
Class<?> typeClass = idProperties.get(0).getType();
if (!typeClass.isAnnotationPresent(PrimaryKeyClass.class)
&& CassandraSimpleTypeHolder.getDataTypeFor(typeClass) == null) {
exceptions.add(new MappingException(
"Fields annotated with @PrimaryKey must be simple CassandraTypes or @PrimaryKeyClass type"));
}
} else if (idPropertyCount > 0) {
/*
* Then we have both PK(s) & PKC(s)
*/
exceptions
.add(new MappingException(
String
.format(
"@Table/@Persistent types must not define both @PrimaryKeyColumn field%s (found %s) and @PrimaryKey field%s (found %s)",
primaryKeyColumnCount == 1 ? "" : "s", primaryKeyColumnCount, idPropertyCount == 1 ? "" : "s",
idPropertyCount)));
throw exceptions;
} else {
/*
* We have no PKs & only PKC(s) -- ensure at least one is of type PARTITIONED
*/
if (partitionKeyColumnCount == 0) {
exceptions.add(new MappingException(String
.format("@Table/@Persistent types must define at least one @PrimaryKeyColumn of type PARTITIONED")));
}
}
}