/* fm - File management class library Copyright (C) 2024 Ulrich Hilger This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package de.uhilger.fm; import java.io.File; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; /** * Ein FileVisitor zum Verschieben, Kopieren oder Loeschen ganzer Ordnerstrukturen mit * Hilfe der Methode Files.walkFileTree von java.nio. * * @author Ulrich Hilger * @version 1, 14. Mai 2021 */ public class FileOpsVisitor extends FileHelper implements FileVisitor { private Path targetDir; private int operation; /** * Den Zielordner fuer Kopier- oder Verschiebeoperationen angeben * * @param targetDir der Zielordner */ public void setTargetDir(Path targetDir) { this.targetDir = targetDir; } /** * Die gewuenschte Dateioperation angeben, * OP_COPY, OP_MOVE oder OP_DELETE * * @param op die Dateioperation */ public void setOperation(int op) { this.operation = op; } /** * Dafuer sorgen, dass beim Kopieren oder Verschieben kein am Ziel bereits existierender * Ordner ueberschrieben wird, indem am Ziel fuer einen bereits existierenden Ordner ein * anderer Name mit laufender Nummer erzeugt wird. * * Invoked for a directory before entries in the directory are visited. If this method * returns CONTINUE, then entries in the directory are visited. If this method returns * SKIP_SUBTREE or SKIP_SIBLINGS then entries in the directory (and any descendants) * will not be visited. * * @param dir Zielordner * @param attrs die gewuenschten Attribute * @return gibt stets FileVisitResult.CONTINUE zurueck * @throws IOException wenn etwas schief geht */ @Override public FileVisitResult preVisitDirectory(Object dir, BasicFileAttributes attrs) throws IOException { if (operation != Eraser.OP_DELETE) { if (dir instanceof Path) { Path sourceDir = (Path) dir; File destFile = targetDir.resolve(sourceDir.getFileName()).toFile(); //logger.fine("sourceDir: " + sourceDir + ", destFile: " + destFile); if (destFile.exists()) { File newDir = getNewFileName(destFile); destFile.renameTo(newDir); } destFile.mkdir(); this.targetDir = destFile.toPath(); //logger.fine("targetDir now: " + targetDir.toString()); } } return FileVisitResult.CONTINUE; } /** * Fuer jede Datei die gewuenschte Dateioperation ausführen * * Invoked for a file in a directory. * * @param file die zu bearbeitende Datei a reference to the file * @param attrs the directory's basic attributes * @return stets FileVisitResult.CONTINUE * @throws IOException wenn etwas schief geht */ @Override public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException { if(operation != Eraser.OP_DELETE) { if (file instanceof Path) { Path source = (Path) file; File destFile = targetDir.resolve(source.getFileName()).toFile(); if (destFile.exists()) { destFile = getNewFileName(destFile); } if (operation == Mover.OP_MOVE) { Files.move(source, destFile.toPath()); } else if (operation == Mover.OP_COPY) { Files.copy(source, destFile.toPath()); } } } else { Files.delete((Path) file); } return FileVisitResult.CONTINUE; } /** * Bei diesem Visitor bleibt diese Methode ungenutzt, hier muessten noch Faelle * behandelt werden, die zu einem Abbruch fuehren und ggf. ein Rollback realisiert werden. * * Invoked for a file that could not be visited. This method is invoked if the file's attributes * could not be read, the file is a directory that could not be opened, and other reasons. * * @param file die Datei, bei der es zum Abbruch kam * @param exc the I/O exception that prevented the file from being visited * @return stets FileVisitResult.CONTINUE * @throws IOException wenn etwas schief laeuft */ @Override public FileVisitResult visitFileFailed(Object file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } /** * Fuer jede Datei Schritte ausfuehren, wie sie sich nach einer Dateioperation ergeben. * Hier wird beim Verschieben von Dateien das Quellverzeichnis geloescht, nachdem es zum * Ziel uebertragen wurde. * * Invoked for a directory after entries in the directory, and all of their descendants, * have been visited. This method is also invoked when iteration of the directory completes * prematurely (by a visitFile method returning SKIP_SIBLINGS, or an I/O error when * iterating over the directory). * * @param dir der fertig durchlaufene Quellordner * @param exc null if the iteration of the directory completes without an error; otherwise * the I/O exception that caused the iteration of the directory to complete prematurely * @return * @throws IOException */ @Override public FileVisitResult postVisitDirectory(Object dir, IOException exc) throws IOException { if (operation != Eraser.OP_DELETE) { if (dir instanceof Path) { Path finishedDir = (Path) dir; targetDir = targetDir.getParent(); if(operation == Mover.OP_MOVE) { //logger.fine("delete " + finishedDir.toString()); Files.delete(finishedDir); } } //logger.fine("targetDir now: " + targetDir.toString()); } else { Files.delete((Path) dir); } return FileVisitResult.CONTINUE; } }