diff -r 2195ee50dfe0 masterfs/apichanges.xml --- a/masterfs/apichanges.xml Sun Apr 11 14:14:55 2010 +0200 +++ b/masterfs/apichanges.xml Sun Apr 11 15:20:42 2010 +0200 @@ -46,6 +46,23 @@ MasterFileSystem API + + + ProvidedExtensions.refreshRecursively was added. + + + + + +

+ ProvidedExtensions.refreshRecursively allows + various version control providers to get better control + on behavior of recursive listener. +

+
+ + +
ProvidedExtensions.canWrite was added. diff -r 2195ee50dfe0 masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java --- a/masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java Sun Apr 11 14:14:55 2010 +0200 +++ b/masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java Sun Apr 11 15:20:42 2010 +0200 @@ -45,6 +45,7 @@ import java.io.IOException; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; @@ -268,6 +269,24 @@ } return null; } + + @Override + public long refreshRecursively(File dir, long lastTimeStamp, List children) { + for (Iterator it = annotationProviders.iterator(); it.hasNext();) { + AnnotationProvider provider = (AnnotationProvider) it.next(); + final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null; + if (iListener instanceof ProvidedExtensions) { + ProvidedExtensions pe = (ProvidedExtensions)iListener; + int prev = children.size(); + long ret = pe.refreshRecursively(dir, lastTimeStamp, children); + assert ret != -1 || prev == children.size() : "When returning -1 from refreshRecursively, you cannot modify children: " + pe; + if (ret != -1) { + return ret; + } + } + } + return super.refreshRecursively(dir, lastTimeStamp, children); + } public static void checkReentrancy() { if (reentrantCheck.get() != null) { diff -r 2195ee50dfe0 masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FileObjectKeeper.java --- a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FileObjectKeeper.java Sun Apr 11 14:14:55 2010 +0200 +++ b/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FileObjectKeeper.java Sun Apr 11 15:20:42 2010 +0200 @@ -41,8 +41,9 @@ import java.io.File; import java.util.Collection; -import java.util.Enumeration; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArraySet; @@ -61,7 +62,7 @@ final class FileObjectKeeper implements FileChangeListener { private static final Logger LOG = Logger.getLogger(FileObjectKeeper.class.getName()); - private Set kept; + private Set kept; private Collection listeners; private final FolderObj root; private long timeStamp; @@ -94,47 +95,47 @@ } } - public void init(long previous, FileObjectFactory factory, boolean expected) { + public List init(long previous, FileObjectFactory factory, boolean expected) { File file = root.getFileName().getFile(); - File[] arr = file.listFiles(); - long ts = 0; - if (arr != null) { - for (File f : arr) { - if (f.isDirectory()) { - continue; - } - long lm = f.lastModified(); - LOG.log(Level.FINE, " check {0} for {1}", new Object[] { lm, f }); - if (lm > ts) { - ts = lm; - } - if (lm > previous && factory != null) { - final BaseFileObj prevFO = factory.getCachedOnly(f); - if (prevFO == null) { - BaseFileObj who = factory.getValidFileObject(f, Caller.Others); - if (who != null) { - LOG.log(Level.FINE, "External change detected {0}", who); //NOI18N - who.fireFileChangedEvent(expected); - } else { - LOG.log(Level.FINE, "Cannot get valid FileObject. File probably removed: {0}", f); //NOI18N - } + LinkedList arr = new LinkedList(); + long ts = root.getProvidedExtensions().refreshRecursively(file, previous, arr); + for (File f : arr) { + if (f.isDirectory()) { + continue; + } + long lm = f.lastModified(); + LOG.log(Level.FINE, " check {0} for {1}", new Object[] { lm, f }); + if (lm > ts) { + ts = lm; + } + if (lm > previous && factory != null) { + final BaseFileObj prevFO = factory.getCachedOnly(f); + if (prevFO == null) { + BaseFileObj who = factory.getValidFileObject(f, Caller.Others); + if (who != null) { + LOG.log(Level.FINE, "External change detected {0}", who); //NOI18N + who.fireFileChangedEvent(expected); } else { - LOG.log(Level.FINE, "Do classical refresh for {0}", prevFO); //NOI18N - prevFO.refresh(expected, true); + LOG.log(Level.FINE, "Cannot get valid FileObject. File probably removed: {0}", f); //NOI18N } + } else { + LOG.log(Level.FINE, "Do classical refresh for {0}", prevFO); //NOI18N + prevFO.refresh(expected, true); } } } timeStamp = ts; LOG.log(Level.FINE, "Testing {0}, time {1}", new Object[] { file, timeStamp }); + return arr; } - private void listenTo(FileObject fo, boolean add) { + private void listenTo(FolderObj fo, boolean add, Collection children) { if (add) { fo.addFileChangeListener(this); if (fo instanceof FolderObj) { FolderObj folder = (FolderObj)fo; - folder.getKeeper(); + folder.getKeeper(children); + folder.getChildren(); kept.add(folder); } LOG.log(Level.FINER, "Listening to {0}", fo); @@ -147,11 +148,19 @@ private void listenToAll(Callable stop) { assert Thread.holdsLock(this); assert kept == null; - kept = new HashSet(); - listenTo(root, true); - Enumeration en = root.getChildren(true); - while (en.hasMoreElements()) { - FileObject fo = en.nextElement(); + kept = new HashSet(); + LinkedList it = new LinkedList(); + listenTo(root, true, it); + FileObjectFactory factory = null; + for (;;) { + File f = it.poll(); + if (f == null) { + break; + } + if (factory == null) { + factory = FileObjectFactory.getInstance(f); + } + FileObject fo = factory.getValidFileObject(f, Caller.Others); if (fo instanceof FolderObj) { FolderObj obj = (FolderObj) fo; Object shallStop = null; @@ -166,7 +175,7 @@ LOG.log(Level.INFO, "addRecursiveListener to {0} interrupted", root); // NOI18N return; } - listenTo(obj, true); + listenTo(obj, true, it); } } } @@ -174,11 +183,11 @@ private void listenNoMore() { assert Thread.holdsLock(this); - listenTo(root, false); - Set k = kept; + listenTo(root, false, null); + Set k = kept; if (k != null) { - for (FileObject fo : k) { - listenTo(fo, false); + for (FolderObj fo : k) { + listenTo(fo, false, null); } kept = null; } @@ -187,15 +196,24 @@ @Override public void fileFolderCreated(FileEvent fe) { Collection arr = listeners; - final FileObject f = fe.getFile(); - if (f instanceof FolderObj) { + final FileObject folder = fe.getFile(); + if (folder instanceof FolderObj) { + FolderObj obj = (FolderObj)folder; synchronized (this) { - listenTo(f, true); - Enumeration en = f.getChildren(true); - while (en.hasMoreElements()) { - FileObject fo = en.nextElement(); + LinkedList it = new LinkedList(); + listenTo(obj, true, it); + FileObjectFactory factory = null; + for (;;) { + File f = it.poll(); + if (f == null) { + break; + } + if (factory == null) { + factory = FileObjectFactory.getInstance(f); + } + FileObject fo = factory.getValidFileObject(f, Caller.Others); if (fo instanceof FolderObj) { - listenTo(fo, true); + listenTo((FolderObj)fo, true, it); } } } @@ -240,11 +258,12 @@ } if (f instanceof FolderObj) { + FolderObj obj = (FolderObj)f; synchronized (this) { if (kept != null) { - kept.remove(f); + kept.remove(obj); } - listenTo(f, false); + listenTo(obj, false, null); } } if (arr == null) { diff -r 2195ee50dfe0 masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java --- a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java Sun Apr 11 14:14:55 2010 +0200 +++ b/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java Sun Apr 11 15:20:42 2010 +0200 @@ -48,6 +48,7 @@ import java.io.OutputStream; import java.io.SyncFailedException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -70,7 +71,6 @@ import org.openide.filesystems.FileChangeListener; import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; -import org.openide.util.Exceptions; import org.openide.util.Mutex; /** @@ -520,22 +520,28 @@ return folderChildren; } - synchronized FileObjectKeeper getKeeper() { + synchronized FileObjectKeeper getKeeper(Collection arr) { if (keeper == null) { keeper = new FileObjectKeeper(this); - keeper.init(-1, null, false); + List ch = keeper.init(-1, null, false); + if (arr != null) { + arr.addAll(ch); + } + } else if (arr != null) { + List ch = keeper.init(keeper.childrenLastModified(), null, false); + arr.addAll(ch); } return keeper; } @Override public final void addRecursiveListener(FileChangeListener fcl) { - getKeeper().addRecursiveListener(fcl); + getKeeper(null).addRecursiveListener(fcl); } @Override public final void removeRecursiveListener(FileChangeListener fcl) { - getKeeper().removeRecursiveListener(fcl); + getKeeper(null).removeRecursiveListener(fcl); } diff -r 2195ee50dfe0 masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java --- a/masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java Sun Apr 11 14:14:55 2010 +0200 +++ b/masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java Sun Apr 11 15:20:42 2010 +0200 @@ -43,6 +43,8 @@ import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.List; import org.openide.filesystems.FileObject; /** @@ -187,4 +189,26 @@ public Object getAttribute(File file, String attrName) { return null; } + + /** Allows versioning system to exclude some children from recursive + * listening check. Also notifies the versioning whenever a refresh + * is required and allows the versiniong to provide special timestamp + * for a directory. + * + * @param dir the directory to check timestamp for + * @param lastTimeStamp the previously known timestamp or -1 + * @param children add children that shall be interated into this array + * @return the timestamp that shall represent this directory, it will + * be compared with timestamps of all children and the newest + * one will be kept and next time passed as lastTimeStamp. Return + * 0 if the directory does not have any special timestamp + * @since 2.23 + */ + public long refreshRecursively(File dir, long lastTimeStamp, List children) { + final File[] arr = dir.listFiles(); + if (arr != null) { + children.addAll(Arrays.asList(arr)); + } + return 0; + } } diff -r 2195ee50dfe0 masterfs/test/unit/src/org/netbeans/modules/masterfs/filebasedfs/FileUtilAddRecursiveListenerTest.java --- a/masterfs/test/unit/src/org/netbeans/modules/masterfs/filebasedfs/FileUtilAddRecursiveListenerTest.java Sun Apr 11 14:14:55 2010 +0200 +++ b/masterfs/test/unit/src/org/netbeans/modules/masterfs/filebasedfs/FileUtilAddRecursiveListenerTest.java Sun Apr 11 15:20:42 2010 +0200 @@ -44,20 +44,28 @@ import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; +import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; import org.netbeans.junit.RandomlyFails; +import org.netbeans.modules.masterfs.filebasedfs.fileobjects.BaseFileObj; import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.test.TestFileUtils; import org.netbeans.modules.masterfs.filebasedfs.FileUtilTest.EventType; import org.netbeans.modules.masterfs.filebasedfs.FileUtilTest.TestFileChangeListener; +import org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileObjectFactory; import org.netbeans.modules.masterfs.filebasedfs.fileobjects.TestUtils; +import org.netbeans.modules.masterfs.providers.ProvidedExtensionsTest; /** * @author Jiri Skrivanek */ public class FileUtilAddRecursiveListenerTest extends NbTestCase { + static { + MockServices.setServices(ProvidedExtensionsTest.AnnotationProviderImpl.class); + } + private final Logger LOG; public FileUtilAddRecursiveListenerTest(String name) { @@ -66,6 +74,11 @@ } @Override + protected void setUp() throws Exception { + clearWorkDir(); + } + + @Override protected Level logLevel() { return Level.FINER; } @@ -276,4 +289,41 @@ LOG.info("OK"); } + + public void testProvideExtensionsRefreshRecursively() throws Exception { + File root = new File(getWorkDir(), "root"); + final File sub = new File(root, "sub"); + final File subsub = new File(sub, "subsub"); + File subsubdir = new File(subsub, "dir"); + subsubdir.mkdirs(); + File subfile = new File(sub, "file"); + subfile.createNewFile(); + File deepfile = new File(subsubdir, "deep"); + deepfile.createNewFile(); + + ProvidedExtensionsTest.ProvidedExtensionsImpl.nextRefreshCall( + sub, + Long.MAX_VALUE - 10, + subfile + ); + + + TestFileChangeListener fcl = new TestFileChangeListener(); + FileObject rf = FileUtil.toFileObject(root); + rf.addRecursiveListener(fcl); + + BaseFileObj noFO = FileObjectFactory.getInstance(root).getCachedOnly(subsubdir); + assertNull("subsub directory has been skipped", noFO); + + assertEquals("No events", 0, fcl.checkAll()); + LOG.log(Level.INFO, "Touching subfile: {0}", deepfile); + TestFileUtils.touch(deepfile, null); + LOG.log(Level.INFO, "Will do refresh, lastModified: {0}", deepfile.lastModified()); + FileUtil.refreshFor(root); + LOG.info("Refresh done"); + fcl.check(EventType.ATTRIBUTE_CHANGED); // ignore if any + + fcl.printAll(LOG); + assertEquals("No other events", 0, fcl.checkAll()); + } } diff -r 2195ee50dfe0 masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java --- a/masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java Sun Apr 11 14:14:55 2010 +0200 +++ b/masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java Sun Apr 11 15:20:42 2010 +0200 @@ -49,6 +49,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -450,6 +451,9 @@ } public static class ProvidedExtensionsImpl extends ProvidedExtensions { + private static File refreshCallForDir; + private static long refreshCallRetValue; + private static List refreshCallToAdd; private int implsMoveCalls; private int moveImplCalls; private int implsRenameCalls; @@ -522,7 +526,26 @@ public void beforeChange(FileObject f) { assertNotNull(FileUtil.toFile(f)); implsBeforeChangeCalls++; - } + } + + public static void nextRefreshCall(File forDir, long retValue, File... toAdd) { + refreshCallForDir = forDir; + refreshCallRetValue = retValue; + refreshCallToAdd = Arrays.asList(toAdd); + } + + @Override + public long refreshRecursively(File dir, long lastTimeStamp, List children) { + if (dir.equals(refreshCallForDir)) { + children.addAll(refreshCallToAdd); + long r = refreshCallRetValue; + refreshCallForDir = null; + refreshCallToAdd = null; + refreshCallRetValue = -1; + return r; + } + return -1; + } @Override public ProvidedExtensions.DeleteHandler getDeleteHandler(File f) {