/* * CopyFileWorker.java - a worker that will copy a file * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2008, 2012 Matthieu Casanova * * This program 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 2 * of the License, or 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.io; //{{{ Imports import java.awt.Component; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; import javax.annotation.Nullable; import org.gjt.sp.jedit.MiscUtilities; import org.gjt.sp.util.Log; import org.gjt.sp.util.Task; import org.gjt.sp.util.ThreadUtilities; //}}} /** * This worker will copy a file. Be careful it override files if the target * already exists * * @author Matthieu Casanova * @since jEdit 4.3pre13 */ public class CopyFileWorker extends Task { /** * The behavior if the target already exists * @since jEdit 5.0 */ public enum Behavior { /** Do not copy file. */ SKIP, /** Overwrite existing file. */ OVERWRITE, /** Rename existing file. */ RENAME } private String source; private final Component comp; private List<String> sources; /** * The behavior if the target already exists. */ private final Behavior behavior; private final String target; /** * A latch to tell when the copy is finished */ private CountDownLatch latch; //{{{ CopyFileWorker constructors /** * Copy a file. Careful, it will <b>overwrite</b> the target. * @param comp the component that will be used as parent in case of error * @param source the source path * @param target the target path (it is the file path, not a parent directory) */ public CopyFileWorker(Component comp, String source, String target) { this(comp, source, target, null); } /** * Copy a file. Careful, it will <b>overwrite</b> the target. * @param comp the component that will be used as parent in case of error * @param source the source path * @param target the target path (it is the file path, not a parent directory) * @param latch a latch so the caller knows when the copy is done */ private CopyFileWorker(Component comp, String source, String target, @Nullable CountDownLatch latch) { if (source == null || target == null) throw new NullPointerException("The source and target cannot be null"); if (source.equals(target)) throw new IllegalArgumentException("The source and target must not be the same"); this.comp = comp; this.source = source; this.target = target; behavior = Behavior.OVERWRITE; this.latch = latch; setLabel("Copy " + source + " to " + target); } /** * Copy all files from the list to the target directory. * If some files already exist in the target directory the files will * be skipped. * @param comp the component that will be used as parent in case of error * @param sources the sources path to copy * @param target the target path (it must be a directory otherwise nothing will be copied) * @since jEdit 5.0 */ public CopyFileWorker(Component comp, List<String> sources, String target) { this(comp, sources, target, Behavior.SKIP); } /** * Copy all files from the list to the target directory. * If some files already exist in the target directory the <code>Behavior</code> will decide what * to do. * @param comp the component that will be used as parent in case of error * @param sources the sources path to copy * @param target the target path (it must be a directory otherwise nothing will be copied) * @param behavior the behavior if the target file already exists * @since jEdit 5.0 */ public CopyFileWorker(Component comp, List<String> sources, String target, Behavior behavior) { if (sources == null || target == null) throw new NullPointerException("The source and target cannot be null"); this.comp = comp; this.sources = sources; this.target = target; this.behavior = behavior; } //}}} //{{{ _run() method @Override public void _run() { Log.log(Log.DEBUG, this, this + ".run()"); if (source != null) { // single file copy try { VFS.copy(this, source, target, comp, false, false); } catch (IOException e) { Log.log(Log.ERROR, this, e, e); } finally { if (latch != null) latch.countDown(); } } else { // List file copy copyFileList(); } } //}}} //{{{ copyFileList() method private void copyFileList() { VFS vfs = VFSManager.getVFSForPath(target); Object targetSession = null; try { targetSession = vfs.createVFSSession(target, comp); if (targetSession == null) { Log.log(Log.ERROR, this, "Target VFS path cannot be reached"); return; } VFSFile targetFile = vfs._getFile(targetSession, target, comp); if (targetFile == null) { Log.log(Log.ERROR, this, "Target is unreachable or do not exist"); return; } if (targetFile.getType() != VFSFile.DIRECTORY) { Log.log(Log.ERROR, this, "Target is not a directory"); return; } if (sources != null) { setMaximum(sources.size()); for (int i = 0; i < sources.size(); i++) { setValue(i); String sourcePath = sources.get(i); String sourceName = MiscUtilities.getFileName(sourcePath); setLabel(sourceName); copy(targetSession, vfs, sourcePath, sourceName, target); } } } catch (IOException e) { Log.log(Log.ERROR, this, e); } catch (InterruptedException e) { Log.log(Log.WARNING, this, "Copy was interrupted"); } finally { VFSManager.sendVFSUpdate(vfs, target, true); try { if (targetSession != null) vfs._endVFSSession(targetSession, comp); } catch (IOException e) { } } } //}}} //{{{ copy() method private void copy(Object vfsSession, VFS vfs, String sourcePath, String sourceName, String targetPath) throws IOException, InterruptedException { String name = getTargetName(vfsSession, vfs, targetPath, sourceName); if (name == null) { return; } String targetName = MiscUtilities.constructPath(targetPath, name); CountDownLatch latch = new CountDownLatch(1); ThreadUtilities.runInBackground(new CopyFileWorker(comp, sourcePath, targetName, latch)); latch.await(); } //}}} //{{{ getTargetName() method @Nullable private String getTargetName(Object session, VFS vfs, String path, String baseName) throws IOException { if (behavior == Behavior.OVERWRITE) { // We want to overwrite, no need to check anything return baseName; } String s = MiscUtilities.constructPath(target, baseName); VFSFile file = vfs._getFile(session, s, comp); if (file == null) { // The target file do not exist, perfect return baseName; } if (behavior == Behavior.SKIP) return null; String extension = MiscUtilities.getFileExtension(baseName); String nameNoExtension = MiscUtilities.getBaseName(baseName); for (int i = 1;i<1000;i++) { String name = nameNoExtension + "-copy-" + i; if (extension != null) name += extension; s = MiscUtilities.constructPath(path, name); file = vfs._getFile(session, s, comp); if (file == null) return name; } return null; } //}}} @Override public String toString() { return "CopyFileWorker[" + source + ',' + target + ']'; } }