144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet/*
244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Copyright (C) 2012 The Android Open Source Project
344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet *
444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Licensed under the Apache License, Version 2.0 (the "License");
544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * you may not use this file except in compliance with the License.
644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * You may obtain a copy of the License at
744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet *
844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet *      http://www.apache.org/licenses/LICENSE-2.0
944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet *
1044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Unless required by applicable law or agreed to in writing, software
1144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * distributed under the License is distributed on an "AS IS" BASIS,
1244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * See the License for the specific language governing permissions and
1444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * limitations under the License.
1544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */
1644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
1744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetpackage com.android.sdklib.build;
1844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
1944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.BufferedReader;
2044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.File;
2144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.FileInputStream;
2244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.FileNotFoundException;
2344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.FileOutputStream;
2444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.IOException;
2544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.InputStreamReader;
2644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.OutputStreamWriter;
2781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohetimport java.io.PrintStream;
2844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.UnsupportedEncodingException;
2944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.security.MessageDigest;
3044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.ArrayList;
3181c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohetimport java.util.Collection;
3244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.Formatter;
3344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.HashMap;
3444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.List;
3544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.Map;
3644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.Map.Entry;
3744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.regex.Matcher;
3844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.regex.Pattern;
3944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
4044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet/**
4144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * A Class to handle a list of jar files, finding and removing duplicates.
4244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet *
4344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Right now duplicates are based on:
4444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * - same filename
4544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * - same length
4644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * - same content: using sha1 comparison.
4744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet *
4844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * The length/sha1 are kept in a cache and only updated if the library is changed.
4944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */
5044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetpublic class JarListSanitizer {
5144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
5244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private static final byte[] sBuffer = new byte[4096];
5344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private static final String CACHE_FILENAME = "jarlist.cache";
5444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private static final Pattern READ_PATTERN = Pattern.compile("^(\\d+) (\\d+) ([0-9a-f]+) (.+)$");
5544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
5644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
5744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Simple class holding the data regarding a jar dependency.
5844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     *
5944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
6044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private static final class JarEntity {
6144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private final File mFile;
6244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private final long mLastModified;
6344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private long mLength;
6444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private String mSha1;
6544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
6644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        /**
6744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * Creates an entity from cached data.
6844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @param path the file path
6944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @param lastModified when it was last modified
7044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @param length its length
7144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @param sha1 its sha1
7244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         */
7344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private JarEntity(String path, long lastModified, long length, String sha1) {
7444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mFile = new File(path);
7544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mLastModified = lastModified;
7644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mLength = length;
7744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mSha1 = sha1;
7844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
7944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
8044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        /**
8144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * Creates an entity from a {@link File}.
8244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @param file the file.
8344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         */
8444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private JarEntity(File file) {
8544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mFile = file;
8644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mLastModified = file.lastModified();
8744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mLength = file.length();
8844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
8944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
9044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        /**
9144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * Checks whether the {@link File#lastModified()} matches the cached value. If not, length
9244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * is updated and the sha1 is reset (but not recomputed, this is done on demand).
9344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @return return whether the file was changed.
9444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         */
9544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private boolean checkValidity() {
9644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (mLastModified != mFile.lastModified()) {
9744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                mLength = mFile.length();
9844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                mSha1 = null;
9944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                return true;
10044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
10144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
10244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return false;
10344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
10444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
10544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private File getFile() {
10644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return mFile;
10744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
10844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
10944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private long getLastModified() {
11044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return mLastModified;
11144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
11244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
11344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private long getLength() {
11444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return mLength;
11544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
11644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
11744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        /**
11844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * Returns the file's sha1, computing it if necessary.
11944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @return the sha1
12044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         * @throws Sha1Exception
12144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet         */
12244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private String getSha1() throws Sha1Exception {
12344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (mSha1 == null) {
12444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                mSha1 = JarListSanitizer.getSha1(mFile);
12544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
12644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return mSha1;
12744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
12844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
12944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private boolean hasSha1() {
13044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return mSha1 != null;
13144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
13244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
13344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
13444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
13544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Exception used to indicate the sanitized list of jar dependency cannot be computed due
13644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * to inconsistency in duplicate jar files.
13744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
13844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    public static final class DifferentLibException extends Exception {
13944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private static final long serialVersionUID = 1L;
14081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        private final String[] mDetails;
14144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
14281c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        public DifferentLibException(String message, String[] details) {
14344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            super(message);
14481c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            mDetails = details;
14581c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        }
14681c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet
14781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        public String[] getDetails() {
14881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            return mDetails;
14944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
15044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
15144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
15244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
15344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Exception to indicate a failure to check a jar file's content.
15444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
15544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    public static final class Sha1Exception extends Exception {
15644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private static final long serialVersionUID = 1L;
15744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        private final File mJarFile;
15844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
15944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        public Sha1Exception(File jarFile, Throwable cause) {
16044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            super(cause);
16144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            mJarFile = jarFile;
16244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
16344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
16444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        public File getJarFile() {
16544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return mJarFile;
16644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
16744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
16844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
16944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private final File mOut;
17081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet    private final PrintStream mOutStream;
17144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
17244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
17344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Creates a sanitizer.
17444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @param out the project output where the cache is to be stored.
17544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
17644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    public JarListSanitizer(File out) {
17744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        mOut = out;
17881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        mOutStream = System.out;
17981c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet    }
18081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet
18181c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet    public JarListSanitizer(File out, PrintStream outStream) {
18281c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        mOut = out;
18381c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        mOutStream = outStream;
18444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
18544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
18644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
18744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Sanitize a given list of files
18844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @param files the list to sanitize
18944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @return a new list containing no duplicates.
19044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @throws DifferentLibException
19144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @throws Sha1Exception
19244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
19381c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet    public List<File> sanitize(Collection<File> files) throws DifferentLibException, Sha1Exception {
19444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        List<File> results = new ArrayList<File>();
19544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
19644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        // get the cache list.
19744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        Map<String, JarEntity> jarList = getCachedJarList();
19844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
19944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        boolean updateJarList = false;
20044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
20144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        // clean it up of removed files.
20244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        // use results as a temp storage to store the files to remove as we go through the map.
20344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        for (JarEntity entity : jarList.values()) {
20444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (entity.getFile().exists() == false) {
20544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                results.add(entity.getFile());
20644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
20744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
20844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
20944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        // the actual clean up.
21044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        if (results.size() > 0) {
21144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            for (File f : results) {
21244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                jarList.remove(f.getAbsolutePath());
21344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
21444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
21544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            results.clear();
21644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            updateJarList = true;
21744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
21844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
21944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        Map<String, List<JarEntity>> nameMap = new HashMap<String, List<JarEntity>>();
22044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
22144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        // update the current jar list if needed, while building a 2ndary map based on
22244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        // filename only.
22344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        for (File file : files) {
22444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            String path = file.getAbsolutePath();
22544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            JarEntity entity = jarList.get(path);
22644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
22744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (entity == null) {
22844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                entity = new JarEntity(file);
22944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                jarList.put(path, entity);
23044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                updateJarList = true;
23144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            } else {
23244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                updateJarList |= entity.checkValidity();
23344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
23444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
23544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            String filename = file.getName();
23644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            List<JarEntity> nameList = nameMap.get(filename);
23744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (nameList == null) {
23844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                nameList = new ArrayList<JarEntity>();
23944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                nameMap.put(filename, nameList);
24044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
24144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            nameList.add(entity);
24244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
24344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
24444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        try {
24544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // now look for dups. Each name list can have more than one file but they must
24644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // have the same size/sha1
24744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            for (Entry<String, List<JarEntity>> entry : nameMap.entrySet()) {
24844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                List<JarEntity> list = entry.getValue();
24944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                checkEntities(entry.getKey(), list);
25044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
25144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                // if we are here, there's no issue. Add the first of the list to the results.
25244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                results.add(list.get(0).getFile());
25344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
25444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
25544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // special case for android-support-v4/13
25644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            checkSupportLibs(nameMap, results);
25744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        } finally {
25844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (updateJarList) {
25944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                writeJarList(nameMap);
26044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
26144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
26244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
26344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        return results;
26444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
26544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
26644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
26744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Checks whether a given list of duplicates can be replaced by a single one.
26844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @param filename the filename of the files
26944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @param list the list of dup files
27044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @throws DifferentLibException
27144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @throws Sha1Exception
27244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
27344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private void checkEntities(String filename, List<JarEntity> list)
27444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            throws DifferentLibException, Sha1Exception {
27544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        if (list.size() == 1) {
27644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return;
27744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
27844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
27944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        JarEntity baseEntity = list.get(0);
28044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        long baseLength = baseEntity.getLength();
28144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        String baseSha1 = baseEntity.getSha1();
28244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
28344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        final int count = list.size();
28444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        for (int i = 1; i < count ; i++) {
28544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            JarEntity entity = list.get(i);
28644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (entity.getLength() != baseLength || entity.getSha1().equals(baseSha1) == false) {
28781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet                throw new DifferentLibException("Jar mismatch! Fix your dependencies",
28881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet                        getEntityDetails(filename, list));
28944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
29044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
29144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
29244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
29344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
29444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
29544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Checks for present of both support libraries in v4 and v13. If both are detected,
29644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * v4 is removed from <var>results</var>
29744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @param nameMap the list of jar as a map of (filename, list of files).
29844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @param results the current list of jar file set to be used. it's already been cleaned of
29944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     *           duplicates.
30044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
30144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private void checkSupportLibs(Map<String, List<JarEntity>> nameMap, List<File> results) {
30244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        List<JarEntity> v4 = nameMap.get("android-support-v4.jar");
30344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        List<JarEntity> v13 = nameMap.get("android-support-v13.jar");
30444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
30544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        if (v13 != null && v4 != null) {
30681c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            mOutStream.println("WARNING: Found both android-support-v4 and android-support-v13 in the dependency list.");
30781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            mOutStream.println("Because v13 includes v4, using only v13.");
30844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            results.remove(v4.get(0).getFile());
30944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
31044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
31144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
31244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private Map<String, JarEntity> getCachedJarList() {
31344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        Map<String, JarEntity> cache = new HashMap<String, JarListSanitizer.JarEntity>();
31444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
31544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        File cacheFile = new File(mOut, CACHE_FILENAME);
31644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        if (cacheFile.exists() == false) {
31744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            return cache;
31844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
31944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
32044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        BufferedReader reader = null;
32144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        try {
32244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            reader = new BufferedReader(new InputStreamReader(new FileInputStream(cacheFile),
32344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    "UTF-8"));
32444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
32544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            String line = null;
32644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            while ((line = reader.readLine()) != null) {
32744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                // skip comments
32844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                if (line.charAt(0) == '#') {
32944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    continue;
33044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                }
33144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
33244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                // get the data with a regexp
33344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                Matcher m = READ_PATTERN.matcher(line);
33444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                if (m.matches()) {
33544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    String path = m.group(4);
33644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
33744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    JarEntity entity = new JarEntity(
33844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                            path,
33944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                            Long.parseLong(m.group(1)),
34044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                            Long.parseLong(m.group(2)),
34144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                            m.group(3));
34244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
34344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    cache.put(path, entity);
34444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                }
34544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
34644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
34744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        } catch (FileNotFoundException e) {
34844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // won't happen, we check up front.
34944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        } catch (UnsupportedEncodingException e) {
35044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // shouldn't happen, but if it does, we just won't have a cache.
35144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        } catch (IOException e) {
35244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // shouldn't happen, but if it does, we just won't have a cache.
35344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        } finally {
35444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            if (reader != null) {
35544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                try {
35644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    reader.close();
35744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                } catch (IOException e) {
35844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                }
35944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
36044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
36144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
36244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        return cache;
36344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
36444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
36544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private void writeJarList(Map<String, List<JarEntity>> nameMap) {
36644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        File cacheFile = new File(mOut, CACHE_FILENAME);
367a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet        OutputStreamWriter writer = null;
36844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        try {
369a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet            writer = new OutputStreamWriter(
37044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    new FileOutputStream(cacheFile), "UTF-8");
37144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
37244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            writer.write("# cache for current jar dependecy. DO NOT EDIT.\n");
37344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            writer.write("# format is <lastModified> <length> <SHA-1> <path>\n");
37444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            writer.write("# Encoding is UTF-8\n");
37544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
37644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            for (List<JarEntity> list : nameMap.values()) {
37744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                // clean up the list of files that don't have a sha1.
37844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                for (int i = 0 ; i < list.size() ; ) {
37944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    JarEntity entity = list.get(i);
38044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    if (entity.hasSha1()) {
38144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                        i++;
38244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    } else {
38344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                        list.remove(i);
38444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    }
38544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                }
38644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
38744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                if (list.size() > 1) {
38844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    for (JarEntity entity : list) {
38944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                        writer.write(String.format("%d %d %s %s\n",
39044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                                entity.getLastModified(),
39144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                                entity.getLength(),
39244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                                entity.getSha1(),
39344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                                entity.getFile().getAbsolutePath()));
39444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    }
39544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                }
39644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
39744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        } catch (IOException e) {
39881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            mOutStream.println("WARNING: unable to write jarlist cache file " +
39944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    cacheFile.getAbsolutePath());
40044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        } catch (Sha1Exception e) {
40144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // shouldn't happen here since we check that the sha1 is present first, meaning it's
40244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            // already been computing.
403a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet        } finally {
404a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet            if (writer != null) {
405a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet                try {
406a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet                    writer.close();
407a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet                } catch (IOException e) {
408a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet                }
409a08befd52438d523cb2cd41b53f961785d3872fdXavier Ducrohet            }
41044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
41144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
41244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
41381c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet    private String[] getEntityDetails(String filename, List<JarEntity> list) throws Sha1Exception {
41481c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        ArrayList<String> result = new ArrayList<String>();
41581c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        result.add(
41644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                String.format("Found %d versions of %s in the dependency list,",
41744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                        list.size(), filename));
41881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        result.add("but not all the versions are identical (check is based on SHA-1 only at this time).");
41981c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        result.add("All versions of the libraries must be the same at this time.");
42081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        result.add("Versions found are:");
42144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        for (JarEntity entity : list) {
42281c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            result.add("Path: " + entity.getFile().getAbsolutePath());
42381c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            result.add("\tLength: " + entity.getLength());
42481c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet            result.add("\tSHA-1: " + entity.getSha1());
42544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
42681c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet
42781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet        return result.toArray(new String[result.size()]);
42844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
42944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
43044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    /**
43144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * Computes the sha1 of a file and returns it.
43244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @param f the file to compute the sha1 for.
43344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @return the sha1 value
43444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     * @throws Sha1Exception if the sha1 value cannot be computed.
43544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet     */
43644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private static String getSha1(File f) throws Sha1Exception {
43744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        synchronized (sBuffer) {
438547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye            FileInputStream fis = null;
43944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            try {
44044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                MessageDigest md = MessageDigest.getInstance("SHA-1");
44144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
442547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                fis = new FileInputStream(f);
44344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                while (true) {
44444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    int length = fis.read(sBuffer);
44544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    if (length > 0) {
44644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                        md.update(sBuffer, 0, length);
44744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    } else {
44844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                        break;
44944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                    }
45044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                }
45144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
45244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                return byteArray2Hex(md.digest());
45344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
45444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            } catch (Exception e) {
45544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet                throw new Sha1Exception(f, e);
456547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye            } finally {
457547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                if (fis != null) {
458547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                    try {
459547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                        fis.close();
460547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                    } catch (IOException e) {
461547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                        // ignore
462547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                    }
463547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                }
46444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet            }
46544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
46644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
46744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet
46844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    private static String byteArray2Hex(final byte[] hash) {
46944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        Formatter formatter = new Formatter();
470547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye        try {
471547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye            for (byte b : hash) {
472547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye                formatter.format("%02x", b);
473547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye            }
474547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye            return formatter.toString();
475547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye        } finally {
476547c7761208632134d33eace29c81a4e60cd0a69Tor Norbye            formatter.close();
47744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet        }
47844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet    }
47944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet}
480