package com.txtr.hibernatedelta.model; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.persistence.GeneratedValue; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import org.apache.commons.lang3.StringUtils; public class HibernateModelUtil { public static final int MAX_IDENTIFIER_LENGTH = 30; private HibernateModelUtil() { } public static void checkColumnOrTableName(String entityName, String physicalName, String context) { checkColumnName("entity " + entityName, physicalName, context); if (OracleKeywords.KEYWORDS.contains(physicalName)) { throw new IllegalStateException("physical name '" + physicalName + "' of entity " + entityName + " is a keyword: " + context); } } public static void checkColumnName(String origin, String columnName, String context) { if (columnName.length() > MAX_IDENTIFIER_LENGTH) { throw new IllegalStateException("physical column name '" + columnName + "' of " + origin + " too long: " + context); } if (!columnName.matches("[a-zA-Z_0-9]*")) { throw new IllegalStateException("physical column name '" + columnName + "' of " + origin + " contains illegal characters: " + context); } } public static void checkColumnNames(String origin, String[] columnNames, String context) { for (String columnName : columnNames) { checkColumnName(origin, columnName, context); } } public static void checkFunctionalIndexColumnNames(String origin, String[] columnNames) { for (String columnName : columnNames) { checkFunctionalIndexColumnName(origin, columnName); } } private static void checkFunctionalIndexColumnName(String origin, String columnName) { checkColumnName(origin, removeFunctions(columnName, origin), ""); } private static String removeFunctions(String columnName, String origin) { String realColumnName = removeFunction(columnName, origin); return columnName.equals(realColumnName) ? realColumnName : removeFunction(realColumnName, origin); } private static String removeFunction(String columnName, String origin) { if (StringUtils.startsWithIgnoreCase(columnName, "LOWER(") && StringUtils.endsWith(columnName, ")")) { return StringUtils.removeEnd(StringUtils.removeStartIgnoreCase(columnName, "LOWER("), ")"); } if (StringUtils.startsWithIgnoreCase(columnName, "TRIM(") && StringUtils.endsWith(columnName, ")")) { return StringUtils.removeEnd(StringUtils.removeStartIgnoreCase(columnName, "TRIM("), ")"); } if (StringUtils.startsWithIgnoreCase(columnName, "SYS.DBMS_LOB.SUBSTR(") && StringUtils.endsWith(columnName, ")")) { List<String> arguments = extractArguments(StringUtils.substringBeforeLast(StringUtils.removeStartIgnoreCase(columnName, "SYS.DBMS_LOB.SUBSTR("), ")")); if (arguments.size() != 2) { throw new IllegalStateException("SYS.DBMS_LOB.SUBSTR must have 2 parameters: " + columnName + " of " + origin); } if (!"512".equals(arguments.get(1))) { throw new IllegalStateException("SYS.DBMS_LOB.SUBSTR length parameter must be 512: " + columnName + " of " + origin); } return arguments.get(0); } if (StringUtils.startsWithIgnoreCase(columnName, "SUBSTR(") && StringUtils.endsWith(columnName, ")")) { List<String> arguments = extractArguments(StringUtils.substringBeforeLast(StringUtils.removeStartIgnoreCase(columnName, "SUBSTR("), ")")); if (arguments.size() != 3) { throw new IllegalStateException("SUBSTR must have 3 parameters: " + columnName + " of " + origin); } if (!"1".equals(arguments.get(1))) { throw new IllegalStateException("SUBSTR start parameter must be 1: " + columnName + " of " + origin); } if (!"512".equals(arguments.get(2))) { throw new IllegalStateException("SUBSTR end parameter must be 512: " + columnName + " of " + origin); } return arguments.get(0); } return columnName; } private static List<String> extractArguments(String value) { List<String> result = new ArrayList<String>(); for (String arg : Arrays.asList(StringUtils.split(value, ","))) { result.add(arg.trim()); } return result; } public static Class<? extends Object> getTableRootClass(Class<? extends Object> entityClass) { HibernateTableRootFinder finder = new HibernateTableRootFinder(); finder.getTableRootClass(entityClass); return finder.getResult(); } public static Class<?> getTableClass(Class<?> entity) { Class<? extends Object> tableRootClass = getTableRootClass(entity); if (entity.equals(tableRootClass)) { return entity; } if (getInheritance(tableRootClass) == InheritanceType.SINGLE_TABLE) { return tableRootClass; } else { return entity; } } public static boolean isTableRoot(Class<?> entity) { return entity.equals(getTableRootClass(entity)); } public static boolean isTableClass(Class<?> entity) { return entity.equals(getTableClass(entity)); } public static InheritanceType getInheritance(Class<?> entity) { Class<? extends Object> tableClass = getTableRootClass(entity); Inheritance inheritance = tableClass.getAnnotation(Inheritance.class); if (inheritance == null) { throw new IllegalStateException("inheritance type not found: " + entity); } return inheritance.strategy(); } public static String getTableName(Class<?> entity) { final Table table = entity.getAnnotation(Table.class); final VirtualRootTable virtualRootTable = entity.getAnnotation(VirtualRootTable.class); if (table != null) { return table.name(); } else if (virtualRootTable != null) { return virtualRootTable.value(); } else { throw new IllegalArgumentException("not a table annotated class: " + entity); } } public static String getSequenceName(Class<?> entity) { final Class<?> tableRootClass = getTableRootClass(entity); if (tableRootClass != null && hasAutoGeneratedId(entity)) { return buildSequenceName(getTableName(tableRootClass)); } else { return null; } } private static boolean hasAutoGeneratedId(Class<?> entity) { for (Field field : entity.getDeclaredFields()) { if (field.getAnnotation(GeneratedValue.class) != null) { return true; } } return entity.getSuperclass() != null && hasAutoGeneratedId(entity.getSuperclass()); } public static String buildSequenceName(String tableName) { // the table name may already be long and we may not exceed 30 characters return (tableName + "_").toUpperCase(); } }