com.datastax.driver.mapping.AnnotationParser 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.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.base.Strings;
import com.google.common.reflect.TypeToken;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.TypeCodec;
import com.datastax.driver.core.UserType;
import com.datastax.driver.mapping.MethodMapper.EnumParamMapper;
import com.datastax.driver.mapping.MethodMapper.ParamMapper;
import com.datastax.driver.mapping.annotations.*;
/**
* Static metods that facilitates parsing class annotations into the corresponding {@link EntityMapper}.
*/
class AnnotationParser {
private static final Comparator fieldComparator = new Comparator() {
public int compare(Field f1, Field f2) {
return position(f1) - position(f2);
}
};
private AnnotationParser() {}
public static EntityMapper parseEntity(Class entityClass, EntityMapper.Factory factory, MappingManager mappingManager) {
Table table = AnnotationChecks.getTypeAnnotation(Table.class, entityClass);
String ksName = table.caseSensitiveKeyspace() ? table.keyspace() : table.keyspace().toLowerCase();
String tableName = table.caseSensitiveTable() ? table.name() : table.name().toLowerCase();
ConsistencyLevel writeConsistency = table.writeConsistency().isEmpty() ? null : ConsistencyLevel.valueOf(table.writeConsistency().toUpperCase());
ConsistencyLevel readConsistency = table.readConsistency().isEmpty() ? null : ConsistencyLevel.valueOf(table.readConsistency().toUpperCase());
if (Strings.isNullOrEmpty(table.keyspace())) {
ksName = mappingManager.getSession().getLoggedKeyspace();
if (Strings.isNullOrEmpty(ksName))
throw new IllegalArgumentException(String.format(
"Error creating mapper for class %s, the @%s annotation declares no default keyspace, and the session is not currently logged to any keyspace",
entityClass.getSimpleName(),
Table.class.getSimpleName()
));
}
EntityMapper mapper = factory.create(entityClass, ksName, tableName, writeConsistency, readConsistency);
List pks = new ArrayList();
List ccs = new ArrayList();
List rgs = new ArrayList();
for (Field field : entityClass.getDeclaredFields()) {
if(field.isSynthetic() || (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC)
continue;
if (mappingManager.isCassandraV1 && field.getAnnotation(Computed.class) != null)
throw new UnsupportedOperationException("Computed fields are not supported with native protocol v1");
AnnotationChecks.validateAnnotations(field, "entity",
Column.class, ClusteringColumn.class, Enumerated.class, Frozen.class, FrozenKey.class,
FrozenValue.class, PartitionKey.class, Transient.class, Computed.class);
if (field.getAnnotation(Transient.class) != null)
continue;
switch (kind(field)) {
case PARTITION_KEY:
pks.add(field);
break;
case CLUSTERING_COLUMN:
ccs.add(field);
break;
default:
rgs.add(field);
break;
}
}
AtomicInteger columnCounter = mappingManager.isCassandraV1 ? null : new AtomicInteger(0);
Collections.sort(pks, fieldComparator);
Collections.sort(ccs, fieldComparator);
validateOrder(pks, "@PartitionKey");
validateOrder(ccs, "@ClusteringColumn");
mapper.addColumns(convert(pks, factory, mapper.entityClass, mappingManager, columnCounter),
convert(ccs, factory, mapper.entityClass, mappingManager, columnCounter),
convert(rgs, factory, mapper.entityClass, mappingManager, columnCounter));
return mapper;
}
public static MappedUDTCodec parseUDT(Class udtClass, EntityMapper.Factory factory, MappingManager mappingManager) {
UDT udt = AnnotationChecks.getTypeAnnotation(UDT.class, udtClass);
String ksName = udt.caseSensitiveKeyspace() ? udt.keyspace() : udt.keyspace().toLowerCase();
String udtName = udt.caseSensitiveType() ? udt.name() : udt.name().toLowerCase();
if (Strings.isNullOrEmpty(udt.keyspace())) {
ksName = mappingManager.getSession().getLoggedKeyspace();
if (Strings.isNullOrEmpty(ksName))
throw new IllegalArgumentException(String.format(
"Error creating UDT codec for class %s, the @%s annotation declares no default keyspace, and the session is not currently logged to any keyspace",
udtClass.getSimpleName(),
UDT.class.getSimpleName()
));
}
UserType userType = mappingManager.getSession().getCluster().getMetadata().getKeyspace(ksName).getUserType(udtName);
List columns = new ArrayList();
for (Field field : udtClass.getDeclaredFields()) {
if(field.isSynthetic() || (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC)
continue;
AnnotationChecks.validateAnnotations(field, "UDT",
com.datastax.driver.mapping.annotations.Field.class, Frozen.class, FrozenKey.class,
FrozenValue.class, Enumerated.class, Transient.class);
if (field.getAnnotation(Transient.class) != null)
continue;
switch (kind(field)) {
case PARTITION_KEY:
throw new IllegalArgumentException("Annotation @PartitionKey is not allowed in a class annotated by @UDT");
case CLUSTERING_COLUMN:
throw new IllegalArgumentException("Annotation @ClusteringColumn is not allowed in a class annotated by @UDT");
default:
columns.add(field);
break;
}
}
List> columnMappers = convert(columns, factory, udtClass, mappingManager, null);
return new MappedUDTCodec(userType, udtClass, columnMappers, mappingManager);
}
private static List> convert(List fields, EntityMapper.Factory factory, Class klass, MappingManager mappingManager, AtomicInteger columnCounter) {
List> mappers = new ArrayList>(fields.size());
for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i);
int pos = position(field);
mappers.add(factory.createColumnMapper(klass, field, pos < 0 ? i : pos, mappingManager, columnCounter));
}
return mappers;
}
private static void validateOrder(List fields, String annotation) {
for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i);
int pos = position(field);
if (pos != i)
throw new IllegalArgumentException(String.format("Invalid ordering value %d for annotation %s of column %s, was expecting %d",
pos, annotation, field.getName(), i));
}
}
private static int position(Field field) {
switch (kind(field)) {
case PARTITION_KEY:
return field.getAnnotation(PartitionKey.class).value();
case CLUSTERING_COLUMN:
return field.getAnnotation(ClusteringColumn.class).value();
default:
return -1;
}
}
public static ColumnMapper.Kind kind(Field field) {
PartitionKey pk = field.getAnnotation(PartitionKey.class);
ClusteringColumn cc = field.getAnnotation(ClusteringColumn.class);
Computed comp = field.getAnnotation(Computed.class);
if (pk != null && cc != null)
throw new IllegalArgumentException("Field " + field.getName() + " cannot have both the @PartitionKey and @ClusteringColumn annotations");
if (pk != null) {
return ColumnMapper.Kind.PARTITION_KEY;
}
if (cc != null) {
return ColumnMapper.Kind.CLUSTERING_COLUMN;
}
if (comp != null) {
return ColumnMapper.Kind.COMPUTED;
}
return ColumnMapper.Kind.REGULAR;
}
public static EnumType enumType(Field field) {
Class> type = field.getType();
if (!type.isEnum())
return null;
Enumerated enumerated = field.getAnnotation(Enumerated.class);
return (enumerated == null) ? EnumType.STRING : enumerated.value();
}
public static String columnName(Field field) {
Column column = field.getAnnotation(Column.class);
Computed computedField = field.getAnnotation(Computed.class);
if (column != null && !column.name().isEmpty()) {
if (computedField != null){
throw new IllegalArgumentException("Cannot use @Column and @Computed on the same field");
}
return column.caseSensitive() ? column.name() : column.name().toLowerCase();
}
com.datastax.driver.mapping.annotations.Field udtField = field.getAnnotation(com.datastax.driver.mapping.annotations.Field.class);
if (udtField != null && !udtField.name().isEmpty()) {
return udtField.caseSensitive() ? udtField.name() : udtField.name().toLowerCase();
}
if (computedField != null) {
return computedField.value();
}
return field.getName().toLowerCase();
}
public static String newAlias(Field field, int columnNumber) {
return "col" + columnNumber;
}
public static TypeCodec
© 2015 - 2025 Weber Informatics LLC | Privacy Policy