use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.
the class FileCollisionDialog method init.
private void init(int collisionType, AbstractFile sourceFile, AbstractFile destFile, boolean multipleFilesMode, boolean allowRename) {
// Init choices
List<DialogAction> actionChoices = new ArrayList<>();
actionChoices.add(FileCollisionAction.CANCEL);
if (multipleFilesMode) {
actionChoices.add(FileCollisionAction.SKIP);
}
// Add 'overwrite' / 'overwrite if older' / 'resume' actions only for 'destination file already exists' collision type
if (collisionType == FileCollisionChecker.DESTINATION_FILE_ALREADY_EXISTS && !destFile.isDirectory()) {
actionChoices.add(FileCollisionAction.OVERWRITE);
if (sourceFile != null) {
actionChoices.add(FileCollisionAction.OVERWRITE_IF_OLDER);
actionChoices.add(FileCollisionAction.OVERWRITE_IF_SIZE_DIFFERS);
// Give resume option only if destination file is smaller than source file
long destSize = destFile.getSize();
long sourceSize = sourceFile.getSize();
if (destSize != -1 && (sourceSize == -1 || destSize < sourceSize)) {
actionChoices.add(FileCollisionAction.RESUME);
}
if (allowRename) {
actionChoices.add(FileCollisionAction.RENAME);
}
}
}
// Init UI
String desc = null;
switch(collisionType) {
case FileCollisionChecker.DESTINATION_FILE_ALREADY_EXISTS:
desc = Translator.get("file_exists_in_destination");
break;
case FileCollisionChecker.SAME_SOURCE_AND_DESTINATION:
desc = Translator.get("same_source_destination");
break;
case FileCollisionChecker.SOURCE_PARENT_OF_DESTINATION:
desc = Translator.get("source_parent_of_destination");
break;
}
YBoxPanel yPanel = new YBoxPanel();
if (desc != null) {
yPanel.add(new InformationPane(desc, null, Font.PLAIN, InformationPane.QUESTION_ICON));
yPanel.addSpace(10);
}
// Add a separator before file details
yPanel.add(new JSeparator());
XAlignedComponentPanel tfPanel = new XAlignedComponentPanel(10);
// If collision type is 'same source and destination' no need to show both source and destination
if (collisionType == FileCollisionChecker.SAME_SOURCE_AND_DESTINATION) {
addFileDetails(tfPanel, sourceFile, Translator.get("name"));
} else {
if (sourceFile != null) {
addFileDetails(tfPanel, sourceFile, Translator.get("source"));
}
addFileDetails(tfPanel, destFile, Translator.get("destination"));
}
yPanel.add(tfPanel);
// Add a separator after file details
yPanel.add(new JSeparator());
init(yPanel, actionChoices, 3);
// TODO below there's workaround to accommodate texts within buttons - any idea to how to make it better?
// override to avoid FocusDialog#pack making the dialog box too small for some buttons
// so they won't display full texts (observe when Spanish lang pack is chosen - a lot of them have ellipsis)
setMinimumSize(null);
setMaximumSize(null);
// 'Apply to all' is available only for 'destination file already exists' collision type
if (multipleFilesMode && collisionType == FileCollisionChecker.DESTINATION_FILE_ALREADY_EXISTS) {
applyToAllCheckBox = new JCheckBox(Translator.get("apply_to_all"));
addComponent(applyToAllCheckBox);
}
// Send a system notification if a notifier is available and enabled
if (NotifierProvider.isAvailable() && NotifierProvider.getNotifier().isEnabled()) {
NotifierProvider.displayBackgroundNotification(NotificationType.JOB_ERROR, getTitle(), desc);
}
}
use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.
the class MoveJob method processFile.
// //////////////////////////////////
// TransferFileJob implementation //
// //////////////////////////////////
/**
* Moves recursively the given file or folder.
*
* @param file the file or folder to move
* @param recurseParams destination folder where the given file will be moved (null for top level files)
*
* @return <code>true</code> if the file has been moved completely (copied + deleted).
*/
@Override
protected boolean processFile(AbstractFile file, Object recurseParams) {
// Stop if interrupted
if (getState() == FileJobState.INTERRUPTED)
return false;
// Destination folder
AbstractFile destFolder = recurseParams == null ? baseDestFolder : (AbstractFile) recurseParams;
// the renamed file (the search results may reside in different sub-folders)
if (renameMode && SearchFile.SCHEMA.equals(destFolder.getURL().getScheme()))
destFolder = file.getParent();
// Is current file at the base folder level ?
boolean isFileInBaseFolder = files.indexOf(file) != -1;
// Determine filename in destination
String destFileName = isFileInBaseFolder && newName != null ? newName : file.getName();
// Create destination AbstractFile instance
AbstractFile destFile = createDestinationFile(file, destFolder, destFileName);
if (destFile == null)
return false;
/*
// Do not follow symlink, simply delete it and return
if(file.isSymlink()) {
do { // Loop for retry
try {
file.delete();
return true;
}
catch(IOException e) {
LOGGER.debug("IOException caught", e);
int ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_delete_file", file.getAbsolutePath()));
// Retry loops
if(ret==FileJobAction.RETRY)
continue;
// Cancel, skip or close dialog returns false
return false;
}
} while(true);
}
*/
destFile = checkForCollision(file, destFolder, destFile, renameMode);
if (destFile == null)
return false;
// I/O bound checks and ends up throwing an exception which also comes at a cost.
if (!append && file.getURL().schemeEquals(destFile.getURL()) && file.isFileOperationSupported(FileOperation.RENAME)) {
try {
file.renameTo(destFile);
return true;
} catch (IOException e) {
// Fail silently: renameTo might fail under normal conditions, for instance for local files which are
// not located on the same volume.
LOGGER.debug("Failed to rename " + file + " into " + destFile + " (not necessarily an error)", e);
}
}
// Move the directory and all its children recursively, by copying files to the destination and then deleting them.
if (file.isDirectory()) {
// create the destination folder if it doesn't exist
if (!(destFile.exists() && destFile.isDirectory())) {
do {
// Loop for retry
try {
destFile.mkdir();
} catch (IOException e) {
DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_create_folder", destFile.getAbsolutePath()));
// Retry loops
if (ret == FileJobAction.RETRY)
continue;
// Cancel, skip or close dialog returns false
return false;
}
break;
} while (true);
}
// move each file in this folder recursively
do {
// Loop for retry
try {
AbstractFile[] subFiles = file.ls();
boolean isFolderEmpty = true;
for (AbstractFile subFile : subFiles) {
// Return now if the job was interrupted, so that we do not attempt to delete this folder
if (getState() == FileJobState.INTERRUPTED)
return false;
// Notify job that we're starting to process this file (needed for recursive calls to processFile)
nextFile(subFile);
if (!processFile(subFile, destFile))
isFolderEmpty = false;
}
// Only when finished with folder, set destination folder's date to match the original folder one
if (destFile.isFileOperationSupported(FileOperation.CHANGE_DATE)) {
try {
destFile.changeDate(file.getDate());
} catch (IOException e) {
LOGGER.debug("failed to change the date of " + destFile, e);
// Fail silently
}
}
// If one file failed to be moved, return false (failure) since this folder could not be moved totally
if (!isFolderEmpty)
return false;
} catch (IOException e) {
// file.ls() failed
DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_read_folder", file.getName()));
// Retry loops
if (ret == FileJobAction.RETRY)
continue;
// Cancel, skip or close dialog returns false
return false;
}
break;
} while (true);
// Return now if the job was interrupted, so that we do not attempt to delete this folder
if (getState() == FileJobState.INTERRUPTED)
return false;
// finally, delete the empty folder
do {
// Loop for retry
try {
file.delete();
return true;
} catch (IOException e) {
DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_delete_folder", file.getAbsolutePath()));
// Retry loops
if (ret == FileJobAction.RETRY)
continue;
// Cancel, skip or close dialog returns false
return false;
}
} while (true);
} else // File is a regular file, move it by copying it to the destination and then deleting it
{
// try the hard way by copying the file first, and then deleting the source file.
if (tryCopyFile(file, destFile, append, errorDialogTitle) && getState() != FileJobState.INTERRUPTED) {
// Delete the source file
do {
// Loop for retry
try {
file.delete();
// All OK
return true;
} catch (IOException e) {
LOGGER.debug("IOException caught", e);
DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_delete_file", file.getAbsolutePath()));
// Retry loops
if (ret == FileJobAction.RETRY)
continue;
// Cancel, skip or close dialog returns false
return false;
}
} while (true);
}
return false;
}
}
use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.
the class UnpackJob method processFile.
/**
* Unpacks the given archive file. If the file is a directory, its children will be processed recursively.
* If the file is not an archive file nor a directory, it is not processed and <code>false</code> is returned.
*
* @param file the file to unpack
* @param recurseParams unused
* @return <code>true</code> if the file has been processed successfully
*/
@Override
protected boolean processFile(AbstractFile file, Object recurseParams) {
// Stop if interrupted
if (getState() == FileJobState.INTERRUPTED)
return false;
// Destination folder
AbstractFile destFolder = baseDestFolder;
// If the file is a directory, process its children recursively
if (file.isDirectory()) {
do {
// Loop for retries
try {
// List files inside archive file (can throw an IOException)
AbstractFile[] archiveFiles = getCurrentFile().ls();
// Recurse on zip's contents
for (int j = 0; j < archiveFiles.length && getState() != FileJobState.INTERRUPTED; j++) {
// Notify job that we're starting to process this file (needed for recursive calls to processFile)
nextFile(archiveFiles[j]);
// Recurse
processFile(archiveFiles[j], destFolder);
}
// Return true when complete
return true;
} catch (IOException e) {
// File could not be uncompressed properly
DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_read_file", getCurrentFilename()));
// Retry loops
if (ret == FileJobAction.RETRY)
continue;
// cancel, skip or close dialog will simply return false
return false;
}
} while (true);
}
// Abort if the file is neither an archive file nor a directory
if (!file.isArchive())
return false;
// 'Cast' the file as an archive file
AbstractArchiveFile archiveFile = file.getAncestor(AbstractArchiveFile.class);
String destSeparator = destFolder.getSeparator();
// Unpack the archive, copying entries one by one, in the iterator's order
try (ArchiveEntryIterator iterator = archiveFile.getEntryIterator()) {
ArchiveEntry entry;
while ((entry = iterator.nextEntry()) != null && getState() != FileJobState.INTERRUPTED) {
String entryPath = entry.getPath();
boolean processEntry = false;
if (selectedEntries == null) {
// Entries are processed
processEntry = true;
} else {
// We need to determine if the entry should be processed or not
// Process this entry if the selectedEntries set contains this entry, or a parent of this entry
int nbSelectedEntries = selectedEntries.size();
for (int i = 0; i < nbSelectedEntries; i++) {
ArchiveEntry selectedEntry = selectedEntries.get(i);
// selectedEntry is a parent of the current entry.
if (selectedEntry.isDirectory()) {
if (entryPath.startsWith(selectedEntry.getPath())) {
processEntry = true;
break;
// Note: we can't remove selectedEntryPath from the set, we still need it
}
} else if (entryPath.equals(selectedEntry.getPath())) {
// If the (regular file) entry is in the set, remove it as we no longer need it (will speed up
// subsequent searches)
processEntry = true;
selectedEntries.remove(i);
break;
}
}
}
if (!processEntry)
continue;
DefaultMutableTreeNode entryNode = archiveFile.getArchiveEntryNode(entryPath);
if (entryNode != null) {
ArchiveEntry archiveEntry = (ArchiveEntry) entryNode.getUserObject();
if (archiveEntry.isSymbolicLink()) {
Files.createSymbolicLink(FileSystems.getDefault().getPath(destFolder.getPath(), entry.getName()), FileSystems.getDefault().getPath(entry.getLinkTarget()));
continue;
}
}
// Resolve the entry file
AbstractFile entryFile = archiveFile.getArchiveEntryFile(entryPath);
// Notify the job that we're starting to process this file
nextFile(entryFile);
// Figure out the destination file's path, relatively to the base destination folder
String relDestPath = baseArchiveDepth == 0 ? entry.getPath() : PathUtils.removeLeadingFragments(entry.getPath(), "/", baseArchiveDepth);
if (newName != null)
relDestPath = newName + (PathUtils.getDepth(relDestPath, "/") <= 1 ? "" : "/" + PathUtils.removeLeadingFragments(relDestPath, "/", 1));
if (!"/".equals(destSeparator))
relDestPath = relDestPath.replace("/", destSeparator);
// Create destination AbstractFile instance
AbstractFile destFile = destFolder.getChild(relDestPath);
// Check for ZipSlip (see https://snyk.io/research/zip-slip-vulnerability)
do {
if (destFolder.isParentOf(destFile))
break;
DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("unpack.entry_out_of_target_dir", destFile.getName()));
// Retry loops
if (ret == FileJobAction.RETRY)
continue;
// Cancel, skip or close dialog returns false
return false;
} while (true);
// Check if the file does not already exist in the destination
destFile = checkForCollision(entryFile, destFolder, destFile, false);
if (destFile == null) {
// A collision occurred and either the file was skipped, or the user cancelled the job
continue;
}
// If the entry is a directory ...
if (entryFile.isDirectory()) {
// Create the directory in the destination, if it doesn't already exist
if (!(destFile.exists() && destFile.isDirectory())) {
// Loop for retry
do {
try {
// Use mkdirs() instead of mkdir() to create any parent folder that doesn't exist yet
destFile.mkdirs();
} catch (IOException e) {
// Unable to create folder
DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_create_folder", entryFile.getName()));
// Retry loops
if (ret == FileJobAction.RETRY)
continue;
// Cancel or close dialog return false
return false;
// Skip continues
}
break;
} while (true);
}
} else // The entry is a regular file, copy it
{
// Create the file's parent directory(s) if it doesn't already exist
AbstractFile destParentFile = destFile.getParent();
if (!destParentFile.exists()) {
// Use mkdirs() instead of mkdir() to create any parent folder that doesn't exist yet
destParentFile.mkdirs();
}
// some archive file implementations (such as TAR) can speed things by an order of magnitude.
if (!tryCopyFile(new ProxiedEntryFile(entryFile, entry, archiveFile, iterator), destFile, append, errorDialogTitle))
return false;
}
}
return true;
} catch (IOException e) {
showErrorDialog(errorDialogTitle, Translator.get("cannot_read_file", archiveFile.getName()));
}
return false;
}
use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.
the class ArchiveJob method processFile.
// //////////////////////////////////
// TransferFileJob implementation //
// //////////////////////////////////
@Override
protected boolean processFile(AbstractFile file, Object recurseParams) {
if (getState() == FileJobState.INTERRUPTED)
return false;
String filePath = file.getAbsolutePath(false);
String entryRelativePath = filePath.substring(baseFolderPath.length() + 1, filePath.length());
// Process current file
do {
// Loop for retry
try {
if (file.isDirectory() && !file.isSymlink()) {
// Create new directory entry in archive file
archiver.createEntry(entryRelativePath, file);
// Recurse on files
AbstractFile[] subFiles = file.ls();
boolean folderComplete = true;
for (int i = 0; i < subFiles.length && getState() != FileJobState.INTERRUPTED; i++) {
// Notify job that we're starting to process this file (needed for recursive calls to processFile)
nextFile(subFiles[i]);
if (!processFile(subFiles[i], null))
folderComplete = false;
}
return folderComplete;
} else {
InputStream in = setCurrentInputStream(file.getInputStream());
// written to the archive OutputStream, this would cause ZipOutputStream to deadlock.
synchronized (ioLock) {
// Create a new file entry in archive and copy the current file
StreamUtils.copyStream(in, archiver.createEntry(entryRelativePath, file));
in.close();
}
return true;
}
}// Catch Exception rather than IOException as ZipOutputStream has been seen throwing NullPointerException
catch (Exception e) {
// In this case, the exception should not be interpreted as an error.
if (getState() == FileJobState.INTERRUPTED)
return false;
LOGGER.debug("Caught IOException", e);
DialogAction ret = showErrorDialog(Translator.get("pack_dialog.error_title"), Translator.get("error_while_transferring", file.getAbsolutePath()));
// Retry loops
if (ret == FileJobAction.RETRY) {
// Reset processed bytes currentFileByteCounter
resetCurrentFileByteCounter();
continue;
}
// Cancel, skip or close dialog return false
return false;
}
} while (true);
}
use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.
the class ArchiveJob method jobStarted.
// //////////////////////
// Overridden methods //
// //////////////////////
/**
* Overriden method to initialize the archiver and handle the case where the destination file already exists.
*/
@Override
protected void jobStarted() {
super.jobStarted();
// Check for file collisions, i.e. if the file already exists in the destination
int collision = FileCollisionChecker.checkForCollision(null, destFile);
if (collision != FileCollisionChecker.NO_COLLISION) {
// File already exists in destination, ask the user what to do (cancel, overwrite,...) but
// do not offer the multiple files mode options such as 'skip' and 'apply to all'.
DialogAction choice = waitForUserResponse(new FileCollisionDialog(getProgressDialog(), getMainFrame(), collision, null, destFile, false, false));
// Overwrite file
if (choice == FileCollisionDialog.FileCollisionAction.OVERWRITE) {
// Do nothing, simply continue and file will be overwritten
} else // 'Cancel' or close dialog interrupts the job
{
interrupt();
return;
}
}
// Loop for retry
do {
try {
// Tries to get an Archiver instance.
this.archiver = Archiver.getArchiver(destFile, archiveFormat);
this.archiver.setComment(archiveComment);
break;
} catch (Exception e) {
DialogAction choice = showErrorDialog(Translator.get("pack_dialog.error_title"), Translator.get("cannot_write_file", destFile.getName()), Arrays.asList(FileJobAction.CANCEL, FileJobAction.RETRY));
// Retry loops
if (choice == FileJobAction.RETRY)
continue;
// 'Cancel' or close dialog interrupts the job
interrupt();
return;
}
} while (true);
}
Aggregations