All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy