com.neovisionaries.security.Digest.java Source code

Java tutorial

Introduction

Here is the source code for com.neovisionaries.security.Digest.java

Source

/*
 * Copyright (C) 2013-2015 Neo Visionaries Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.neovisionaries.security;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.codec.BinaryEncoder;
import org.apache.commons.codec.EncoderException;

/**
 * A wrapper class over MessageDigest with many {@code update}
 * methods in a fluent style, meaning {@code update} methods
 * can be chained.
 *
 * <p>
 * {@code update} methods are provided for all the primitive types
 * and {@code String}, and their array types. In addition,
 * {@link #updateJson(String)} has been available since the version
 * 1.2 which updates the digest with the content of the given JSON.
 * Note that {@link #update(String)} and {@link #updateJson(String)}
 * are different.
 * </p>
 *
 * <p>
 * <code>getInstance<i>XXX</i></code> methods (where <code><i>XXX</i></code>
 * is a pre-defined algorithm name with hyphens removed) such as
 * {@link #getInstanceSHA1()} are provided. They won't throw
 * {@code NoSuchAlgorithmException}.
 * </p>
 *
 * <pre style="background-color: #EEEEEE; margin-left: 2em; margin-right: 2em; border: 1px solid black;">
 * <span style="color: darkgreen;">
 * // Compute SHA-1 of "Hello, world.".
 * // 'digest' will have "2ae01472317d1935a84797ec1983ae243fc6aa28".</span>
 * String digest = Digest.{@link #getInstanceSHA1()}
 *                 .{@link #update(String) update}(<span style="color: #990000">"Hello, world."</span>)
 *                 .{@link #digestAsString()};
 *
 * <span style="color: darkgreen;">// Compute SHA-1 of "Hello, world." and get the result as Base64.
 * // 'digest' will have "KuAUcjF9GTWoR5fsGYOuJD/Gqig=".</span>
 * String digest = Digest.{@link #getInstanceSHA1()}
 *                 .{@link #update(String) update}(<span style="color: #990000">"Hello, world."</span>)
 *                 .{@link #digestAsString(BinaryEncoder) digestAsString}(new {@link
 *                 org.apache.commons.codec.binary.Base64#Base64() Base64()});
 *
 * <span style="color: darkgreen;">// Compute SHA-1 of two JSONs.
 * // 'result1' and 'result2' will have the same value.</span>
 * String json1 = <span style="color: #990000">"{ \"key1\":\"value1\", \"key2\":\"value2\" }"</span>;
 * String json2 = <span style="color: #990000">"{ \"key2\":\"value2\", \"key1\":\"value1\" }"</span>;
 * String result1 = Digest.{@link #getInstanceSHA1()}.{@link #updateJson(String)
 * updateJson}(json1).{@link #digestAsString()};
 * String result2 = Digest.{@link #getInstanceSHA1()}.{@link #updateJson(String)
 * updateJson}(json2).{@link #digestAsString()};
 * </pre>
 *
 * @author Takahiko Kawasaki
 */
public class Digest implements Cloneable {
    /**
     * Features to control behaviors.
     *
     * @since 1.2
     */
    public static enum Feature {
        /**
         * Ignore JSON key-value entries whose value is {@code null}.
         * In other words, JSON key-value entries whose value is
         * {@code null} are treated as if they did not exist.
         *
         * <p>
         * If this feature is enabled, two JSONs below generate
         * the same digest value.
         * </p>
         *
         * <pre>
         * { "key1":"value1", "key2":null }
         * { "key1":"value1" }
         * </pre>
         *
         * <p>
         * The default value is 'disabled'.
         * </p>
         */
        IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL,

        /**
         * Ignore JSON key-value entries whose value is {@code false}.
         * In other words, JSON key-value entries whose value is
         * {@code false} are treated as if they did not exist.
         *
         * <p>
         * If this feature is enabled, two JSONs below generate
         * the same digest value.
         * </p>
         *
         * <pre>
         * { "key1":"value1", "key2":false }
         * { "key1":"value1" }
         * </pre>
         *
         * <p>
         * The default value is 'disabled'.
         * </p>
         *
         * @since 1.5
         */
        IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_FALSE,

        /**
         * Ignore JSON key-value entries whose value is zero.
         * In other words, JSON key-value entries whose value is
         * zero are treated as if they did not exist.
         *
         * <p>
         * If this feature is enabled, two JSONs below generate
         * the same digest value.
         * </p>
         *
         * <pre>
         * { "key1":"value1", "key2":0, "key3":0.0 }
         * { "key1":"value1" }
         * </pre>
         *
         * <p>
         * The default value is 'disabled'.
         * </p>
         *
         * @since 1.5
         */
        IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_ZERO,

