package liquibase.ext.spatial.preconditions;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import liquibase.database.Database;
import liquibase.database.core.DerbyDatabase;
import liquibase.database.core.H2Database;
import liquibase.exception.PreconditionErrorException;
import liquibase.exception.PreconditionFailedException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.exception.ValidationErrors;
import liquibase.exception.Warnings;
import liquibase.ext.spatial.xml.XmlConstants;
import liquibase.parser.core.ParsedNode;
import liquibase.parser.core.ParsedNodeException;
import liquibase.precondition.AbstractPrecondition;
import liquibase.precondition.Precondition;
import liquibase.precondition.core.IndexExistsPrecondition;
import liquibase.precondition.core.TableExistsPrecondition;
import liquibase.resource.ResourceAccessor;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.Column;
import liquibase.structure.core.Index;
import liquibase.structure.core.Schema;
import liquibase.structure.core.Table;
import liquibase.util.StringUtils;
/**
* <code>SpatialIndexExistsPrecondition</code> determines if a spatial index exists on a specified
* table.
*/
public class SpatialIndexExistsPrecondition extends AbstractPrecondition {
private String catalogName;
private String schemaName;
private String tableName;
private String columnNames;
private String indexName;
public String getCatalogName() {
return this.catalogName;
}
public void setCatalogName(final String catalogName) {
this.catalogName = catalogName;
}
public String getSchemaName() {
return this.schemaName;
}
public void setSchemaName(final String schemaName) {
this.schemaName = schemaName;
}
public String getTableName() {
return this.tableName;
}
public void setTableName(final String tableName) {
this.tableName = tableName;
}
public String getIndexName() {
return this.indexName;
}
public void setIndexName(final String indexName) {
this.indexName = indexName;
}
public String getColumnNames() {
return this.columnNames;
}
public void setColumnNames(final String columnNames) {
this.columnNames = columnNames;
}
@Override
public String getName() {
return "spatialIndexExists";
}
@Override
public Warnings warn(final Database database) {
return new Warnings();
}
@Override
public ValidationErrors validate(final Database database) {
final ValidationErrors validationErrors;
if ((database instanceof DerbyDatabase || database instanceof H2Database)
&& getTableName() == null) {
validationErrors = new ValidationErrors();
validationErrors
.addError("tableName is required for " + database.getDatabaseProductName());
} else {
final IndexExistsPrecondition precondition = new IndexExistsPrecondition();
precondition.setCatalogName(getCatalogName());
precondition.setSchemaName(getSchemaName());
precondition.setTableName(getTableName());
precondition.setIndexName(getIndexName());
precondition.setColumnNames(getColumnNames());
validationErrors = precondition.validate(database);
}
return validationErrors;
}
@Override
public void check(final Database database, final DatabaseChangeLog changeLog,
final ChangeSet changeSet) throws PreconditionFailedException, PreconditionErrorException {
Precondition delegatedPrecondition;
if (database instanceof DerbyDatabase || database instanceof H2Database) {
final TableExistsPrecondition precondition = new TableExistsPrecondition();
precondition.setCatalogName(getCatalogName());
precondition.setSchemaName(getSchemaName());
final String tableName = getHatboxTableName();
precondition.setTableName(tableName);
delegatedPrecondition = precondition;
} else {
final IndexExistsPrecondition precondition = new IndexExistsPrecondition();
precondition.setCatalogName(getCatalogName());
precondition.setSchemaName(getSchemaName());
precondition.setTableName(getTableName());
precondition.setIndexName(getIndexName());
precondition.setColumnNames(getColumnNames());
delegatedPrecondition = precondition;
}
delegatedPrecondition.check(database, changeLog, changeSet);
}
/**
* Generates the table name containing the Hatbox index.
*
* @return the Hatbox table name.
*/
protected String getHatboxTableName() {
final String tableName;
if (!StringUtils.hasUpperCase(getTableName())) {
tableName = getTableName() + "_hatbox";
} else {
tableName = getTableName() + "_HATBOX";
}
return tableName;
}
/**
* Creates an example of the database object for which to check.
*
* @param database
* the database instance.
* @param tableName
* the table name of the index.
* @return the database object example.
*/
public DatabaseObject getExample(final Database database, final String tableName) {
final Schema schema = new Schema(getCatalogName(), getSchemaName());
final DatabaseObject example;
// For GeoDB, the index is another table.
if (database instanceof DerbyDatabase || database instanceof H2Database) {
final String correctedTableName = database.correctObjectName(getHatboxTableName(),
Table.class);
example = new Table().setName(correctedTableName).setSchema(schema);
} else {
example = getIndexExample(database, schema, tableName);
}
return example;
}
/**
* Generates the {@link Index} example (taken from {@link IndexExistsPrecondition}).
*
* @param database
* the database instance.
* @param schema
* the schema instance.
* @param tableName
* the table name of the index.
* @return the index example.
*/
protected Index getIndexExample(final Database database, final Schema schema,
final String tableName) {
final Index example = new Index();
if (tableName != null) {
example.setTable((Table) new Table().setName(
database.correctObjectName(getTableName(), Table.class)).setSchema(schema));
}
example.setName(database.correctObjectName(getIndexName(), Index.class));
if (StringUtils.trimToNull(getColumnNames()) != null) {
for (final String columnName : getColumnNames().split("\\s*,\\s*")) {
final Column column = new Column(database.correctObjectName(columnName, Column.class));
example.getColumns().add(column);
}
}
return example;
}
/**
* @see liquibase.serializer.LiquibaseSerializable#getSerializedObjectName()
*/
@Override
public String getSerializedObjectName() {
return "spatialIndexExists";
}
/**
* @see liquibase.serializer.LiquibaseSerializable#getSerializableFields()
*/
@Override
public Set<String> getSerializableFields() {
return new LinkedHashSet<String>(Arrays.asList("catalogName", "schemaName", "tableName",
"columnNames", "indexName"));
}
/**
* @see liquibase.serializer.LiquibaseSerializable#getSerializableFieldValue(java.lang.String)
*/
@Override
public Object getSerializableFieldValue(final String field) {
final Object value;
if ("catalogName".equals(field)) {
value = getCatalogName();
} else if ("schemaName".equals(field)) {
value = getSchemaName();
} else if ("tableName".equals(field)) {
value = getTableName();
} else if ("columnNames".equals(field)) {
value = getColumnNames();
} else if ("indexName".equals(field)) {
value = getIndexName();
} else {
throw new UnexpectedLiquibaseException("Unexpected field request on "
+ getSerializedObjectName() + ": " + field);
}
return value;
}
/**
* @see liquibase.serializer.LiquibaseSerializable#getSerializableFieldType(java.lang.String)
*/
@Override
public SerializationType getSerializableFieldType(final String field) {
return SerializationType.NAMED_FIELD;
}
/**
* @see liquibase.serializer.LiquibaseSerializable#getSerializedObjectNamespace()
*/
@Override
public String getSerializedObjectNamespace() {
return XmlConstants.SPATIAL_CHANGELOG_NAMESPACE;
}
/**
* @see liquibase.serializer.LiquibaseSerializable#serialize()
*/
@Override
public ParsedNode serialize() throws ParsedNodeException {
final String namespace = getSerializedObjectNamespace();
final ParsedNode node = new ParsedNode(namespace, getSerializedObjectName());
for (final String field : getSerializableFields()) {
final Object value = getSerializableFieldValue(field);
node.addChild(namespace, field, value);
}
return node;
}
/**
* @see liquibase.precondition.Precondition#load(liquibase.parser.core.ParsedNode,
* liquibase.resource.ResourceAccessor)
*/
@Override
public void load(final ParsedNode parsedNode, final ResourceAccessor resourceAccessor)
throws ParsedNodeException {
final String namespace = null;
this.catalogName = parsedNode.getChildValue(namespace, "catalogName", String.class);
this.schemaName = parsedNode.getChildValue(namespace, "schemaName", String.class);
this.tableName = parsedNode.getChildValue(namespace, "tableName", String.class);
this.columnNames = parsedNode.getChildValue(namespace, "columnNames", String.class);
this.indexName = parsedNode.getChildValue(namespace, "indexName", String.class);
}
}