/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander 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. * * muCommander 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.job.impl; import com.mucommander.commons.file.AbstractFile; import com.mucommander.commons.file.archive.AbstractRWArchiveFile; import com.mucommander.commons.file.util.FileSet; import com.mucommander.job.FileCollisionChecker; import com.mucommander.job.FileJobAction; import com.mucommander.text.Translator; import com.mucommander.ui.dialog.file.FileCollisionDialog; import com.mucommander.ui.dialog.file.FileCollisionRenameDialog; import com.mucommander.ui.dialog.file.ProgressDialog; import com.mucommander.ui.main.MainFrame; import java.io.IOException; /** * This class is the parent class of {@link com.mucommander.job.impl.CopyJob} and {@link com.mucommander.job.impl.MoveJob} and * allows them to share methods and fields. * * @author Maxence Bernard, Mariusz Jakubowski * @see com.mucommander.job.impl.CopyJob * @see com.mucommander.job.impl.MoveJob */ public abstract class AbstractCopyJob extends TransferFileJob { /** Base destination folder */ protected AbstractFile baseDestFolder; /** New filename in destination */ protected String newName; /** Default choice when encountering an existing file */ protected int defaultFileExistsAction = FileCollisionDialog.ASK_ACTION; /** Title used for error dialogs */ protected String errorDialogTitle; protected boolean append; /** The archive that contains the destination files (may be null) */ protected AbstractRWArchiveFile archiveToOptimize; /** True when an archive is being optimized */ protected boolean isOptimizingArchive; /** * Creates a new <code>AbstractCopyJob</code>. * * @param progressDialog dialog which shows this job's progress * @param mainFrame mainFrame this job has been triggered by * @param files files which are going to be copied * @param destFolder destination folder where the files will be copied * @param newName the new filename in the destination folder, can be <code>null</code> in which case the original filename will be used. * @param fileExistsAction default action to be performed when a file already exists in the destination, see {@link com.mucommander.ui.dialog.file.FileCollisionDialog} for allowed values */ public AbstractCopyJob(ProgressDialog progressDialog, MainFrame mainFrame, FileSet files, AbstractFile destFolder, String newName, int fileExistsAction) { super(progressDialog, mainFrame, files); this.baseDestFolder = destFolder; this.newName = newName; this.defaultFileExistsAction = fileExistsAction; } /** * Creates a destination file given a destination folder and a new file name. * @param destFolder a destination folder * @param destFileName a destination file name * @return the destination file or null if it cannot be created */ protected AbstractFile createDestinationFile(AbstractFile destFolder, String destFileName) { AbstractFile destFile; do { // Loop for retry try { destFile = destFolder.getDirectChild(destFileName); break; } catch(IOException e) { // Destination file couldn't be instantiated int ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_write_file", destFileName)); // Retry loops if(ret==FileJobAction.RETRY) continue; // Cancel or close dialog return false return null; // Skip continues } } while(true); return destFile; } /** * Checks if there is a file collision (file exists in the destination). * If there is no collision this method returns destFile. * If there is a collision this method returns: <ul> * <li>null if a user cancelled the transfer * <li>null if a user skipped the file * <li>destFile if a user resumed the transfer (and sets append flag) * <li>destFile if a user has chosen to overwrite the file * <li>new file if a user renamed the file * </ul> * @param file a source file * @param destFolder a destination folder * @param destFile a destination file * @param allowCaseVariation if true, * @return destFile the new destination file */ protected AbstractFile checkForCollision(AbstractFile file, AbstractFile destFolder, AbstractFile destFile, boolean allowCaseVariation) { append = false; while (true) { // Check for file collisions (file exists in the destination, destination subfolder of source, ...) // if a default action hasn't been specified int collision = FileCollisionChecker.checkForCollision(file, destFile); // If allowCaseVariation is true and both files are equal, test if the destination filename is a variation // of the original filename with a different case. If that is the case, do not warn about the source and // destination being the same. if(allowCaseVariation && collision==FileCollisionChecker.SAME_SOURCE_AND_DESTINATION) { String sourceFileName = file.getName(); String destFileName = destFile.getName(); if(sourceFileName.equalsIgnoreCase(destFileName) && !sourceFileName.equals(destFileName)) break; } // Handle collision, asking the user what to do or using a default action to resolve the collision if(collision != FileCollisionChecker.NO_COLLOSION) { int choice; // Use default action if one has been set, if not show up a dialog if(defaultFileExistsAction==FileCollisionDialog.ASK_ACTION) { FileCollisionDialog dialog = new FileCollisionDialog(getProgressDialog(), getMainFrame(), collision, file, destFile, true, true); choice = waitForUserResponse(dialog); // If 'apply to all' was selected, this choice will be used for any other files (user will not be asked again) if(dialog.applyToAllSelected()) defaultFileExistsAction = choice; } else choice = defaultFileExistsAction; // Cancel, skip or close dialog if (choice==-1 || choice== FileCollisionDialog.CANCEL_ACTION) { interrupt(); return null; } // Skip file else if (choice== FileCollisionDialog.SKIP_ACTION) { return null; } // Append to file (resume file copy) else if (choice== FileCollisionDialog.RESUME_ACTION) { append = true; break; } // Overwrite file else if (choice== FileCollisionDialog.OVERWRITE_ACTION) { // Do nothing, simply continue break; } // Overwrite file if destination is older else if (choice== FileCollisionDialog.OVERWRITE_IF_OLDER_ACTION) { // Overwrite if file is newer (stricly) if(file.getDate()<=destFile.getDate()) return null; break; } else if (choice == FileCollisionDialog.RENAME_ACTION) { setPaused(true); FileCollisionRenameDialog dlg = new FileCollisionRenameDialog(getMainFrame(), destFile); String destFileName = (String) waitForUserResponseObject(dlg); setPaused(false); if (destFileName != null) { destFile = createDestinationFile(destFolder, destFileName); } else { // turn on FileCollisionDialog, so we don't loop indefinitely defaultFileExistsAction = FileCollisionDialog.ASK_ACTION; } // continue with collision checking continue; } } break; // no collision } return destFile; } /** * Optimizes the given writable archive file and notifies the user in case of an error. * * @param rwArchiveFile the writable archive file to optimize */ protected void optimizeArchive(AbstractRWArchiveFile rwArchiveFile) { isOptimizingArchive = true; while(true) { try { archiveToOptimize = rwArchiveFile; archiveToOptimize.optimizeArchive(); break; } catch(IOException e) { if(showErrorDialog(errorDialogTitle, Translator.get("error_while_optimizing_archive", rwArchiveFile.getName()))==FileJobAction.RETRY) continue; break; } } isOptimizingArchive = false; } }