package io.ebeaninternal.dbmigration.model.build;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.IndexSet;
import io.ebeaninternal.dbmigration.model.MColumn;
import io.ebeaninternal.dbmigration.model.MCompoundForeignKey;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.model.visitor.BaseTablePropertyVisitor;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.TableJoinColumn;
import io.ebeaninternal.server.deploy.id.ImportedId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
protected final ModelBuildContext ctx;
private final MTable table;
private final BeanDescriptor<?> beanDescriptor;
private final IndexSet indexSet = new IndexSet();
private MColumn lastColumn;
private int countForeignKey;
private int countIndex;
private int countUnique;
private int countCheck;
public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table, BeanDescriptor<?> beanDescriptor) {
this.ctx = ctx;
this.table = table;
this.beanDescriptor = beanDescriptor;
addIndexes(beanDescriptor.getIndexDefinitions());
}
private void addIndexes(IndexDefinition[] indexes) {
if (indexes != null) {
for (IndexDefinition index : indexes) {
String[] columns = index.getColumns();
indexSet.add(columns);
if (index.isUnique()) {
String uqName = index.getName();
if (uqName == null || uqName.trim().isEmpty()) {
uqName = determineUniqueConstraintName(columns);
}
table.addUniqueConstraint(columns, false, uqName);
} else {
String idxName = index.getName();
if (idxName == null || idxName.trim().isEmpty()) {
idxName = determineIndexName(columns);
}
ctx.addIndex(idxName, table.getName(), columns);
}
}
}
}
@Override
public void visitEnd() {
table.setPkName(determinePrimaryKeyName());
for (MColumn column : table.allColumns()) {
if (hasValue(column.getForeignKeyIndex())) {
if (indexSet.contains(column.getName())) {
column.setForeignKeyIndex(null);
}
}
}
for (MCompoundForeignKey compoundKey : table.getCompoundKeys()) {
if (indexSet.contains(compoundKey.getColumns())) {
compoundKey.setIndexName(null);
}
}
addDraftTable();
table.updateCompoundIndices();
}
private void addDraftTable() {
if (beanDescriptor.isDraftable() || beanDescriptor.isDraftableElement()) {
ctx.createDraft(table, !beanDescriptor.isDraftableElement());
}
}
@Override
public void visitMany(BeanPropertyAssocMany<?> p) {
if (p.hasJoinTable() && p.getMappedBy() == null) {
MTable intersectionTable = new ModelBuildIntersectionTable(ctx, p).build();
if (p.isO2mJoinTable()) {
intersectionTable.clearForeignKeyIndexes();
Collection<MColumn> cols = intersectionTable.allColumns();
if (cols.size() == 2) {
MColumn col = new ArrayList<>(cols).get(1);
col.setUnique(determineUniqueConstraintName(col.getName()));
}
}
} else if (p.isElementCollection()) {
ModelBuildElementTable.build(ctx, p);
}
}
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
if (p instanceof BeanPropertyAssocOne) {
visitOneImported((BeanPropertyAssocOne)p);
} else {
visitScalar(p);
}
if (embedded.isId()) {
lastColumn.setPrimaryKey(true);
}
}
@Override
public void visitOneImported(BeanPropertyAssocOne<?> p) {
TableJoinColumn[] columns = p.getTableJoin().columns();
if (columns.length == 0) {
throw new RuntimeException("No join columns for " + p.getFullBeanName());
}
ImportedId importedId = p.getImportedId();
List<MColumn> modelColumns = new ArrayList<>(columns.length);
PropertyForeignKey foreignKey = p.getForeignKey();
MCompoundForeignKey compoundKey = null;
if (columns.length > 1) {
String refTable = p.getTargetDescriptor().getBaseTable();
String fkName = determineForeignKeyConstraintName(p.getName());
String fkIndex = determineForeignKeyIndexName(p.getName());
compoundKey = new MCompoundForeignKey(fkName, refTable, fkIndex);
table.addForeignKey(compoundKey);
}
for (TableJoinColumn column : columns) {
String dbCol = column.getLocalDbColumn();
BeanProperty importedProperty = importedId.findMatchImport(dbCol);
if (importedProperty == null) {
throw new RuntimeException("Imported BeanProperty not found?");
}
String columnDefn = ctx.getColumnDefn(importedProperty, true);
String refColumn = importedProperty.getDbColumn();
MColumn col = table.addColumn(dbCol, columnDefn, !p.isNullable());
col.setDbMigrationInfos(p.getDbMigrationInfos());
col.setDefaultValue(p.getDbColumnDefault());
if (columns.length == 1) {
if (p.hasForeignKey() && !importedProperty.getBeanDescriptor().suppressForeignKey()) {
String refTable = importedProperty.getBeanDescriptor().getBaseTable();
if (refTable == null) {
refTable = p.getTargetDescriptor().getBaseTable();
}
col.setReferences(refTable + "." + refColumn);
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
if (p.hasForeignKeyIndex()) {
col.setForeignKeyIndex(determineForeignKeyIndexName(col.getName()));
}
if (foreignKey != null) {
col.setForeignKeyModes(foreignKey.getOnDelete(), foreignKey.getOnUpdate());
}
}
} else {
compoundKey.addColumnPair(dbCol, refColumn);
}
modelColumns.add(col);
}
if (p.isOneToOne()) {
if (modelColumns.size() == 1) {
MColumn col = modelColumns.get(0);
col.setUniqueOneToOne(determineUniqueConstraintName(col.getName()));
indexSetAdd(col.getName());
} else {
String uqName = determineUniqueConstraintName(p.getName());
table.addUniqueConstraint(modelColumns, true, uqName);
indexSetAdd(modelColumns);
}
}
}
@Override
public void visitScalar(BeanProperty p) {
if (p.isSecondaryTable()) {
lastColumn = null;
return;
}
MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p, false));
col.setComment(p.getDbComment());
col.setDraftOnly(p.isDraftOnly());
col.setHistoryExclude(p.isExcludedFromHistory());
if (p.isId()) {
col.setPrimaryKey(true);
if (p.getBeanDescriptor().isUseIdGenerator()) {
col.setIdentity(true);
}
TableJoin primaryKeyJoin = p.getBeanDescriptor().getPrimaryKeyJoin();
if (primaryKeyJoin != null && !table.isPartitioned()) {
TableJoinColumn[] columns = primaryKeyJoin.columns();
col.setReferences(primaryKeyJoin.getTable() + "." + columns[0].getForeignDbColumn());
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
}
} else {
col.setDefaultValue(p.getDbColumnDefault());
if (!p.isNullable() || p.isDDLNotNull()) {
col.setNotnull(true);
}
}
col.setDbMigrationInfos(p.getDbMigrationInfos());
if (p.isUnique() && !p.isId()) {
col.setUnique(determineUniqueConstraintName(col.getName()));
indexSetAdd(col.getName());
}
Set<String> checkConstraintValues = p.getDbCheckConstraintValues();
if (checkConstraintValues != null) {
if (beanDescriptor.hasInheritance()) {
InheritInfo inheritInfo = beanDescriptor.getInheritInfo();
inheritInfo.appendCheckConstraintValues(p.getName(), checkConstraintValues);
}
col.setCheckConstraint(buildCheckConstraint(p.getDbColumn(), checkConstraintValues));
col.setCheckConstraintName(determineCheckConstraintName(col.getName()));
}
lastColumn = col;
table.addColumn(col);
}
private String buildCheckConstraint(String dbColumn, Set<String> checkConstraintValues) {
StringBuilder sb = new StringBuilder();
sb.append("check ( ").append(dbColumn).append(" in (");
int count = 0;
for (String value : checkConstraintValues) {
if (count++ > 0) {
sb.append(",");
}
sb.append(value);
}
sb.append("))");
return sb.toString();
}
private void indexSetAdd(String column) {
indexSet.add(column);
}
private void indexSetAdd(List<MColumn> modelColumns) {
String[] cols = new String[modelColumns.size()];
for (int i = 0; i < modelColumns.size(); i++) {
cols[i] = modelColumns.get(i).getName();
}
indexSet.add(cols);
}
protected String determinePrimaryKeyName() {
return ctx.primaryKeyName(table.getName());
}
protected String determineForeignKeyConstraintName(String columnName) {
return ctx.foreignKeyConstraintName(table.getName(), columnName, ++countForeignKey);
}
protected String determineForeignKeyIndexName(String column) {
String[] cols = {column};
return determineForeignKeyIndexName(cols);
}
protected String determineForeignKeyIndexName(String[] columns) {
return ctx.foreignKeyIndexName(table.getName(), columns, ++countIndex);
}
protected String determineIndexName(String column) {
return ctx.indexName(table.getName(), column, ++countIndex);
}
protected String determineIndexName(String[] columns) {
return ctx.indexName(table.getName(), columns, ++countIndex);
}
protected String determineUniqueConstraintName(String columnName) {
return ctx.uniqueConstraintName(table.getName(), columnName, ++countUnique);
}
protected String determineUniqueConstraintName(String[] columnNames) {
return ctx.uniqueConstraintName(table.getName(), columnNames, ++countUnique);
}
protected String determineCheckConstraintName(String columnName) {
return ctx.checkConstraintName(table.getName(), columnName, ++countCheck);
}
private boolean hasValue(String val) {
return val != null && !val.isEmpty();
}
}