com.seeq.utilities.Checksum Maven / Gradle / Ivy
The newest version!
package com.seeq.utilities;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import javax.xml.bind.DatatypeConverter;
import lombok.EqualsAndHashCode;
/**
* Provides a method of computing a checksum for both in-memory objects and files. Implements convenient toString() and
* equals() methods for conversion to a hex representation and for comparison.
*
* Note: The checksum is done in a transitive manner. I.e., the order of items added to the checksum does not affect the
* final checksum value.
*/
@EqualsAndHashCode
public class Checksum {
private final byte[] hash = new byte[16];
/**
* Returns a hash byte array representing the checksum
*
* @return a hash byte array representing the checksum
*/
public byte[] getHash() {
return this.hash;
}
/**
* Computes a checksum for an object by reading from a file.
*
* @param filePath
* the path to create a checksum for
* @throws IOException
* thrown if there is an issue reading the file
*/
public Checksum(Path filePath) throws IOException {
this.computeFromFile(filePath);
}
/**
* Computes a checksum for an object by reading from a file.
*
* @param fileName
* the file to create a checksum for
* @throws IOException
* thrown if there is an issue reading the file
*/
public Checksum(File fileName) throws IOException {
this.computeFromFile(fileName);
}
/**
* Computes a checksum by hashing a byte array.
*
* @param bytes
* the bytes to create a checksum for
*/
public Checksum(byte[] bytes) {
this.computeFromByteArray(bytes);
}
/**
* Intended to be used in conjunction with {@link #addFile(File)} to checksum a set of files.
*/
public Checksum() {
}
private static MessageDigest createMD5() {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 algorithm not available");
}
return md5;
}
private void computeFromByteArray(byte[] bytes) {
if (bytes == null) {
return;
}
// Create the md5 instance and update the state
MessageDigest md5 = createMD5();
this.addDigestToHash(md5.digest(bytes));
}
private void computeFromFile(Path path) throws IOException {
if (path == null) {
return;
}
this.computeFromFile(path.toFile());
}
private void computeFromFile(File file) throws IOException {
if (file == null || !file.exists()) {
return;
}
// Create the md5 instance and update the state
MessageDigest md5 = createMD5();
try (DigestInputStream md5InputStream = new DigestInputStream(Files.newInputStream(file.toPath()), md5)) {
byte[] inputBuffer = new byte[4096];
while (md5InputStream.read(inputBuffer) >= 0) {}
this.addDigestToHash(md5InputStream.getMessageDigest().digest());
}
}
private void addDigestToHash(byte[] digest) {
ByteBuffer oldHash = ByteBuffer.wrap(this.hash);
ByteBuffer newHash = ByteBuffer.wrap(digest);
LongBuffer oldHashLongs = oldHash.asLongBuffer();
LongBuffer newHashLongs = newHash.asLongBuffer();
for (int i = 0; i < 2; i++) {
oldHashLongs.put(i, oldHashLongs.get(i) + newHashLongs.get(i));
}
}
/**
* Add a path to the checksum computation. Useful for computing the checksum of a set of files.
*
* @param path
* The file to add to the checksum.
* @return This object to allow fluent style programming.
* @throws IOException
* If a file access error occurs.
*/
public Checksum addFile(Path path) throws IOException {
this.computeFromFile(path);
return this;
}
/**
* Add a file to the checksum computation. Useful for computing the checksum of a set of files.
*
* @param file
* The file to add to the checksum.
* @return This object to allow fluent style programming.
* @throws IOException
* If a file access error occurs.
*/
public Checksum addFile(File file) throws IOException {
this.computeFromFile(file);
return this;
}
/**
* Add files to the checksum computation.
*
* @param files
* The files to add to the checksum
* @return This object to allow fluent style programming.
* @throws IOException
* If a file access error occurs.
*/
public Checksum addFiles(Iterator files) throws IOException {
while (files.hasNext()) {
this.computeFromFile(files.next());
}
return this;
}
/**
* Add a byte array to the checksum computation.
*
* @param bytes
* The bytes to add to the checksum
* @return This object to allow fluent style programming.
*/
public Checksum addByteArray(byte[] bytes) {
this.computeFromByteArray(bytes);
return this;
}
/**
* Returns a hexadecimal string representing the checksum
*/
@Override
public String toString() {
return DatatypeConverter.printHexBinary(this.hash).replace("-", "").toLowerCase();
}
}