private String[] getEntityDetails(String filename, List<JarEntity> list) throws Sha1Exception {
    ArrayList<String> result = new ArrayList<String>();
    result.add(
        String.format("Found %d versions of %s in the dependency list,", list.size(), filename));
    result.add(
        "but not all the versions are identical (check is based on SHA-1 only at this time).");
    result.add("All versions of the libraries must be the same at this time.");
    result.add("Versions found are:");
    for (JarEntity entity : list) {
      result.add("Path: " + entity.getFile().getAbsolutePath());
      result.add("\tLength: " + entity.getLength());
      result.add("\tSHA-1: " + entity.getSha1());
    }

    return result.toArray(new String[result.size()]);
  }
  private void writeJarList(Map<String, List<JarEntity>> nameMap) {
    File cacheFile = new File(mOut, CACHE_FILENAME);
    OutputStreamWriter writer = null;
    try {
      writer = new OutputStreamWriter(new FileOutputStream(cacheFile), "UTF-8");

      writer.write("# cache for current jar dependency. DO NOT EDIT.\n");
      writer.write("# format is <lastModified> <length> <SHA-1> <path>\n");
      writer.write("# Encoding is UTF-8\n");

      for (List<JarEntity> list : nameMap.values()) {
        // clean up the list of files that don't have a sha1.
        for (int i = 0; i < list.size(); ) {
          JarEntity entity = list.get(i);
          if (entity.hasSha1()) {
            i++;
          } else {
            list.remove(i);
          }
        }

        if (list.size() > 1) {
          for (JarEntity entity : list) {
            writer.write(
                String.format(
                    "%d %d %s %s\n",
                    entity.getLastModified(),
                    entity.getLength(),
                    entity.getSha1(),
                    entity.getFile().getAbsolutePath()));
          }
        }
      }
    } catch (IOException e) {
      mOutStream.println(
          "WARNING: unable to write jarlist cache file " + cacheFile.getAbsolutePath());
    } catch (Sha1Exception e) {
      // shouldn't happen here since we check that the sha1 is present first, meaning it's
      // already been computing.
    } finally {
      if (writer != null) {
        try {
          writer.close();
        } catch (IOException e) {
        }
      }
    }
  }
  /**
   * Checks whether a given list of duplicates can be replaced by a single one.
   *
   * @param filename the filename of the files
   * @param list the list of dup files
   * @throws DifferentLibException
   * @throws Sha1Exception
   */
  private void checkEntities(String filename, List<JarEntity> list)
      throws DifferentLibException, Sha1Exception {
    if (list.size() == 1) {
      return;
    }

    JarEntity baseEntity = list.get(0);
    long baseLength = baseEntity.getLength();
    String baseSha1 = baseEntity.getSha1();

    final int count = list.size();
    for (int i = 1; i < count; i++) {
      JarEntity entity = list.get(i);
      if (entity.getLength() != baseLength || entity.getSha1().equals(baseSha1) == false) {
        throw new DifferentLibException(
            "Jar mismatch! Fix your dependencies", getEntityDetails(filename, list));
      }
    }
  }
  /**
   * Sanitize a given list of files
   *
   * @param files the list to sanitize
   * @return a new list containing no duplicates.
   * @throws DifferentLibException
   * @throws Sha1Exception
   */
  public List<File> sanitize(Collection<File> files) throws DifferentLibException, Sha1Exception {
    List<File> results = new ArrayList<File>();

    // get the cache list.
    Map<String, JarEntity> jarList = getCachedJarList();

    boolean updateJarList = false;

    // clean it up of removed files.
    // use results as a temp storage to store the files to remove as we go through the map.
    for (JarEntity entity : jarList.values()) {
      if (entity.getFile().exists() == false) {
        results.add(entity.getFile());
      }
    }

    // the actual clean up.
    if (results.size() > 0) {
      for (File f : results) {
        jarList.remove(f.getAbsolutePath());
      }

      results.clear();
      updateJarList = true;
    }

    Map<String, List<JarEntity>> nameMap = new HashMap<String, List<JarEntity>>();

    // update the current jar list if needed, while building a secondary map based on
    // filename only.
    for (File file : files) {
      String path = file.getAbsolutePath();
      JarEntity entity = jarList.get(path);

      if (entity == null) {
        entity = new JarEntity(file);
        jarList.put(path, entity);
        updateJarList = true;
      } else {
        updateJarList |= entity.checkValidity();
      }

      String filename = file.getName();
      List<JarEntity> nameList = nameMap.get(filename);
      if (nameList == null) {
        nameList = new ArrayList<JarEntity>();
        nameMap.put(filename, nameList);
      }
      nameList.add(entity);
    }

    try {
      // now look for duplicates. Each name list can have more than one file but they must
      // have the same size/sha1
      for (Entry<String, List<JarEntity>> entry : nameMap.entrySet()) {
        List<JarEntity> list = entry.getValue();
        checkEntities(entry.getKey(), list);

        // if we are here, there's no issue. Add the first of the list to the results.
        results.add(list.get(0).getFile());
      }

      // special case for android-support-v4/13
      checkSupportLibs(nameMap, results);
    } finally {
      if (updateJarList) {
        writeJarList(nameMap);
      }
    }

    return results;
  }