package liquibase.diff.compare.core; import liquibase.database.Database; import liquibase.diff.ObjectDifferences; import liquibase.diff.compare.CompareControl; import liquibase.diff.compare.DatabaseObjectComparatorFactory; import liquibase.structure.DatabaseObject; import liquibase.diff.compare.DatabaseObjectComparator; import liquibase.diff.compare.DatabaseObjectComparatorChain; import liquibase.structure.core.Column; import liquibase.structure.core.DataType; import java.util.Collection; import java.util.HashSet; import java.util.Set; public final class DefaultDatabaseObjectComparator implements DatabaseObjectComparator { @Override public int getPriority(Class<? extends DatabaseObject> objectType, Database database) { return PRIORITY_DEFAULT; } @Override public String[] hash(DatabaseObject databaseObject, Database accordingTo, DatabaseObjectComparatorChain chain) { String name = databaseObject.getName(); if (name == null) { name = "null"; } return new String[] {name.toLowerCase()}; } @Override public boolean isSameObject(DatabaseObject databaseObject1, DatabaseObject databaseObject2, Database accordingTo, DatabaseObjectComparatorChain chain) { if (databaseObject1.getSchema() != null && databaseObject2.getSchema() != null && !DatabaseObjectComparatorFactory.getInstance().isSameObject(databaseObject1.getSchema(), databaseObject2.getSchema(), chain.getSchemaComparisons(), accordingTo)) { return false; } if (databaseObject1.getClass().isAssignableFrom(databaseObject2.getClass()) || databaseObject2.getClass().isAssignableFrom(databaseObject1.getClass())) { return nameMatches(databaseObject1, databaseObject2, accordingTo); } return false; } @Override public ObjectDifferences findDifferences(DatabaseObject databaseObject1, DatabaseObject databaseObject2, Database accordingTo, CompareControl compareControl, DatabaseObjectComparatorChain chain, Set<String> exclude) { Set<String> attributes = new HashSet<String>(); attributes.addAll(databaseObject1.getAttributes()); attributes.addAll(databaseObject2.getAttributes()); ObjectDifferences differences = new ObjectDifferences(compareControl); exclude.add("schema"); exclude.add("catalog"); for (String attribute : attributes) { if (exclude.contains(attribute)) { continue; } Object attribute1 = databaseObject1.getAttribute(attribute, Object.class); Object attribute2 = databaseObject2.getAttribute(attribute, Object.class); attribute1 = undoCollection(attribute1, attribute2); attribute2 = undoCollection(attribute2, attribute1); ObjectDifferences.CompareFunction compareFunction; if (attribute1 instanceof DatabaseObject || attribute2 instanceof DatabaseObject) { Class<? extends DatabaseObject> type; if (attribute1 != null) { type = (Class<? extends DatabaseObject>) attribute1.getClass(); } else { type = (Class<? extends DatabaseObject>) attribute2.getClass(); } compareFunction = new ObjectDifferences.DatabaseObjectNameCompareFunction(type, accordingTo); } else if (attribute1 instanceof DataType || attribute2 instanceof DataType) { compareFunction = new ObjectDifferences.ToStringCompareFunction(false); } else if (attribute1 instanceof Column.AutoIncrementInformation || attribute2 instanceof Column.AutoIncrementInformation) { compareFunction = new ObjectDifferences.ToStringCompareFunction(false); } else if (attribute1 instanceof Collection || attribute2 instanceof Collection) { compareFunction = new ObjectDifferences.OrderedCollectionCompareFunction(new ObjectDifferences.StandardCompareFunction(chain.getSchemaComparisons(), accordingTo)); } else { compareFunction = new ObjectDifferences.StandardCompareFunction(chain.getSchemaComparisons(), accordingTo); } differences.compare(attribute, databaseObject1, databaseObject2, compareFunction); } return differences; } /** * Sometimes an attribute in one object is a single-entity collection and on the other it is just the object. * Check the passed potentialCollection and if it is a single-entry collection of the same type as the otherObject, return just the collection element. * Otherwise, return the original collection. */ protected Object undoCollection(Object potentialCollection, Object otherObject) { if (potentialCollection != null && otherObject != null && potentialCollection instanceof Collection && !(otherObject instanceof Collection)) { if (((Collection) potentialCollection).size() == 1 && ((Collection) potentialCollection).iterator().next().getClass().equals(otherObject.getClass())) { potentialCollection = ((Collection) potentialCollection).iterator().next(); } } return potentialCollection; } //Static so it can be used in other comparators if needed public static boolean nameMatches(DatabaseObject databaseObject1, DatabaseObject databaseObject2, Database accordingTo) { String object1Name = accordingTo.correctObjectName(databaseObject1.getName(), databaseObject1.getClass()); String object2Name = accordingTo.correctObjectName(databaseObject2.getName(), databaseObject2.getClass()); if (object1Name == null && object2Name == null) { return true; } if (object1Name == null || object2Name == null) { return false; } if (accordingTo.isCaseSensitive()) { return object1Name.equals(object2Name); } else { return object1Name.equalsIgnoreCase(object2Name); } } }