package liquibase.changelog.filter;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.RanChangeSet;
import liquibase.database.Database;
import liquibase.exception.DatabaseException;
import java.util.*;
public class ShouldRunChangeSetFilter implements ChangeSetFilter {
private final Map<String, RanChangeSet> ranChangeSets;
private final boolean ignoreClasspathPrefix;
public ShouldRunChangeSetFilter(Database database, boolean ignoreClasspathPrefix) throws DatabaseException {
this.ignoreClasspathPrefix = ignoreClasspathPrefix;
this.ranChangeSets = new HashMap<String, RanChangeSet>();
//ensure we have only the latest version of each ranChangeset in case multiple versions ended up in the databasechangelog table
for (RanChangeSet ranChangeSet : database.getRanChangeSetList()) {
RanChangeSet existingChangeSet = ranChangeSets.get(ranChangeSet.toString());
boolean addToSet = false;
if (existingChangeSet == null) {
addToSet = true;
} else {
Date existingDate = existingChangeSet.getDateExecuted();
Date thisDate = ranChangeSet.getDateExecuted();
if (existingDate != null && thisDate != null) {
int comparedDates = thisDate.compareTo(existingDate);
if (comparedDates < 0) {
addToSet = true;
} else if (comparedDates == 0) {
Integer existingOrder = existingChangeSet.getOrderExecuted();
Integer thisOrder = ranChangeSet.getOrderExecuted();
if (existingOrder != null && thisOrder != null && thisOrder.compareTo(existingOrder) < 0) {
addToSet = true;
}
}
}
}
if (addToSet) {
this.ranChangeSets.put(ranChangeSet.toString(), ranChangeSet);
}
}
}
public ShouldRunChangeSetFilter(Database database) throws DatabaseException {
this(database, true);
}
@Override
@SuppressWarnings({"RedundantIfStatement"})
public ChangeSetFilterResult accepts(ChangeSet changeSet) {
for (RanChangeSet ranChangeSet : this.ranChangeSets.values()) {
if (changeSetsMatch(changeSet, ranChangeSet)) {
if (changeSet.shouldAlwaysRun()) {
return new ChangeSetFilterResult(true, "Change set always runs", this.getClass());
}
if (changeSet.shouldRunOnChange() && checksumChanged(changeSet, ranChangeSet)) {
return new ChangeSetFilterResult(true, "Change set checksum changed", this.getClass());
}
return new ChangeSetFilterResult(false, "Change set already ran", this.getClass());
}
}
return new ChangeSetFilterResult(true, "Change set has not ran yet", this.getClass());
}
protected boolean changeSetsMatch(ChangeSet changeSet, RanChangeSet ranChangeSet) {
return idsAreEqual(changeSet, ranChangeSet)
&& authorsAreEqual(changeSet, ranChangeSet)
&& pathsAreEqual(changeSet, ranChangeSet);
}
protected boolean idsAreEqual(ChangeSet changeSet, RanChangeSet ranChangeSet) {
return ranChangeSet.getId().equals(changeSet.getId());
}
protected boolean authorsAreEqual(ChangeSet changeSet, RanChangeSet ranChangeSet) {
return ranChangeSet.getAuthor().equals(changeSet.getAuthor());
}
private boolean pathsAreEqual(ChangeSet changeSet, RanChangeSet ranChangeSet) {
String ranChangeSetPath = getPath(ranChangeSet);
String changeSetPath = getPath(changeSet);
if (ranChangeSetPath == null) {
return changeSetPath == null;
} else {
return ranChangeSetPath.equalsIgnoreCase(changeSetPath);
}
}
protected boolean checksumChanged(ChangeSet changeSet, RanChangeSet ranChangeSet) {
return !changeSet.generateCheckSum().equals(ranChangeSet.getLastCheckSum());
}
private String getPath(RanChangeSet ranChangeSet) {
return normalizePath(ranChangeSet.getChangeLog());
}
private String getPath(ChangeSet changeSet) {
return normalizePath(changeSet.getFilePath());
}
protected String normalizePath(String filePath) {
if (filePath == null) {
return null;
}
if (ignoreClasspathPrefix) {
return filePath.replaceFirst("^classpath:", "");
}
return filePath;
}
}