        /**
         * Ignore JSON key-value entries whose value is an empty string.
         * In other words, JSON key-value entries whose value is an empty
         * string are treated as if they did not exist.
         *
         * <p>
         * If this feature is enabled, two JSONs below generate
         * the same digest value.
         * </p>
         *
         * <pre>
         * { "key1":"value1", "key2":"" }
         * { "key1":"value1" }
         * </pre>
         *
         * <p>
         * The default value is 'disabled'.
         * </p>
         *
         * @since 1.5
         */
        IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_STRING,

        /**
         * Ignore JSON key-value entries whose value is an empty array.
         * In other words, JSON key-value entries whose value is an empty
         * array are treated as if they did not exist.
         *
         * <p>
         * If this feature is enabled, two JSONs below generate
         * the same digest value.
         * </p>
         *
         * <pre>
         * { "key1":"value1", "key2":[] }
         * { "key1":"value1" }
         * </pre>
         *
         * <p>
         * The default value is 'disabled'.
         * </p>
         *
         * @since 1.5
         */
        IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_ARRAY,

        /**
         * Ignore JSON key-value entries whose value is an empty object.
         * In other words, JSON key-value entries whose value is an empty
         * object are treated as if they did not exist.
         *
         * <p>
         * If this feature is enabled, two JSONs below generate
         * the same digest value.
         * </p>
         *
         * <pre>
         * { "key1":"value1", "key2":{} }
         * { "key1":"value1" }
         * </pre>
         *
         * <p>
         * The default value is 'disabled'.
         * </p>
         *
         * @since 1.5
         */
        IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_OBJECT,

        /**
         * Sort keys of JSON key-value entries before updating.
         *
         * <p>
         * If this feature is enabled, two JSONs below generate
         * the same digest value.
         * </p>
         *
         * <pre>
         * { "key1":"value1", "key2":"value2" }
         * { "key2":"value2", "key1":"value1" }
         * </pre>
         *
         * <p>
         * The default value is 'enabled'.
         * </p>
         */
        SORT_JSON_OBJECT_ENTRY_KEYS
    }

