001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  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,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.spi.connector.checksum;
020
021import java.io.BufferedInputStream;
022import java.io.ByteArrayInputStream;
023import java.io.File;
024import java.io.IOException;
025import java.io.InputStream;
026import java.nio.ByteBuffer;
027import java.nio.file.Files;
028import java.nio.file.Path;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032
033/**
034 * Helper for checksum operations.
035 *
036 * @since 1.8.0
037 */
038public final class ChecksumAlgorithmHelper {
039    private ChecksumAlgorithmHelper() {
040        // nop
041    }
042
043    /**
044     * Calculates checksums for specified data.
045     *
046     * @param data        The content for which to calculate checksums, must not be {@code null}.
047     * @param factories   The checksum algorithm factories to use, must not be {@code null}.
048     * @return The calculated checksums, indexed by algorithm name, or the exception that occurred while trying to
049     * calculate it, never {@code null}.
050     * @throws IOException In case of any problem.
051     */
052    public static Map<String, String> calculate(byte[] data, List<ChecksumAlgorithmFactory> factories)
053            throws IOException {
054        try (InputStream inputStream = new ByteArrayInputStream(data)) {
055            return calculate(inputStream, factories);
056        }
057    }
058
059    /**
060     * Calculates checksums for specified file.
061     *
062     * @param file        The file for which to calculate checksums, must not be {@code null}.
063     * @param factories   The checksum algorithm factories to use, must not be {@code null}.
064     * @return The calculated checksums, indexed by algorithm name, or the exception that occurred while trying to
065     * calculate it, never {@code null}.
066     * @throws IOException In case of any problem.
067     */
068    public static Map<String, String> calculate(File file, List<ChecksumAlgorithmFactory> factories)
069            throws IOException {
070        return calculate(file.toPath(), factories);
071    }
072
073    /**
074     * Calculates checksums for specified file.
075     *
076     * @param path        The file for which to calculate checksums, must not be {@code null}.
077     * @param factories   The checksum algorithm factories to use, must not be {@code null}.
078     * @return The calculated checksums, indexed by algorithm name, or the exception that occurred while trying to
079     * calculate it, never {@code null}.
080     * @throws IOException In case of any problem.
081     * @since 2.0.0
082     */
083    public static Map<String, String> calculate(Path path, List<ChecksumAlgorithmFactory> factories)
084            throws IOException {
085        try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(path))) {
086            return calculate(inputStream, factories);
087        }
088    }
089
090    private static Map<String, String> calculate(InputStream inputStream, List<ChecksumAlgorithmFactory> factories)
091            throws IOException {
092        LinkedHashMap<String, ChecksumAlgorithm> algorithms = new LinkedHashMap<>();
093        factories.forEach(f -> algorithms.put(f.getName(), f.getAlgorithm()));
094        final byte[] buffer = new byte[1024 * 32];
095        for (; ; ) {
096            int read = inputStream.read(buffer);
097            if (read < 0) {
098                break;
099            }
100            for (ChecksumAlgorithm checksumAlgorithm : algorithms.values()) {
101                checksumAlgorithm.update(ByteBuffer.wrap(buffer, 0, read));
102            }
103        }
104        LinkedHashMap<String, String> result = new LinkedHashMap<>();
105        algorithms.forEach((k, v) -> result.put(k, v.checksum()));
106        return result;
107    }
108}