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,15 +182,18 @@ 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());
while (true) {
try {
if (file.isDirectory()) {
copyDirectory(file, targetFile, totalItems, currentItem, callback, globalResponse);
copyDirectory(file, targetFile, totalItems, currentItem, callback, globalResponse, globalErrorResponse);
} else {
if (targetFile.exists()) {
if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) {
currentItem[0] += countItems(file);
continue;
break;
}
if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) {
OverwriteResponse res = callback.confirmOverwrite(file, targetFile);
@ -195,17 +201,34 @@ public class FileOperations {
if (res == OverwriteResponse.NO_TO_ALL) {
globalResponse[0] = OverwriteResponse.NO_TO_ALL;
currentItem[0] += countItems(file);
continue;
break;
}
if (res == OverwriteResponse.NO) {
currentItem[0] += countItems(file);
continue;
break;
}
if (res == OverwriteResponse.YES_TO_ALL) globalResponse[0] = OverwriteResponse.YES_TO_ALL;
if (res == OverwriteResponse.YES_TO_ALL)
globalResponse[0] = OverwriteResponse.YES_TO_ALL;
}
}
copyFileWithProgress(file, targetFile, totalItems, currentItem, callback);
}
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;
}
}
}
}
}
target.setLastModified(source.lastModified());
@ -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,12 +408,16 @@ 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 (callback != null && callback.isCancelled()) break;
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
while (true) {
try {
if (child.isDirectory()) {
deleteDirectoryRecursive(child, totalItems, currentItem, callback);
deleteDirectoryRecursive(child, totalItems, currentItem, callback, globalErrorResponse);
} else {
currentItem[0]++;
if (callback != null) {
@ -392,6 +425,20 @@ public class FileOperations {
}
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;
}
}
}
}
}
currentItem[0]++;
@ -490,10 +537,24 @@ public class FileOperations {
public static void deleteFromFtp(List<FileItem> items, ProgressCallback callback) throws IOException {
for (FileItem item : items) {
if (callback != null && callback.isCancelled()) break;
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,6 +565,8 @@ public class FileOperations {
for (FileItem item : items) {
if (callback != null && callback.isCancelled()) break;
while (true) {
try {
if (item.isDirectory()) {
if (item.isFtp()) {
File tempDir = java.nio.file.Files.createTempDirectory("kf-ftp-copy").toFile();
@ -523,6 +586,18 @@ public class FileOperations {
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;
}
}
}
}
}
@ -544,6 +619,8 @@ public class FileOperations {
for (FileItem item : ftpItems) {
if (callback != null && callback.isCancelled()) break;
while (true) {
try {
if (callback != null) {
callback.onProgress(currentItem[0], totalItems, item.getName());
}
@ -560,6 +637,18 @@ public class FileOperations {
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();