    /**
     * Characters used to generate a hex string.
     */
    private static final char[] mHexArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
            'e', 'f' };

    /**
     * The wrapped messaged digest object.
     */
    private MessageDigest mMessageDigest;

    /**
     * Features (configuration).
     */
    private HashMap<Feature, Boolean> mFeatures;

    /**
     * Constructor with a {@link MessageDigest} instance.
     *
     * @param messageDigest
     *         A {@link MessageDigest} instance wrapped in this
     *         {@code Digest} instance.
     *
     * @throws IllegalArgumentException
     *         The given argument is null.
     */
    public Digest(MessageDigest messageDigest) {
        if (messageDigest == null) {
            throw new IllegalArgumentException("messageDigest is null");
        }

        mMessageDigest = messageDigest;
        mFeatures = createFeatureMap();
    }

    /**
     * Create a feature map with default values.
     */
    private HashMap<Feature, Boolean> createFeatureMap() {
        HashMap<Feature, Boolean> map = new HashMap<Feature, Boolean>();

        map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL, Boolean.FALSE);
        map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_FALSE, Boolean.FALSE);
        map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_ZERO, Boolean.FALSE);
        map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_STRING, Boolean.FALSE);
        map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_ARRAY, Boolean.FALSE);
        map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_OBJECT, Boolean.FALSE);
        map.put(Feature.SORT_JSON_OBJECT_ENTRY_KEYS, Boolean.TRUE);

        return map;
    }

    /**
     * Constructor with an algorithm name.
     *
     * <p>
     * This constructor is equivalent to {@link #Digest(MessageDigest) this}{@code
     * (}{@link MessageDigest#getInstance(String) MessageDigest.getInstance}{@code
     * (algorithm))}.
     * </p>
     *
     * @param algorithm
     *         Algorithm name such as "MD5" and "SHA-1".
     *
     * @throws NoSuchAlgorithmException
     *         No provider supports the specified algorithm.
     *
     * @since 1.2
     */
    public Digest(String algorithm) throws NoSuchAlgorithmException {
        this(MessageDigest.getInstance(algorithm));
    }

    /**
     * Constructor with an algorithm name and a provider.
     *
     * <p>
     * This constructor is equivalent to {@link #Digest(MessageDigest) this}{@code
     * (}{@link MessageDigest#getInstance(String,String) MessageDigest.getInstance}{@code
     * (algorithm, provider))}.
     * </p>
     *
     * @param algorithm
     *         Algorithm name such as "MD5" and "SHA-1".
     *
     * @param provider
     *         Provider name.
     *
     * @throws NoSuchAlgorithmException
     *         The provider does not support the specified algorithm.
     *
     * @throws NoSuchProviderException
     *         The specified provider is not registered in the security provider list.
     *
     * @since 1.2
     */
    public Digest(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException {
        this(MessageDigest.getInstance(algorithm, provider));
    }

    /**
     * Constructor with an algorithm name and a provider.
     *
     * <p>
     * This constructor is equivalent to {@link #Digest(MessageDigest) this}{@code
     * (}{@link MessageDigest#getInstance(String,Provider) MessageDigest.getInstance}{@code
     * (algorithm, provider))}.
     * </p>
     *
     * @param algorithm
     *         Algorithm name such as "MD5" and "SHA-1".
     *
     * @param provider
     *         Provider.
     *
     * @throws NoSuchAlgorithmException
     *         The provider does not support the specified algorithm.
     *
     * @since 1.2
     */
    public Digest(String algorithm, Provider provider) throws NoSuchAlgorithmException {
        this(MessageDigest.getInstance(algorithm, provider));
    }

    /**
     * Create a {@code Digest} instance with the specified algorithm.
     *
     * <p>
     * This method creates a {@link MessageDigest} instance by
     * {@link MessageDigest#getInstance(String)} and wraps it
     * in a {@code Digest} instance.
     * </p>
     *
     * @param algorithm
     *         Algorithm name such as "MD5" and "SHA-1".
     *
     * @return
     *         A {@code Digest} instance that implements the specified algorithm.
     *
     * @throws NoSuchAlgorithmException
     *         No provider supports the specified algorithm.
     */
    public static Digest getInstance(String algorithm) throws NoSuchAlgorithmException {
        return new Digest(algorithm);
    }

    /**
     * Create a {@code Digest} instance with the specified algorithm.
     *
     * <p>
     * This method creates a {@link MessageDigest} instance by
     * {@link MessageDigest#getInstance(String, String)} and wraps it
     * in a {@code Digest} instance.
     * </p>
     *
     * @param algorithm
     *         Algorithm name such as "MD5" and "SHA-1".
     *
     * @param provider
     *         Provider name.
     *
     * @return
     *         A {@code Digest} instance that implements the specified algorithm.
     *
     * @throws NoSuchAlgorithmException
     *         The provider does not support the specified algorithm.
     *
     * @throws NoSuchProviderException
     *         The specified provider is not registered in the security provider list.
     */
    public static Digest getInstance(String algorithm, String provider)
            throws NoSuchAlgorithmException, NoSuchProviderException {
        return new Digest(algorithm, provider);
    }

    /**
     * Create a {@code Digest} instance with the specified algorithm.
     *
     * <p>
     * This method creates a {@link MessageDigest} instance by
     * {@link MessageDigest#getInstance(String, Provider)} and wraps it
     * in a {@code Digest} instance.
     * </p>
     *
     * @param algorithm
     *         Algorithm name such as "MD5" and "SHA-1".
     *
     * @param provider
     *         Provider.
     *
     * @return
     *         A {@code Digest} instance that implements the specified algorithm.
     *
     * @throws NoSuchAlgorithmException
     *         The provider does not support the specified algorithm.
     */
    public static Digest getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException {
        return new Digest(algorithm, provider);
    }

    /**
     * Create a {@code Digest} instance with the specified algorithm.
     *
     * <p>
     * This method exists just to ignore {@code NoSuchAlgorithmException}.
     * </p>
     *
     * @param algorithm
     *         Algorithm name such as "MD5" and "SHA-1".
     *
     * @return
     *         A {@code Digest} instance that implements the specified algorithm.
     */
    private static Digest getInstancePredefined(String algorithm) {
        try {
            return getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            // This won't happen.
            return null;
        }
    }

    /**
     * Create a {@code Digest} instance that implements MD2.
     *
     * @return
     *         A {@code Digest} instance that implements MD2.
     */
    public static Digest getInstanceMD2() {
        return getInstancePredefined("MD2");
    }

    /**
     * Create a {@code Digest} instance that implements MD5.
     *
     * @return
     *         A {@code Digest} instance that implements MD5.
     */
    public static Digest getInstanceMD5() {
        return getInstancePredefined("MD5");
    }

    /**
     * Create a {@code Digest} instance that implements SHA-1.
     *
     * @return
     *         A {@code Digest} instance that implements SHA-1.
     */
    public static Digest getInstanceSHA1() {
        return getInstancePredefined("SHA-1");
    }

    /**
     * Create a {@code Digest} instance that implements SHA-256.
     *
     * @return
     *         A {@code Digest} instance that implements SHA-256.
     */
    public static Digest getInstanceSHA256() {
        return getInstancePredefined("SHA-256");
    }

    /**
     * Create a {@code Digest} instance that implements SHA-384.
     *
     * @return
     *         A {@code Digest} instance that implements SHA-384.
     */
    public static Digest getInstanceSHA384() {
        return getInstancePredefined("SHA-384");
    }

    /**
     * Create a {@code Digest} instance that implements SHA-512.
     *
     * @return
     *         A {@code Digest} instance that implements SHA-512.
     */
    public static Digest getInstanceSHA512() {
        return getInstancePredefined("SHA-512");
    }

    /**
     * Get the algorithm name.
     *
     * <p>
     * This method just calls {@link MessageDigest#getAlgorithm()
     * getAlgorithm()} of the wrapped {@code MessageDigest} instance.
     * </p>
     *
     * @return
     *         Algorithm name.
     */
    public String getAlgorithm() {
        return mMessageDigest.getAlgorithm();
    }

    /**
     * Get the length of the digest in bytes.
     *
     * <p>
     * This method just calls {@link MessageDigest#getDigestLength()
     * getDigestLength()} of the wrapped {@code MessageDigest} instance.
     * </p>
     *
     * @return
     *         Length of the digest in bytes.
     */
    public int getDigestLength() {
        return mMessageDigest.getDigestLength();
    }

    /**
     * Get the provider.
     *
     * <p>
     * This method just calls {@link MessageDigest#getProvider()
     * getProvider()} of the wrapped {@code MessageDigest} instance.
     * </p>
     *
     * @return
     *         Provider.
     */
    public Provider getProvider() {
        return mMessageDigest.getProvider();
    }

    /**
     * Get the wrapped {@code MessageDigest} instance.
     *
     * @return
     *         The {@code MessageDigest} instance that has been
     *         given to the constructor.
     */
    public MessageDigest getWrappedMessageDigest() {
        return mMessageDigest;
    }

    /**
     * Get a clone of this {@code Digest} instance.
     *
     * @return
     *         A cloned object.
     *
     * @throws CloneNotSupportedException
     *         The implementation does not support {@code clone} operation.
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object clone() throws CloneNotSupportedException {
        Digest cloned = (Digest) super.clone();

        cloned.mMessageDigest = (MessageDigest) mMessageDigest.clone();
        cloned.mFeatures = (HashMap<Feature, Boolean>) mFeatures.clone();

        return cloned;
    }

    /**
     * Complete the hash computation. The digest is reset after
     * this call is made.
     *
     * <p>
     * This method just calls {@link MessageDigest#digest() digest()}
     * method of the wrapped {@code MessageDigest} instance.
     * </p>
     *
     * @return
     *         The resulting hash value.
     */
    public byte[] digest() {
        return mMessageDigest.digest();
    }

    /**
     * Perform the final update with the given byte array, and then
     * complete the hash computation. The digest is reset after
     * this call is made.
     *
     * <p>
     * This method just calls {@link MessageDigest#digest(byte[])
     * digest(byte[])} method of the wrapped {@code MessageDigest}
     * instance.
     * </p>
     *
     * @param input
     *         Byte array used for the last update.
     *
     * @return
     *         The resulting hash value.
     */
    public byte[] digest(byte[] input) {
        return mMessageDigest.digest(input);
    }

    /**
     * Complete the hash computation. The digest is reset after
     * this call is made.
     *
     * <p>
     * This method just calls {@link MessageDigest#digest(byte[],int,int)
     * digest(byte[], int, int)} method of the wrapped {@code MessageDigest}
     * instance.
     * </p>
     *
     * @param output
     *         Output buffer for the computed digest.
     *
     * @param offset
     *         Offset into the output buffer to begin storing the digest.
     *
     * @param length
     *         Number of bytes within the output buffer allotted for the digest.
     *
     * @return
     *         The resulting hash value.
     */
    public int digest(byte[] output, int offset, int length) throws DigestException {
        return mMessageDigest.digest(output, offset, length);
    }

    /**
     * Complete the hash computation and get the resulting hash value
     * as a hex string. The digest is reset after this call is made.
     *
     * <p>
     * This method calls {@link #digest()} method and converts the result
     * to a String object.
     * </p>
     *
     * @return
     *         The result hash value represented in a hex String.
     */
    public String digestAsString() {
        return bytesToHex(digest());
    }

    /**
     * Perform the final update with the given byte array, and then
     * complete the hash computation and get the resulting hash value
     * as a hex string. The digest is reset after this call is made.
     *
     * <p>
     * This method calls {@link #digest(byte[])} method and converts
     * the result to a String object.
     * </p>
     *
     * @param input
     *         Byte array used for the last update.
     *
     * @return
     *         The result hash value represented in a hex String.
     */
    public String digestAsString(byte[] input) {
        return bytesToHex(digest(input));
    }

    /**
     * Complete the hash computation and get the resulting hash value
     * as a string. The given encoder is used to convert the digest
     * value to a string.
     *
     * <p>
     * This method is an alias of {@link #digestAsString(byte[],
     * BinaryEncoder) digestAsString((byte[])null, encoder)}.
     * </p>
     *
     * @param encoder
     *         Encoder to convert a digest value to a byte array
     *         whose elements are printable characters. For example,
     *         {@link org.apache.commons.codec.binary.Base64}.
     *
     * @return
     *         The result hash value encoded by the encoder.
     *
     * @throws RuntimeException
     *         If the encoder throws {@link EncoderException},
     *         a {@code RuntimeException} wrapping the
     *         {@code EncoderException} is thrown.
     *
     * @since 1.3
     */
    public String digestAsString(BinaryEncoder encoder) {
        return digestAsString((byte[]) null, encoder);
    }

    /**
     * Perform the final update with the given byte array, and then
     * complete the hash computation and get the resulting hash value
     * as a string. The given encoder is used to convert the digest
     * value to a string.
     *
     * @param input
     *         Byte array used for the last update. If {@code null}
     *         is given, it is just ignored.
     *
     * @param encoder
     *         Encoder to convert a digest value to a byte array
     *         whose elements are printable characters. For example,
     *         {@link org.apache.commons.codec.binary.Base64}.
     *
     * @return
     *         The result hash value encoded by the encoder.
     *
     * @throws IllegalArgumentException
     *         {@code encoder} is {@code null}.
     *
     * @throws RuntimeException
     *         If the encoder throws {@link EncoderException},
     *         a {@code RuntimeException} wrapping the
     *         {@code EncoderException} is thrown.
     *
     * @since 1.3
     */
    public String digestAsString(byte[] input, BinaryEncoder encoder) {
        if (encoder == null) {
            throw new IllegalArgumentException("encoder is null.");
        }

        // Compute the digest value.
        byte[] digest = (input != null) ? digest(input) : digest();

        // Encoded value.
        byte[] encoded = null;

        try {
            // Encode the digest value.
            encoded = encoder.encode(digest);
        } catch (EncoderException e) {
            // Failed to encode the digest value.
            throw new RuntimeException("Failed to encode the digest value.", e);
        }

        try {
            // Convert the byte array into a string.
            return new String(encoded, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // This won't happen.
            return null;
        }
    }

    /**
     * Reset the wrapped {@code MessageDigest} instance.
     *
     * @return
     *         {@code this} object.
     */
    public Digest reset() {
        mMessageDigest.reset();

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link MessageDigest#update(byte) update(byte)}
     * method of the wrapped {@code MessageDigest} instance.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(byte input) {
        mMessageDigest.update(input);

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link MessageDigest#update(byte[]) update(byte[])}
     * method of the wrapped {@code MessageDigest} instance.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(byte[] input) {
        mMessageDigest.update(input);

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link MessageDigest#update(byte[],int,int)
     * update(byte[], int, int)} method of the wrapped
     * {@code MessageDigest} instance.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(byte[] input, int offset, int length) {
        mMessageDigest.update(input, offset, length);

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link MessageDigest#update(ByteBuffer)
     * update(ByteBuffer)} method of the wrapped {@code MessageDigest}
     * instance.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(ByteBuffer input) {
        mMessageDigest.update(input);

        return this;
    }

    /**
     * Check the validity of the combination of the given parameters.
     *
     * @param size
     *         Size of an array.
     *
     * @param offset
     *         Offset in the array.
     *
     * @param length
     *         Length of data to use.
     *
     * @throws IllegalArgumentException
     * <ul>
     * <li>{@code offset < 0}
     * <li>{@code size <= offset}
     * <li>{@code length < 0}
     * <li>{@code size < (offset + length)}
     * </ul>
     */
    private void checkRange(int size, int offset, int length) {
        String message = null;

        if (offset < 0) {
            message = "The offset is less than 0.";
        } else if (size <= offset) {
            message = "The offset is equal to or greater than the size.";
        } else if (length < 0) {
            message = "The length is less than 0.";
        } else if (size < (offset + length)) {
            message = "The sum of the offset and the length is greater than the size.";
        }

        if (message != null) {
            throw new IllegalArgumentException(message);
        }
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(boolean)
     * update}{@code (input.booleanValue())}.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.4
     */
    public Digest update(Boolean input) {
        if (input == null) {
            return this;
        }

        return update(input.booleanValue());
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(Boolean[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.4
     */
    public Digest update(Boolean[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(Boolean)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     *
     * @since 1.4
     */
    public Digest update(Boolean[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * @param input
     *         Input data. {@code true} results in {@link #update(byte)
     *         update}{@code ((byte)1)} and {@code false} results in
     *         {@link #update(byte) update}{@code ((byte)0)}.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(boolean input) {
        return update((byte) (input ? 1 : 0));
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(boolean[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(boolean[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(boolean)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(boolean[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(char)
     * update}{@code (input.charValue())}.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.4
     */
    public Digest update(Character input) {
        if (input == null) {
            return this;
        }

        return update(input.charValue());
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(Character[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.4
     */
    public Digest update(Character[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(Character)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     *
     * @since 1.4
     */
    public Digest update(Character[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(byte) update}{@code
     * ((byte)((input >> 8) & 0xff))} and {@link #update(byte)
     * update}({@code ((byte)(input >> 0) & 0xff)}.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(char input) {
        update((byte) ((input >> 8) & 0xff));
        update((byte) ((input >> 0) & 0xff));

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(char[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(char[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(char)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(char[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(byte) update}{@code
     * ((byte)((input >> 8) & 0xff))} and {@link #update(byte)
     * update}({@code ((byte)(input >> 0) & 0xff)}.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(short input) {
        update((byte) ((input >> 8) & 0xff));
        update((byte) ((input >> 0) & 0xff));

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(short[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(short[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(short)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(short[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(byte)} for each byte of the
     * 4 bytes from MSB to LSB (from {@code ((input >> 24) & 0xff)}
     * to {@code ((input >> 0) & 0xff)}).
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(int input) {
        update((byte) ((input >> 24) & 0xff));
        update((byte) ((input >> 16) & 0xff));
        update((byte) ((input >> 8) & 0xff));
        update((byte) ((input >> 0) & 0xff));

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(int[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(int[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(int)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(int[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(byte)} for each byte of the
     * 8 bytes from MSB to LSB (from {@code ((input >> 54) & 0xff)}
     * to {@code ((input >> 0) & 0xff)}).
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(long input) {
        update((byte) ((input >> 54) & 0xff));
        update((byte) ((input >> 48) & 0xff));
        update((byte) ((input >> 40) & 0xff));
        update((byte) ((input >> 32) & 0xff));
        update((byte) ((input >> 24) & 0xff));
        update((byte) ((input >> 16) & 0xff));
        update((byte) ((input >> 8) & 0xff));
        update((byte) ((input >> 0) & 0xff));

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(long[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(long[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(long)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(long[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method converts the given {@code float} value to a
     * {@code int} by {@link Float#floatToRawIntBits(float)}
     * and then passes it to {@link #update(int)}.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(float input) {
        return update(Float.floatToRawIntBits(input));
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(float[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(float[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(float)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(float[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method converts the given {@code double} value to a
     * {@code long} by {@link Double#doubleToRawLongBits(double)}
     * and then passes it to {@link #update(long)}.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(double input) {
        return update(Double.doubleToRawLongBits(input));
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(double[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(double[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(double)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(double[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method checks the class of the given instance and
     * calls a corresponding {@code update} method.
     * </p>
     *
     * <table border="1" cellpadding="5" style="margin: 1em; border-collapse: collapse;">
     *   <tr bgcolor="orange">
     *     <th>Class</th>
     *     <th>Executed code</th>
     *   </tr>
     *   <tr>
     *     <td>{@code Byte}</td>
     *     <td>{@link #update(byte) update}{@code (((Byte)number).byteValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code Short}</td>
     *     <td>{@link #update(short) update}{@code (((Short)number).shortValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code Integer}</td>
     *     <td>{@link #update(int) update}{@code (((Integer)number).intValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code Long}</td>
     *     <td>{@link #update(long) update}{@code (((Long)number).longValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code Float}</td>
     *     <td>{@link #update(float) update}{@code (((Float)number).floatValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code Double}</td>
     *     <td>{@link #update(double) update}{@code (((Double)number).doubleValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code BigInteger}</td>
     *     <td>{@link #update(byte[]) update}{@code (((BigInteger)number).toByteArray())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code BigDecimal}</td>
     *     <td>{@link #update(String) update}{@code (((BigDecimal)number).toString())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code AtomicInteger}</td>
     *     <td>{@link #update(int) update}{@code (((AtomicInteger)number).intValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>{@code AtomicLong}</td>
     *     <td>{@link #update(long) update}{@code (((AtomicLong)number).longValue())}</td>
     *   </tr>
     *   <tr>
     *     <td>Others</td>
     *     <td>Ignored.</td>
     *   </tr>
     * </table>
     *
     * @param number
     *         Input data. If null or none of the above, update is not performed.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.1
     */
    public Digest update(Number number) {
        if (number == null) {
            return this;
        }

        // Byte
        if (number instanceof Byte) {
            update(((Byte) number).byteValue());
        }
        // Short
        else if (number instanceof Short) {
            update(((Short) number).shortValue());
        }
        // Integer
        else if (number instanceof Integer) {
            update(((Integer) number).intValue());
        }
        // Long
        else if (number instanceof Long) {
            update(((Long) number).longValue());
        }
        // Float
        else if (number instanceof Float) {
            update(((Float) number).floatValue());
        }
        // Double
        else if (number instanceof Double) {
            update(((Double) number).doubleValue());
        }
        // BigInteger
        else if (number instanceof BigInteger) {
            update(((BigInteger) number).toByteArray());
        }
        // BigDecimal
        else if (number instanceof BigDecimal) {
            update(((BigDecimal) number).toString());
        }
        // AtomicInteger
        else if (number instanceof AtomicInteger) {
            update(((AtomicInteger) number).intValue());
        }
        // AtomicLong
        else if (number instanceof AtomicLong) {
            update(((AtomicLong) number).longValue());
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(Number[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.1
     */
    public <TNumber extends Number> Digest update(TNumber[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(Number)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     *
     * @since 1.1
     */
    public <TNumber extends Number> Digest update(TNumber[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method converts the given string into bytes with the
     * character set of UTF-8 and then passes the byte array to
     * {@link #update(byte[])}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(String input) {
        if (input == null) {
            return this;
        }

        try {
            return update(input.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This won't happen.
            return this;
        }
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method is an alias of {@link #update(String[], int, int)
     * update}{@code (input, 0, input.length)}.
     * </p>
     *
     * @param input
     *         Input data. If null is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     */
    public Digest update(String[] input) {
        if (input == null) {
            return this;
        }

        return update(input, 0, input.length);
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(String)} for each
     * array element which is in the specified range.
     * </p>
     *
     * @param input
     *         Input data.
     *
     * @param offset
     *         The offset to start from in the array.
     *
     * @param length
     *         The number of elements to use, starting at offset.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IllegalArgumentException
     *         The range specified by the parameters is invalid.
     */
    public Digest update(String[] input, int offset, int length) {
        if (input == null) {
            return this;
        }

        checkRange(input.length, offset, length);

        for (int i = 0; i < length; ++i) {
            update(input[i + offset]);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(Object)} for each element.
     * </p>
     *
     * @param input
     *         Input data. If {@code null} is given, update is not performed.
     *         {@code null} elements are ignored. Elements of unsupported
     *         classes are ignored, too.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.1
     */
    public Digest update(Iterable<?> input) {
        if (input == null) {
            return this;
        }

        for (Object element : input) {
            if (element == null) {
                continue;
            }

            update(element);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method calls {@link #update(Object)} for each element.
     * </p>
     *
     * @param input
     *         Input data. If {@code null} is given, update is not performed.
     *         {@code null} elements are ignored. Elements of unsupported
     *         classes are ignored, too.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.4
     */
    public Digest update(Object... input) {
        if (input == null) {
            return this;
        }

        for (Object element : input) {
            if (element == null) {
                continue;
            }

            update(element);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given input data.
     *
     * <p>
     * This method checks the class of the given object and calls
     * a corresponding {@code update} method.
     * </p>
     *
     * @param input
     *         Input data. If {@code null} is given, update is not performed.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.4
     */
    public Digest update(Object input) {
        if (input == null) {
            return this;
        }

        if (input instanceof String) {
            update((String) input);
        } else if (input instanceof String[]) {
            update((String[]) input);
        } else if (input instanceof Character) {
            update((Character) input);
        } else if (input instanceof Character[]) {
            update((Character[]) input);
        } else if (input instanceof Boolean) {
            update((Boolean) input);
        } else if (input instanceof boolean[]) {
            update((boolean[]) input);
        } else if (input instanceof Boolean[]) {
            update((Boolean[]) input);
        } else if (input instanceof Number) {
            update((Number) input);
        } else if (input instanceof byte[]) {
            update((byte[]) input);
        } else if (input instanceof Byte[]) {
            update((Byte[]) input);
        } else if (input instanceof ByteBuffer) {
            update((ByteBuffer) input);
        } else if (input instanceof char[]) {
            update((char[]) input);
        } else if (input instanceof double[]) {
            update((double[]) input);
        } else if (input instanceof Double[]) {
            update((Double[]) input);
        } else if (input instanceof float[]) {
            update((float[]) input);
        } else if (input instanceof Float[]) {
            update((Float[]) input);
        } else if (input instanceof int[]) {
            update((int[]) input);
        } else if (input instanceof Integer[]) {
            update((Integer[]) input);
        } else if (input instanceof long[]) {
            update((long[]) input);
        } else if (input instanceof Long[]) {
            update((Long[]) input);
        } else if (input instanceof short[]) {
            update((short[]) input);
        } else if (input instanceof Short[]) {
            update((Short[]) input);
        } else if (input instanceof Iterable<?>) {
            update((Iterable<?>) input);
        }

        return this;
    }

    /**
     * Update the wrapped {@code MessageDigest} object with the
     * given JSON. This method updates the digest based on the
     * content of the given JSON, and in the respect, this method
     * is different from {@link #update(String)}.
     *
     * <p>
     * JSONs with the same content, for example, two JSONs below,
     * generate the same digest.
     * </p>
     *
     * <pre>
     * { "key1":"value1", "key2":"value2" }
     * { "key1" : "value1" , "key2" : "value2" }
     * </pre>
     *
     * <p>
     * If {@link Feature#IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL}
     * is enabled (it is disabled by default), key-value entries
     * with value 'null' are treated as if they did not exist.
     * Therefore, two JSONs below generate the same digest value.
     * </p>
     *
     * <pre>
     * { "key1":"value1", "key2":null }
     * { "key1":"value1" }
     * </pre>
     *
     * <p>
     * If {@link Feature#SORT_JSON_OBJECT_ENTRY_KEYS} is enabled
     * (it is enabled by default), orders of JSON object keys do
     * not matter. Therefore, two JSONs below generate the same
     * digest value.
     * </p>
     *
     * <pre>
     * { "key1":"value1", "key2":"value2" }
     * { "key2":"value2", "key1":"value1" }
     * </pre>
     *
     * @param json
     *         JSON.
     *
     * @return
     *         {@code this} object.
     *
     * @throws IOException
     *         Failed to parse the given JSON.
     *
     * @since 1.2
     */
    public Digest updateJson(String json) throws IOException {
        return new JsonDigestUpdater().update(this, json);
    }

    /**
     * Check if the specified feature is enabled.
     *
     * @param feature
     *         Feature to check.
     *
     * @return
     *         {@code true} if the feature is enabled. Otherwise, {@code false}.
     *
     * @since 1.2
     */
    public boolean isEnabled(Feature feature) {
        return mFeatures.get(feature).booleanValue();
    }

    /**
     * Enable or disable the specified feature.
     *
     * @param feature
     *         {@link Feature} to enable or disable.
     *
     * @param enabled
     *         {@code true} to enable the feature.
     *         {@code false} to disable the feature.
     *
     * @return
     *         {@code this} object.
     *
     * @since 1.2
     */
    public Digest setEnabled(Feature feature, boolean enabled) {
        mFeatures.put(feature, Boolean.valueOf(enabled));

        return this;
    }

    /**
     * Convert the given byte array to a hex string.
     *
     * @param bytes
     *
     * @return
     *         A hex string with 0-9 and a-f.
     */
    public static String bytesToHex(byte[] bytes) {
        // http://stackoverflow.com/a/9855338/1174054
        char[] hexChars = new char[bytes.length * 2];
        int v;

        for (int j = 0; j < bytes.length; ++j) {
            v = bytes[j] & 0xFF;
            hexChars[j * 2] = mHexArray[v >>> 4];
            hexChars[j * 2 + 1] = mHexArray[v & 0x0F];
        }

        return new String(hexChars);
    }
}