| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.chemistry.opencmis.commons.impl.dataobjects; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Locale; |
| |
| import org.apache.chemistry.opencmis.commons.data.ContentStreamHash; |
| |
| public class ContentStreamHashImpl implements ContentStreamHash { |
| |
| public static final String ALGORITHM_MD5 = "md5"; |
| public static final String ALGORITHM_SHA1 = "sha-1"; |
| public static final String ALGORITHM_SHA224 = "sha-224"; |
| public static final String ALGORITHM_SHA256 = "sha-256"; |
| public static final String ALGORITHM_SHA384 = "sha-384"; |
| public static final String ALGORITHM_SHA512 = "sha-512"; |
| public static final String ALGORITHM_SHA3 = "sha-3"; |
| |
| private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); |
| |
| private String propertyValue; |
| private String algorithm = null; |
| private String hash = null; |
| |
| /** |
| * Constructs an object from the {@code cmis:contentStreamHash} property |
| * value. |
| * |
| * @param propertyValue |
| * the property value |
| */ |
| public ContentStreamHashImpl(String propertyValue) { |
| this.propertyValue = propertyValue; |
| |
| if (propertyValue == null) { |
| return; |
| } |
| |
| String pv = propertyValue.trim(); |
| int algEnd = pv.indexOf('}'); |
| if (pv.charAt(0) != '{' || algEnd < 1) { |
| return; |
| } |
| |
| this.algorithm = pv.substring(1, algEnd).toLowerCase(Locale.ENGLISH); |
| this.hash = pv.substring(algEnd + 1).replaceAll("\\s", "").toLowerCase(Locale.ENGLISH); |
| } |
| |
| /** |
| * Constructs an object from the algorithm and hash. |
| * |
| * @param algorithm |
| * the algorithm |
| * @param hash |
| * the hash value |
| */ |
| public ContentStreamHashImpl(String algorithm, String hashStr) { |
| if (algorithm == null || algorithm.trim().length() == 0) { |
| throw new IllegalArgumentException("Algorithm must be set!"); |
| } |
| |
| if (hashStr == null || hashStr.trim().length() == 0) { |
| throw new IllegalArgumentException("Hash must be set!"); |
| } |
| |
| this.algorithm = algorithm.toLowerCase(Locale.ENGLISH); |
| this.hash = hashStr.replaceAll("\\s", "").toLowerCase(Locale.ENGLISH); |
| this.propertyValue = "{" + this.algorithm + "}" + this.hash; |
| } |
| |
| /** |
| * Constructs an object from the algorithm and hash. |
| * |
| * @param algorithm |
| * the algorithm |
| * @param hashBytes |
| * the hash value as byte array |
| */ |
| public ContentStreamHashImpl(String algorithm, byte[] hashBytes) { |
| if (algorithm == null || algorithm.trim().length() == 0) { |
| throw new IllegalArgumentException("Algorithm must be set!"); |
| } |
| |
| if (hashBytes == null || hashBytes.length == 0) { |
| throw new IllegalArgumentException("Hash must be set!"); |
| } |
| |
| this.algorithm = algorithm.toLowerCase(Locale.ENGLISH); |
| this.hash = byteArrayToHexString(hashBytes); |
| this.propertyValue = "{" + this.algorithm + "}" + this.hash; |
| } |
| |
| public String getPropertyValue() { |
| return propertyValue; |
| } |
| |
| public String getAlgorithm() { |
| return algorithm; |
| } |
| |
| public String getHash() { |
| return hash; |
| } |
| |
| @Override |
| public int hashCode() { |
| return propertyValue.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| |
| ContentStreamHashImpl other = (ContentStreamHashImpl) obj; |
| if (propertyValue == null) { |
| if (other.propertyValue != null) { |
| return false; |
| } |
| } else if (!propertyValue.equals(other.propertyValue)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public String toString() { |
| return propertyValue; |
| } |
| |
| /** |
| * Creates a list of content hashes from a stream |
| * <p> |
| * This method consumes the stream but doesn't close it. |
| * |
| * @param stream |
| * the stream |
| * @param algorithm |
| * the algorithms |
| * @return the list of content hashes |
| */ |
| public static List<ContentStreamHash> createContentStreamHashes(InputStream stream, String... algorithm) |
| throws IOException, NoSuchAlgorithmException { |
| if (stream == null) { |
| throw new IllegalArgumentException("Stream must be set!"); |
| } |
| |
| if (algorithm == null || algorithm.length == 0) { |
| throw new IllegalArgumentException("Algorithm must be set!"); |
| } |
| |
| MessageDigest[] md = new MessageDigest[algorithm.length]; |
| for (int i = 0; i < algorithm.length; i++) { |
| md[i] = MessageDigest.getInstance(algorithm[i]); |
| } |
| |
| int b; |
| byte[] buffer = new byte[64 * 1024]; |
| while ((b = stream.read(buffer)) > -1) { |
| for (int j = 0; j < md.length; j++) { |
| md[j].update(buffer, 0, b); |
| } |
| } |
| |
| List<ContentStreamHash> result = new ArrayList<ContentStreamHash>(); |
| |
| for (int i = 0; i < md.length; i++) { |
| result.add(new ContentStreamHashImpl(algorithm[i], md[i].digest())); |
| } |
| |
| return result; |
| } |
| |
| protected static String byteArrayToHexString(byte[] bytes) { |
| int n = bytes.length; |
| char[] hashHex = new char[n * 2]; |
| for (int i = 0; i < n; i++) { |
| hashHex[i * 2] = HEX_DIGITS[(0xF0 & bytes[i]) >>> 4]; |
| hashHex[i * 2 + 1] = HEX_DIGITS[0x0F & bytes[i]]; |
| } |
| |
| return new String(hashHex); |
| } |
| } |