added skip all on error

This commit is contained in:
Radek Davidek 2026-05-20 21:42:34 +02:00
parent 61998ecc40
commit f7d63027d4
3 changed files with 163 additions and 72 deletions

1
.gitignore vendored
View File

@ -23,3 +23,4 @@ buildNumber.properties
# OS
.DS_Store
Thumbs.db
.claude

View File

@ -59,6 +59,7 @@ public class FileOperations {
long totalItems = calculateTotalItems(cleanedItems);
final long[] currentItem = {0};
final OverwriteResponse[] globalResponse = {null};
final ErrorResponse[] globalErrorResponse = {null};
for (FileItem item : cleanedItems) {
if (callback != null && callback.isCancelled()) break;
@ -95,9 +96,10 @@ public class FileOperations {
}
while (true) {
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
try {
if (source.isDirectory()) {
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse);
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse, globalErrorResponse);
} else {
copyFileWithProgress(source, target, totalItems, currentItem, callback);
}
@ -107,6 +109,7 @@ public class FileOperations {
ErrorResponse res = callback.onError(source, e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
break;
} else {
throw e;
@ -163,7 +166,7 @@ public class FileOperations {
copyPermissions(source, target);
}
private static void copyDirectory(File source, File target, long totalItems, final long[] currentItem, ProgressCallback callback, final OverwriteResponse[] globalResponse) throws IOException {
private static void copyDirectory(File source, File target, long totalItems, final long[] currentItem, ProgressCallback callback, final OverwriteResponse[] globalResponse, final ErrorResponse[] globalErrorResponse) throws IOException {
if (!target.exists()) {
if (!target.mkdirs()) {
throw new IOException("Failed to create directory: " + target.getAbsolutePath());
@ -179,32 +182,52 @@ public class FileOperations {
if (files != null) {
for (File file : files) {
if (callback != null && callback.isCancelled()) break;
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
File targetFile = new File(target, file.getName());
if (file.isDirectory()) {
copyDirectory(file, targetFile, totalItems, currentItem, callback, globalResponse);
} else {
if (targetFile.exists()) {
if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) {
currentItem[0] += countItems(file);
continue;
while (true) {
try {
if (file.isDirectory()) {
copyDirectory(file, targetFile, totalItems, currentItem, callback, globalResponse, globalErrorResponse);
} else {
if (targetFile.exists()) {
if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) {
currentItem[0] += countItems(file);
break;
}
if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) {
OverwriteResponse res = callback.confirmOverwrite(file, targetFile);
if (res == OverwriteResponse.CANCEL) return;
if (res == OverwriteResponse.NO_TO_ALL) {
globalResponse[0] = OverwriteResponse.NO_TO_ALL;
currentItem[0] += countItems(file);
break;
}
if (res == OverwriteResponse.NO) {
currentItem[0] += countItems(file);
break;
}
if (res == OverwriteResponse.YES_TO_ALL)
globalResponse[0] = OverwriteResponse.YES_TO_ALL;
}
}
copyFileWithProgress(file, targetFile, totalItems, currentItem, callback);
}
if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) {
OverwriteResponse res = callback.confirmOverwrite(file, targetFile);
if (res == OverwriteResponse.CANCEL) return;
if (res == OverwriteResponse.NO_TO_ALL) {
globalResponse[0] = OverwriteResponse.NO_TO_ALL;
currentItem[0] += countItems(file);
continue;
}
if (res == OverwriteResponse.NO) {
currentItem[0] += countItems(file);
continue;
}
if (res == OverwriteResponse.YES_TO_ALL) globalResponse[0] = OverwriteResponse.YES_TO_ALL;
break;
} catch (IOException e) {
if (callback != null) {
ErrorResponse res = callback.onError(file, e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
// SKIP: Break while(true) to continue with next file in for loop
// we don't increment currentItem here because it's hard to know
// what exactly was processed inside copyDirectory if it failed midway.
break;
} else {
throw e;
}
}
copyFileWithProgress(file, targetFile, totalItems, currentItem, callback);
}
}
}
@ -273,6 +296,7 @@ public class FileOperations {
long totalItems = calculateTotalItems(cleanedItems);
long[] currentItem = {0};
final OverwriteResponse[] globalResponse = {null};
final ErrorResponse[] globalErrorResponse = {null};
for (FileItem item : cleanedItems) {
if (callback != null && callback.isCancelled()) break;
@ -306,6 +330,7 @@ public class FileOperations {
long itemCount = countItems(source);
while (true) {
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
try {
if (source.renameTo(target)) {
currentItem[0] += itemCount;
@ -315,7 +340,7 @@ public class FileOperations {
} else {
// Fallback for cross-device moves
if (source.isDirectory()) {
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse);
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse, globalErrorResponse);
deleteDirectoryInternal(source);
} else {
copyFileWithProgress(source, target, totalItems, currentItem, callback);
@ -328,6 +353,7 @@ public class FileOperations {
ErrorResponse res = callback.onError(source, e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
break;
} else {
throw e;
@ -348,15 +374,17 @@ public class FileOperations {
List<FileItem> cleanedItems = cleanDuplicateItems(items);
long totalItems = calculateTotalItems(cleanedItems);
long[] currentItem = {0};
final ErrorResponse[] globalErrorResponse = {null};
for (FileItem item : cleanedItems) {
if (callback != null && callback.isCancelled()) break;
File file = item.getFile();
while (true) {
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
try {
if (file.isDirectory()) {
deleteDirectoryRecursive(file, totalItems, currentItem, callback);
deleteDirectoryRecursive(file, totalItems, currentItem, callback, globalErrorResponse);
} else {
currentItem[0]++;
if (callback != null) {
@ -370,6 +398,7 @@ public class FileOperations {
ErrorResponse res = callback.onError(file, e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
break;
} else {
throw e;
@ -379,18 +408,36 @@ public class FileOperations {
}
}
private static void deleteDirectoryRecursive(File directory, long totalItems, long[] currentItem, ProgressCallback callback) throws IOException {
private static void deleteDirectoryRecursive(File directory, long totalItems, long[] currentItem, ProgressCallback callback, final ErrorResponse[] globalErrorResponse) throws IOException {
File[] children = directory.listFiles();
if (children != null) {
for (File child : children) {
if (child.isDirectory()) {
deleteDirectoryRecursive(child, totalItems, currentItem, callback);
} else {
currentItem[0]++;
if (callback != null) {
callback.onProgress(currentItem[0], totalItems, child.getName());
if (callback != null && callback.isCancelled()) break;
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
while (true) {
try {
if (child.isDirectory()) {
deleteDirectoryRecursive(child, totalItems, currentItem, callback, globalErrorResponse);
} else {
currentItem[0]++;
if (callback != null) {
callback.onProgress(currentItem[0], totalItems, child.getName());
}
deleteFileWithForce(child);
}
break;
} catch (IOException e) {
if (callback != null) {
ErrorResponse res = callback.onError(child, e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
// SKIP:
break;
} else {
throw e;
}
}
deleteFileWithForce(child);
}
}
}
@ -490,9 +537,23 @@ public class FileOperations {
public static void deleteFromFtp(List<FileItem> items, ProgressCallback callback) throws IOException {
for (FileItem item : items) {
if (callback != null && callback.isCancelled()) break;
FtpService.deleteOnFtp(item.getFtpProfile(), item.getFtpPath());
if (callback != null) {
callback.onFileProgress(item.getSize(), item.getSize());
while (true) {
try {
FtpService.deleteOnFtp(item.getFtpProfile(), item.getFtpPath());
if (callback != null) {
callback.onFileProgress(item.getSize(), item.getSize());
}
break;
} catch (IOException e) {
if (callback != null) {
ErrorResponse res = callback.onError(item.getFile(), e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
break;
} else {
throw e;
}
}
}
}
}
@ -504,23 +565,37 @@ public class FileOperations {
for (FileItem item : items) {
if (callback != null && callback.isCancelled()) break;
if (item.isDirectory()) {
if (item.isFtp()) {
File tempDir = java.nio.file.Files.createTempDirectory("kf-ftp-copy").toFile();
copyFromFtp(java.util.Collections.singletonList(item), tempDir, callback);
uploadLocalToFtp(profile, tempDir, remotePath + "/" + item.getName(), callback);
deleteRecursively(tempDir);
} else {
uploadLocalToFtp(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
}
} else {
if (item.isFtp()) {
File tempFile = java.nio.file.Files.createTempFile("kf-ftp-copy", ".tmp").toFile();
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), tempFile, callback);
FtpService.uploadFile(profile, tempFile, remotePath + "/" + item.getName(), callback);
tempFile.delete();
} else {
FtpService.uploadFile(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
while (true) {
try {
if (item.isDirectory()) {
if (item.isFtp()) {
File tempDir = java.nio.file.Files.createTempDirectory("kf-ftp-copy").toFile();
copyFromFtp(java.util.Collections.singletonList(item), tempDir, callback);
uploadLocalToFtp(profile, tempDir, remotePath + "/" + item.getName(), callback);
deleteRecursively(tempDir);
} else {
uploadLocalToFtp(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
}
} else {
if (item.isFtp()) {
File tempFile = java.nio.file.Files.createTempFile("kf-ftp-copy", ".tmp").toFile();
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), tempFile, callback);
FtpService.uploadFile(profile, tempFile, remotePath + "/" + item.getName(), callback);
tempFile.delete();
} else {
FtpService.uploadFile(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
}
}
break;
} catch (IOException e) {
if (callback != null) {
ErrorResponse res = callback.onError(item.getFile(), e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
break;
} else {
throw e;
}
}
}
}
@ -543,22 +618,36 @@ public class FileOperations {
for (FileItem item : ftpItems) {
if (callback != null && callback.isCancelled()) break;
if (callback != null) {
callback.onProgress(currentItem[0], totalItems, item.getName());
}
if (item.isDirectory()) {
File targetDir = new File(localTarget, item.getName());
FtpService.downloadDirectory(item.getFtpProfile(), item.getFtpPath(), targetDir, callback);
} else {
File targetFile = new File(localTarget, item.getName());
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), targetFile, callback);
}
currentItem[0]++;
if (callback != null) {
callback.onProgress(currentItem[0], totalItems, item.getName());
while (true) {
try {
if (callback != null) {
callback.onProgress(currentItem[0], totalItems, item.getName());
}
if (item.isDirectory()) {
File targetDir = new File(localTarget, item.getName());
FtpService.downloadDirectory(item.getFtpProfile(), item.getFtpPath(), targetDir, callback);
} else {
File targetFile = new File(localTarget, item.getName());
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), targetFile, callback);
}
currentItem[0]++;
if (callback != null) {
callback.onProgress(currentItem[0], totalItems, item.getName());
}
break;
} catch (IOException e) {
if (callback != null) {
ErrorResponse res = callback.onError(item.getFile(), e);
if (res == ErrorResponse.ABORT) throw e;
if (res == ErrorResponse.RETRY) continue;
break;
} else {
throw e;
}
}
}
}
}
@ -1635,7 +1724,7 @@ public class FileOperations {
}
public enum ErrorResponse {
RETRY, SKIP, ABORT
RETRY, SKIP, SKIP_ALL, ABORT
}
public enum SymlinkResponse {

View File

@ -3767,7 +3767,7 @@ public class MainWindow extends JFrame {
final FileOperations.ErrorResponse[] result = new FileOperations.ErrorResponse[1];
try {
SwingUtilities.invokeAndWait(() -> {
Object[] options = {"Skip", "Retry", "Abort"};
Object[] options = {"Skip", "Skip All", "Retry", "Abort"};
int n = JOptionPane.showOptionDialog(progressDialog,
"Error operating on file: " + file.getName() + "\n" + e.getMessage(),
"Error",
@ -3779,7 +3779,8 @@ public class MainWindow extends JFrame {
switch (n) {
case 0: result[0] = FileOperations.ErrorResponse.SKIP; break;
case 1: result[0] = FileOperations.ErrorResponse.RETRY; break;
case 1: result[0] = FileOperations.ErrorResponse.SKIP_ALL; break;
case 2: result[0] = FileOperations.ErrorResponse.RETRY; break;
default:
result[0] = FileOperations.ErrorResponse.ABORT;
progressDialog.cancel();