liquibase.change.CheckSum Maven / Gradle / Ivy
package liquibase.change;
import liquibase.util.MD5Util;
import liquibase.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.text.Normalizer;
/**
* CheckSums are used by liquibase to determine if a Change has been modified since it was originally ran.
* CheckSums can be computed on either a String or an {@link InputStream}.
* The CheckSum contains a version number which can be used to determine whether the algorithm for computing a
* storedCheckSum has changed since the last time it was computed. If the algorithm changes, we cannot rely on
* the storedCheckSum value.
*
* It is not up to this class to determine what should be storedCheckSum-ed, it simply hashes what is passed to it.
*/
public final class CheckSum {
public static final int CURRENT_CHECKSUM_ALGORITHM_VERSION = 8;
private int version;
private String storedCheckSum;
/**
* Constructor. Stores a given checksum generated by a given algorithm version into the new CheckSum object.
* @param checksum Generated checksum (format depends on version)
* @param version The version of the Liquibase checksum generator used
*/
private CheckSum(String checksum, int version) {
this.storedCheckSum = checksum;
this.version = version;
}
/**
* Parse the given storedCheckSum string value and return a new CheckSum object.
*/
public static CheckSum parse(String checksumValue) {
final int CHECKSUM_OFFSET_IN_STRING = 2;
if (checksumValue == null) {
return null;
}
// The general layout of a checksum is:
// <1 digit: algorithm version number>:<1..n characters alphanumeric checksum>
// Example: 7:2cdf9876e74347162401315d34b83746
if (checksumValue.matches("^\\d:.*")) {
// String has the information about the used checksum algorithm version
return new CheckSum(checksumValue.substring(CHECKSUM_OFFSET_IN_STRING),
Integer.parseInt(checksumValue.substring(0,1)));
} else {
// No version information found
return new CheckSum(checksumValue, 1);
}
}
/**
* Return the current CheckSum algorithm version.
*/
public static int getCurrentVersion() {
return CURRENT_CHECKSUM_ALGORITHM_VERSION;
}
/**
* Compute a storedCheckSum of the given string.
*/
public static CheckSum compute(String valueToChecksum) {
return new CheckSum(MD5Util.computeMD5(
//remove "Unknown" unicode char 65533
Normalizer.normalize(
StringUtils.standardizeLineEndings(valueToChecksum)
.replaceAll("\\uFFFD", "")
, Normalizer.Form.NFC)
), getCurrentVersion());
}
/**
* Compute a CheckSum of the given data stream (no normalization of line endings!)
*/
public static CheckSum compute(final InputStream stream, boolean standardizeLineEndings) {
InputStream newStream = stream;
if (standardizeLineEndings) {
newStream = new InputStream() {
@Override
public int read() throws IOException {
int read = stream.read();
if (read == '\r') {
return read();
} else {
return read;
}
}
};
}
return new CheckSum(MD5Util.computeMD5(newStream), getCurrentVersion());
}
@Override
public String toString() {
return version+":"+this.storedCheckSum;
}
/**
* Return the Checksum Algorithm version for this CheckSum
*/
public int getVersion() {
return version;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj instanceof CheckSum) && this.toString().equals(obj.toString());
}
}