/* * This file is part of Fim - File Integrity Manager * * Copyright (C) 2017 Etienne Vrignaud * * Fim is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fim is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Fim. If not, see <http://www.gnu.org/licenses/>. */ package org.fim.model; import org.fim.util.Logger; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Scanner; import static org.atteo.evo.inflector.English.plural; import static org.fim.util.FileUtil.byteCountToDisplaySize; import static org.fim.util.FileUtil.removeFile; public class DuplicateResult { private static final Comparator<DuplicateSet> wastedSpaceDescendingComparator = new WastedSpaceDescendingComparator(); private final Context context; private final List<DuplicateSet> duplicateSets; private long duplicatedFilesCount; private long totalWastedSpace; private long filesRemoved; private long spaceFreed; public DuplicateResult(Context context) { this.context = context; this.duplicateSets = new ArrayList<>(); this.duplicatedFilesCount = 0; this.totalWastedSpace = 0; this.filesRemoved = 0; this.spaceFreed = 0; } public void addDuplicatedFiles(List<FileState> duplicatedFiles) { if (duplicatedFiles.size() > 1) { duplicatedFilesCount += duplicatedFiles.size() - 1; duplicatedFiles.stream() .filter(fileState -> duplicatedFiles.indexOf(fileState) > 0) .forEach(fileState -> totalWastedSpace += fileState.getFileLength()); DuplicateSet duplicateSet = new DuplicateSet(duplicatedFiles); duplicateSets.add(duplicateSet); } } public void sortDuplicateSets() { Collections.sort(duplicateSets, wastedSpaceDescendingComparator); } public DuplicateResult displayAndRemoveDuplicates() { if (context.isVerbose() || context.isRemoveDuplicates()) { for (DuplicateSet duplicateSet : duplicateSets) { manageDuplicateSet(duplicateSet); } } if (filesRemoved == 0) { if (duplicatedFilesCount > 0) { Logger.out.printf("%d duplicate %s, %s of total wasted space%n", duplicatedFilesCount, pluralForLong("file", duplicatedFilesCount), byteCountToDisplaySize(totalWastedSpace)); } else { Logger.out.println("No duplicate file found"); } } else { Logger.out.printf("Removed %d files and freed %s%n", filesRemoved, byteCountToDisplaySize(spaceFreed)); long remainingDuplicates = duplicatedFilesCount - filesRemoved; long remainingWastedSpace = totalWastedSpace - spaceFreed; if (remainingDuplicates > 0) { Logger.out.printf("Still have %d duplicate %s, %s of total wasted space%n", remainingDuplicates, pluralForLong("file", remainingDuplicates), byteCountToDisplaySize(remainingWastedSpace)); } else { Logger.out.println("No duplicate file remains"); } } return this; } private void manageDuplicateSet(DuplicateSet duplicateSet) { int index = duplicateSets.indexOf(duplicateSet) + 1; List<FileState> duplicatedFiles = duplicateSet.getDuplicatedFiles(); long fileLength = duplicatedFiles.get(0).getFileLength(); int duplicateTime = duplicatedFiles.size() - 1; long wastedSpace = duplicateSet.getWastedSpace(); Logger.out.printf("- Duplicate set #%d: duplicated %d %s, %s each, %s of wasted space%n", index, duplicateTime, plural("time", duplicateTime), byteCountToDisplaySize(fileLength), byteCountToDisplaySize(wastedSpace)); if (context.isRemoveDuplicates()) { selectFilesToRemove(duplicatedFiles); } String action; for (FileState fileState : duplicatedFiles) { action = " "; if (fileState.isToRemove() && removeFile(context, context.getRepositoryRootDir(), fileState)) { action = "[-]"; filesRemoved++; spaceFreed += fileState.getFileLength(); } Logger.out.printf(" %s %s%n", action, fileState.getFileName()); } Logger.newLine(); } protected void selectFilesToRemove(List<FileState> duplicatedFiles) { if (context.isAlwaysYes()) { for (FileState fileState : duplicatedFiles) { if (duplicatedFiles.indexOf(fileState) > 0) { fileState.setToRemove(true); } } } else { for (FileState fileState : duplicatedFiles) { int index = duplicatedFiles.indexOf(fileState) + 1; Logger.out.printf(" [%s] %s%n", index, fileState.getFileName()); } while (true) { Logger.out.printf(" Preserve files [1 - %d, all or a, none or n]: ", duplicatedFiles.size()); String inputLine = readInputLine(); if (manageAnswers(duplicatedFiles, inputLine)) { break; } } } } protected boolean manageAnswers(List<FileState> duplicatedFiles, String inputLine) { boolean gotCorrectAnswer = false; for (FileState fileState : duplicatedFiles) { fileState.setToRemove(true); } try (Scanner scanner = new Scanner(inputLine)) { while (scanner.hasNext()) { String answer = scanner.next(); if ("a".equals(answer) || "all".equals(answer)) { for (FileState fileState : duplicatedFiles) { fileState.setToRemove(false); } gotCorrectAnswer = true; break; } else if ("n".equals(answer) || "none".equals(answer)) { gotCorrectAnswer = true; // Add the additional file that we will remove. It was not counted in the initial wasted space. duplicatedFilesCount++; totalWastedSpace += duplicatedFiles.get(0).getFileLength(); break; } int index = safeParseInt(answer); if (index >= 1 && index <= duplicatedFiles.size()) { duplicatedFiles.get(index - 1).setToRemove(false); gotCorrectAnswer = true; } } } return gotCorrectAnswer; } private String readInputLine() { Scanner scanner = new Scanner(System.in); return scanner.nextLine(); } private int safeParseInt(String answer) { try { return Integer.parseInt(answer); } catch (NumberFormatException ex) { return -1; } } public long getDuplicatedFilesCount() { return duplicatedFilesCount; } public long getTotalWastedSpace() { return totalWastedSpace; } public long getFilesRemoved() { return filesRemoved; } public long getSpaceFreed() { return spaceFreed; } public List<DuplicateSet> getDuplicateSets() { return duplicateSets; } private String pluralForLong(String word, long count) { return plural(word, count > 1 ? 2 : 1); } public static class WastedSpaceDescendingComparator implements Comparator<DuplicateSet> { @Override public int compare(DuplicateSet ds1, DuplicateSet ds2) { return Long.compare(ds2.getWastedSpace(), ds1.getWastedSpace()); } } }