package liquibase.datatype; import liquibase.database.Database; import liquibase.database.core.MSSQLDatabase; import liquibase.exception.UnexpectedLiquibaseException; import liquibase.servicelocator.PrioritizedService; import liquibase.statement.DatabaseFunction; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Object representing a data type, instead of a plain string. It will be returned by * the getXXXType in the Database interface. */ public abstract class LiquibaseDataType implements PrioritizedService { private String name; private String[] aliases; private int priority; private int minParameters; private int maxParameters; private List<Object> parameters = new ArrayList<Object>(); private String additionalInformation; private String rawDefinition; protected LiquibaseDataType(LiquibaseDataType originalType) { name = originalType.name; this.minParameters = originalType.minParameters; this.maxParameters = originalType.maxParameters; this.aliases = originalType.aliases; this.priority = originalType.priority; } public LiquibaseDataType() { DataTypeInfo dataTypeAnnotation = this.getClass().getAnnotation(DataTypeInfo.class); this.name = dataTypeAnnotation.name(); this.minParameters = dataTypeAnnotation.minParameters(); this.maxParameters = dataTypeAnnotation.maxParameters(); this.aliases = dataTypeAnnotation.aliases(); this.priority = dataTypeAnnotation.priority(); } protected LiquibaseDataType(String name, int minParameters, int maxParameters) { this.name = name; this.minParameters = minParameters; this.maxParameters = maxParameters; this.aliases = new String[0]; this.priority = 0; } public String getName() { return name; } public String[] getAliases() { return aliases; } @Override public int getPriority() { return priority; } public boolean supports(Database database) { return true; } public int getMinParameters(Database database) { return minParameters; } public int getMaxParameters(Database database) { return maxParameters; } public Object[] getParameters() { return parameters.toArray(); } public void addParameter(Object value) { this.parameters.add(value); } public String getAdditionalInformation() { return additionalInformation; } public void setAdditionalInformation(String additionalInformation) { this.additionalInformation = additionalInformation; } public String getRawDefinition() { return rawDefinition; } public boolean validate(Database database) { int maxParameters = this.getMaxParameters(database); int minParameters = this.getMinParameters(database); if (parameters.size() > maxParameters) { throw new UnexpectedLiquibaseException("Type "+getClass()+" doesn't support "+ maxParameters+" parameters"); } if (parameters.size() < minParameters) { throw new UnexpectedLiquibaseException("Type "+getClass()+" requires "+ minParameters+" parameters"); } return true; } public DatabaseDataType toDatabaseDataType(Database database) { if (database instanceof MSSQLDatabase) { String name = database.escapeDataTypeName(getName()); int dataTypeMaxParameters = database.getDataTypeMaxParameters(getName()); Object[] parameters = getParameters(); if (dataTypeMaxParameters < parameters.length) { parameters = Arrays.copyOfRange(parameters, 0, dataTypeMaxParameters); } return new DatabaseDataType(name, parameters); } DatabaseDataType type = new DatabaseDataType(name.toUpperCase(), getParameters()); type.addAdditionalInformation(additionalInformation); return type; } /** * Returns the value object in a format to include in SQL. Quote if necessary. */ public String objectToSql(Object value, Database database) { if (value == null || value.toString().equalsIgnoreCase("null")) { return null; } else if (value instanceof DatabaseFunction) { return functionToSql((DatabaseFunction) value, database); } else if (value instanceof Number) { return numberToSql((Number) value, database); } return otherToSql(value, database); } protected String functionToSql(DatabaseFunction function, Database database) { return function == null ? null : database.generateDatabaseFunctionValue(function); } protected String numberToSql(Number number, Database database) { if (number == null) { return null; } if (number instanceof BigDecimal) { return formatNumber(((BigDecimal) number).toPlainString()); } return formatNumber(number.toString()); } protected String otherToSql(Object value, Database database) { return value == null ? null : value.toString(); } public Object sqlToObject(String value, Database database) { return value; } @Override public String toString() { String returnString = getName(); if (parameters != null && parameters.size() > 0 && maxParameters > 0) { returnString += "("; for (Object param : parameters) { if (returnString == null) { returnString += "NULL,"; } returnString += param.toString()+","; } returnString = returnString.replaceFirst(",$", ""); // if (getUnit() != null) { // returnString+=" " + getUnit(); // } returnString += ")"; } if (additionalInformation != null) { returnString += " "+additionalInformation; } return returnString.trim(); } @Override public boolean equals(final Object o) { return o instanceof LiquibaseDataType && toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } protected boolean isCurrentDateTimeFunction(String string, Database database) { return string.toLowerCase().startsWith("current_timestamp") || string.toLowerCase().startsWith(DatabaseFunction.CURRENT_DATE_TIME_PLACE_HOLDER) || database.getCurrentDateTimeFunction().equalsIgnoreCase(string); } public void finishInitialization(String originalDefinition) { this.rawDefinition = originalDefinition; } protected String formatNumber(String value) { if (value == null) { return null; } return value.replaceFirst("\\.0+$", ""); } }