package com.txtr.hibernatedelta.validator;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.NotFound;
import com.txtr.hibernatedelta.model.HibernateModelUtil;
import com.txtr.hibernatedelta.model.OracleKeywords;
import com.txtr.hibernatedelta.model.VirtualRootTable;
public class HibernateValidator {
public static final int DISCRIMINATOR_LENGTH = 128;
static void validateTable(Class<?> entity) {
Table table = entity.getAnnotation(Table.class);
DiscriminatorValue discriminatorValue = entity.getAnnotation(DiscriminatorValue.class);
Inheritance inheritance = entity.getAnnotation(Inheritance.class);
DiscriminatorColumn discriminatorColumn = entity.getAnnotation(DiscriminatorColumn.class);
if (discriminatorColumn != null) {
if (discriminatorColumn.discriminatorType() != DiscriminatorType.STRING) {
throw new IllegalStateException("DiscriminatorColumn.discriminatorType must 'DiscriminatorType.STRING': " + entity);
}
if (discriminatorColumn.length() != DISCRIMINATOR_LENGTH) {
throw new IllegalStateException("DiscriminatorColumn.length must be 128: " + entity);
}
}
if (HibernateModelUtil.isTableRoot(entity)) {
validateRootTable(entity, table, discriminatorValue, inheritance, discriminatorColumn);
} else {
validateNonRootTable(entity, table, discriminatorValue, inheritance, discriminatorColumn);
}
if (table != null){
checkTableName(entity, table);
}
}
private static void checkTableName(Class<?> entity, Table table) {
final String tableName = table.name();
final String entityName = entity.getSimpleName();
if (entityName.length() <= 28){
if (tableName.toLowerCase().endsWith("table")){
if (! StringUtils.substringBefore(tableName.toLowerCase(), "table").equalsIgnoreCase(entityName)){
throw new IllegalStateException("table name " + tableName + " of entity " + entity + " doesn't match");
}
} else {
if (! tableName.equalsIgnoreCase(entityName)){
throw new IllegalStateException("table name " + tableName + " of entity " + entity + " doesn't match");
}
}
}
//else: too long table names can't be checked algorithmically
}
private static void validateNonRootTable(Class<?> entity, Table table, DiscriminatorValue discriminatorValue, Inheritance inheritance, DiscriminatorColumn discriminatorColumn) {
if (inheritance != null) {
throw new IllegalStateException("inheritance must be null: " + entity);
}
if (discriminatorColumn != null) {
throw new IllegalStateException("DiscriminatorColumn must be null: " + entity);
}
InheritanceType type = HibernateModelUtil.getInheritance(entity);
checkDiscriminatorValue(entity, discriminatorValue, type);
if (type == InheritanceType.SINGLE_TABLE) {
if (table != null) {
throw new IllegalStateException("table must be null: " + entity);
}
} else {
if (table == null) {
throw new IllegalStateException("table annotation missing: " + entity);
}
}
}
private static void validateRootTable(Class<?> entity, Table table, DiscriminatorValue discriminatorValue, Inheritance inheritance, DiscriminatorColumn discriminatorColumn) {
if (table == null && entity.getAnnotation(VirtualRootTable.class) == null) {
throw new IllegalStateException("table annotation missing: " + entity);
}
if (inheritance != null) {
if (inheritance.strategy() == InheritanceType.SINGLE_TABLE) {
if (discriminatorColumn == null) {
throw new IllegalStateException("DiscriminatorColumn must not be null: " + entity);
}
} else {
if (discriminatorColumn != null) {
throw new IllegalStateException("DiscriminatorColumn must be null: " + entity);
}
}
checkDiscriminatorValue(entity, discriminatorValue, inheritance.strategy());
}
}
private static void checkDiscriminatorValue(Class<?> entity, DiscriminatorValue discriminatorValue, InheritanceType type) {
if (type == InheritanceType.SINGLE_TABLE && !isAbstractClass(entity)) {
if (discriminatorValue == null) {
throw new IllegalStateException("entity with inheritance and without discriminator value: " + entity);
}
} else {
if (discriminatorValue != null) {
throw new IllegalStateException("entity doesn't need a discriminator discriminator value: " + entity);
}
}
}
public static boolean isAbstractClass(Class<?> clazz) {
int modifier = clazz.getModifiers();
return Modifier.isAbstract(modifier) || Modifier.isInterface(modifier);
}
static void validateColumn(Class<?> entity, Field field) {
String name = entity.getName() + "." + field.getName();
GeneratedValue generatedValue = field.getAnnotation(GeneratedValue.class);
if (generatedValue != null) {
if (generatedValue.strategy() != GenerationType.AUTO) {
throw new IllegalStateException("strategy must be auto: " + name);
}
if (StringUtils.isNotBlank(generatedValue.generator())) {
throw new IllegalStateException("generator must be null: " + name);
}
}
Column column = field.getAnnotation(Column.class);
if (column != null) {
if (field.getType().equals(String.class) && column.length() > 4000) {
throw new IllegalStateException("varchar greater than 4000: " + name);
}
if (StringUtils.isNotBlank(column.name())
&& ! OracleKeywords.KEYWORDS.contains(field.getName())
&& field.getName().length() <= 28){
if (column.name().toLowerCase().endsWith("_id")){
if (! StringUtils.substringBefore(column.name().toLowerCase(), "_id").equalsIgnoreCase(field.getName())){
throw new IllegalStateException("column/field name mismatch: " + name);
}
} else {
if (! column.name().equalsIgnoreCase(field.getName())){
throw new IllegalStateException("column/field name mismatch: " + name);
}
}
}
}
NotFound notFound = field.getAnnotation(NotFound.class);
if (notFound != null){
throw new IllegalStateException("@NotFound annotation on " + name);
}
final JoinColumn joinColumn = field.getAnnotation(JoinColumn.class);
if (joinColumn != null && StringUtils.isNotBlank(joinColumn.name())){
if (field.getName().length() <= 25 && ! (field.getName() + "_id").equalsIgnoreCase(joinColumn.name())){
throw new IllegalStateException("JoinColumn name mismatch: " + name);
}
}
}
public static String getFullName(final Field field) {
return field.getDeclaringClass().getName() + "." + field.getName();
}
}