001/**
002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC
003 * and GZip files. (http://jwat.org/)
004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.jwat.common;
019
020import java.security.MessageDigest;
021import java.security.NoSuchAlgorithmException;
022import java.util.Arrays;
023import java.util.Map;
024import java.util.TreeMap;
025import java.util.zip.CRC32;
026import java.util.zip.Checksum;
027
028/**
029 * This class represents a message digest including information about algorithm
030 * and encoding used.
031 *
032 * @author nicl
033 */
034public class Digest {
035
036    /** Digest algorithm used. */
037    public String algorithm;
038
039    /** Digest in bytes as returned by e.g. <code>MessageDigest</code>. */
040    public byte[] digestBytes;
041
042    /** Digest string, encoded. */
043    public String digestString;
044
045    /** Digest encoding used. (E.g. Base16, 32 or 64) */
046    public String encoding;
047
048    /** Cache length of algorithm digest output. */
049    protected static Map<String, Integer> digestAlgoLengthache = new TreeMap<String, Integer>();
050
051    /**
052     * Returns the length of an algorithms digest output or -1 if it is an
053     * invalid digest algorithm.
054     * @param digestAlgorithm digest algorithm
055     * @return digest algorithm output length or -1 if invalid digest algorithm
056     */
057    public static synchronized int digestAlgorithmLength(String digestAlgorithm) {
058        if (digestAlgorithm == null || digestAlgorithm.length() == 0) {
059            throw new IllegalArgumentException("'digestAlgorithm' is empty or null");
060        }
061        Integer cachedLen = digestAlgoLengthache.get(digestAlgorithm);
062        if (cachedLen == null) {
063            try {
064                MessageDigest md = MessageDigest.getInstance(digestAlgorithm);
065                byte[] digest = md.digest(new byte[16]);
066                cachedLen = digest.length;
067                md.reset();
068            } catch (NoSuchAlgorithmException e) {
069            }
070            if (cachedLen == null) {
071                cachedLen = -1;
072            }
073            digestAlgoLengthache.put(digestAlgorithm,  cachedLen);
074        }
075        return cachedLen;
076    }
077
078    @Override
079    public boolean equals(Object obj) {
080        if (obj == null || !(obj instanceof Digest)) {
081            return false;
082        }
083        Digest digestObj = (Digest)obj;
084        if (!Arrays.equals(digestBytes, digestObj.digestBytes)) {
085            return false;
086        }
087        if (algorithm != null) {
088            if (!algorithm.equals(digestObj.algorithm)) {
089                return false;
090            }
091        } else if (digestObj.algorithm != null) {
092            return false;
093        }
094        if (digestString != null) {
095            if (!digestString.equals(digestObj.digestString)) {
096                return false;
097            }
098        } else if (digestObj.digestString != null) {
099            return false;
100        }
101        if (encoding != null) {
102            if (!encoding.equals(digestObj.encoding)) {
103                return false;
104            }
105        } else if (digestObj.encoding != null) {
106            return false;
107        }
108        return true;
109    }
110
111    @Override
112    public int hashCode() {
113        int hashCode = 0;
114        if (digestBytes != null) {
115            Checksum checksum = new CRC32();
116            checksum.update(digestBytes, 0, digestBytes.length);
117            hashCode ^= checksum.getValue();
118        }
119        if (algorithm != null) {
120            hashCode ^= algorithm.hashCode();
121        }
122        if (digestString != null) {
123            hashCode ^= digestString.hashCode();
124        }
125        if (encoding != null) {
126            hashCode ^= encoding.hashCode();
127        }
128        return hashCode;
129    }
130
131}