# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: /data/work/src/netbeans-cdev # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: masterfs/apichanges.xml --- masterfs/apichanges.xml Base (BASE) +++ masterfs/apichanges.xml Locally Modified (Based On LOCAL) @@ -49,6 +49,19 @@ MasterFileSystem API + + + Delegate FileObject.copy() to ProvidedExtensions + + + + + + ProvidedExtensions.getCopyHandler() allows to delegate a copy operation. + + + + Guaranteed event order when deleting and creating files and new calls in ProvidedExtensions Index: masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java --- masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java Locally Modified (Based On LOCAL) @@ -72,6 +72,20 @@ this.annotationProviders = annotationProviders; } + @Override + public IOHandler getCopyHandler(File from, File to) { + IOHandler retValue = null; + for (Iterator it = annotationProviders.iterator(); it.hasNext() && retValue == null;) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + ProvidedExtensions.IOHandler delgate = ((ProvidedExtensions)iListener).getCopyHandler(from, to); + retValue = delgate != null ? new DelegatingIOHandler(delgate) : null; + } + } + return retValue; + } + public ProvidedExtensions.DeleteHandler getDeleteHandler(final File f) { ProvidedExtensions.DeleteHandler retValue = null; for (Iterator it = annotationProviders.iterator(); it.hasNext() && retValue == null;) { @@ -386,6 +400,51 @@ } } + @Override + public void beforeCopy(final FileObject from, final File to) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + runCheckCode(new Runnable() { + public void run() { + ((ProvidedExtensions)iListener).beforeCopy(from, to); + } + }); + } + } + } + + @Override + public void copySuccess(final FileObject from, final File to) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + runCheckCode(new Runnable() { + public void run() { + ((ProvidedExtensions)iListener).copySuccess(from, to); + } + }); + } + } + } + + @Override + public void copyFailure(final FileObject from, final File to) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + runCheckCode(new Runnable() { + public void run() { + ((ProvidedExtensions)iListener).copyFailure(from, to); + } + }); + } + } + } + public static void checkReentrancy() { if (reentrantCheck.get() != null) { Logger.getLogger("org.netbeans.modules.masterfs.ProvidedExtensionsProxy").log(Level.INFO,"Unexpected reentrant call", new Throwable());//NOI18N Index: masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/BaseFileObj.java --- masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/BaseFileObj.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/BaseFileObj.java Locally Modified (Based On LOCAL) @@ -214,10 +214,43 @@ } @Override + public FileObject copy(FileObject target, String name, String ext) throws IOException { + ProvidedExtensions extensions = getProvidedExtensions(); + + File to = getToFile(target, name, ext); + + extensions.beforeCopy(target, to); + FileObject result = null; + try { + final IOHandler copyHandler = extensions.getCopyHandler(getFileName().getFile(), to); + if (copyHandler != null) { + if (target instanceof FolderObj) { + result = handleMoveCopy((FolderObj)target, name, ext, copyHandler); + } else { + copyHandler.handle(); + refresh(true); + //perfromance bottleneck to call refresh on folder + //(especially for many files to be copied) + target.refresh(true); // XXX ? + result = target.getFileObject(name, ext); // XXX ? + assert result != null : "Cannot find " + target + " with " + name + "." + ext; + } + FileUtil.copyAttributes(this, result); + } else { + result = super.copy(target, name, ext); + } + } catch (IOException ioe) { + extensions.copyFailure(this, to); + throw ioe; + } + extensions.copySuccess(this, to); + return result; + } + + @Override public final FileObject move(FileLock lock, FileObject target, String name, String ext) throws IOException { ProvidedExtensions extensions = getProvidedExtensions(); - File to = (target instanceof FolderObj) ? new File(((BaseFileObj) target).getFileName().getFile(), FileInfo.composeName(name, ext)) : - new File(FileUtil.toFile(target), FileInfo.composeName(name, ext)); + File to = getToFile(target, name, ext); extensions.beforeMove(this, to); FileObject result = null; @@ -253,8 +286,17 @@ } public BaseFileObj move(FileLock lock, FolderObj target, String name, String ext, ProvidedExtensions.IOHandler moveHandler) throws IOException { - moveHandler.handle(); - String nameExt = FileInfo.composeName(name,ext); + return handleMoveCopy(target, name, ext, moveHandler); + } + + private File getToFile(FileObject target, String name, String ext) { + File to = (target instanceof FolderObj) ? new File(((BaseFileObj) target).getFileName().getFile(), FileInfo.composeName(name, ext)) : new File(FileUtil.toFile(target), FileInfo.composeName(name, ext)); + return to; + } + + private BaseFileObj handleMoveCopy(FolderObj target, String name, String ext, IOHandler handler) throws IOException { + handler.handle(); + String nameExt = FileInfo.composeName(name, ext); target.getChildrenCache().getChild(nameExt, true); //TODO: review BaseFileObj result = null; @@ -268,9 +310,7 @@ result.fireFileFolderCreatedEvent(false); } break; - } - // #179109 - result is sometimes null, probably when moved file - // is not yet ready. We wait max. 1000 ms. + } // is not yet ready. We wait max. 1000 ms. try { Thread.sleep(100); } catch (InterruptedException ex) { @@ -288,7 +328,6 @@ return result; } - void rename(final FileLock lock, final String name, final String ext, final ProvidedExtensions.IOHandler handler) throws IOException { if (!checkLock(lock)) { FSException.io("EXC_InvalidLock", lock, getPath()); // NOI18N Index: masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java --- masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java Base (BASE) +++ masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java Locally Modified (Based On LOCAL) @@ -67,8 +67,26 @@ * @author Radek Matous */ public class ProvidedExtensions implements InterceptionListener { + /** * Return instance of {@link ProvidedExtensions.IOHandler} + * that is responsible for copying the file or null. + * + * Just the first non null instance of IOHandler is used by + * MasterFileSystem + * + * @param from file to be copied + * @param to target to copy this file to + * @return instance of {@link ProvidedExtensions.IOHandler} + * that is responsible for copying the file or null + */ + public ProvidedExtensions.IOHandler getCopyHandler( + File from, File to) { + return null; + } + + /** + * Return instance of {@link ProvidedExtensions.IOHandler} * that is responsible for moving the file or null. * * Just the first non null instance of IOHandler is used by @@ -170,6 +188,33 @@ /** * Called by MasterFileSystem before FileObject + * is copied + * @param from FileObject to be moved + * @param to File target to move this file to + * @since 2.28 + */ + public void beforeCopy(FileObject from, File to) {} + + /** + * Called by MasterFileSystem after FileObject + * was successfully copied + * @param from FileObject to be moved + * @param to File target to move this file to + * @since 2.28 + */ + public void copySuccess(FileObject from, File to) {} + + /** + * Called by MasterFileSystem after a FileObject + * copy failed + * @param from FileObject to be moved + * @param to File target to move this file to + * @since 2.28 + */ + public void copyFailure(FileObject from, File to) {} + + /** + * Called by MasterFileSystem before FileObject * is moved * @param from FileObject to be moved * @param to File target to move this file to Index: masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java --- masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java Base (BASE) +++ masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java Locally Modified (Based On LOCAL) @@ -336,6 +336,42 @@ } } + public void testCopy_BeforeSuccessFailure() throws IOException { + FileObject fromFolder = FileUtil.toFileObject(getWorkDir()).createFolder("copyFrom"); + FileObject toFolder = FileUtil.toFileObject(getWorkDir()).createFolder("copyTo"); + assertNotNull(fromFolder); + assertNotNull(toFolder); + FileObject fromCopy = fromFolder.createData("aa"); + assertNotNull(fromCopy); + iListener.clear(); + + assertNotNull(iListener); + assertEquals(0,iListener.beforeCopyCalls); + assertEquals(0,iListener.copySuccessCalls); + assertEquals(0,iListener.copyFailureCalls); + + // copy + fromCopy.copy(toFolder, fromCopy.getName(), fromCopy.getExt()); + assertTrue(fromCopy.isValid()); + assertEquals(1,iListener.beforeCopyCalls); + assertEquals(1,iListener.copySuccessCalls); + + iListener.clear(); + try { + // success + assertEquals(0,iListener.copySuccessCalls); + assertEquals(0,iListener.copyFailureCalls); + + // move to itself => failure + fromCopy.copy(toFolder, fromCopy.getName(), fromCopy.getExt()); + fail(); + } catch (IOException ex) { + // failure + assertEquals(0,iListener.copySuccessCalls); + assertEquals(1,iListener.copyFailureCalls); + } + } + public void testImplsRename2() throws IOException { final List events = new ArrayList(); FileObject fo = FileUtil.toFileObject(getWorkDir()); @@ -559,6 +595,9 @@ private int beforeMoveCalls; private int moveSuccessCalls; private int moveFailureCalls; + private int beforeCopyCalls; + private int copySuccessCalls; + private int copyFailureCalls; private static boolean implsMoveRetVal = true; private static boolean implsRenameRetVal = true; @@ -600,6 +639,9 @@ beforeMoveCalls = 0; moveSuccessCalls = 0; moveFailureCalls = 0; + beforeCopyCalls = 0; + copySuccessCalls = 0; + copyFailureCalls = 0; implsFileLockCalls = 0; implsCanWriteCalls = 0; } @@ -659,6 +701,18 @@ moveFailureCalls++; } + public void beforeCopy(FileObject fo, File to) { + beforeCopyCalls++; + } + + public void copySuccess(FileObject fo, File to) { + copySuccessCalls++; + } + + public void copyFailure(FileObject fo, File to) { + copyFailureCalls++; + } + public static void nextRefreshCall(File forDir, long retValue, File... toAdd) { refreshCallForDir = forDir; refreshCallRetValue = retValue;