Allow modules to dynamically add/remove layer content
diff -r a9b41094a261 -r ea72e89cae6d openide.filesystems/nbproject/project.properties
--- a/openide.filesystems/nbproject/project.properties Thu Aug 20 17:55:11 2009 +0200
+++ b/openide.filesystems/nbproject/project.properties Wed Aug 26 11:49:55 2009 +0200
@@ -44,4 +44,4 @@
javadoc.main.page=org/openide/filesystems/doc-files/api.html
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml
-spec.version.base=7.23.0
+spec.version.base=7.24.0
diff -r a9b41094a261 -r ea72e89cae6d openide.filesystems/src/org/openide/filesystems/ExternalUtil.java
--- a/openide.filesystems/src/org/openide/filesystems/ExternalUtil.java Thu Aug 20 17:55:11 2009 +0200
+++ b/openide.filesystems/src/org/openide/filesystems/ExternalUtil.java Wed Aug 26 11:49:55 2009 +0200
@@ -112,7 +112,7 @@
return orig;
}
- private static Logger LOG = Logger.getLogger("org.openide.filesystems"); // NOI18N
+ final static Logger LOG = Logger.getLogger("org.openide.filesystems"); // NOI18N
/** Logs a text.
*/
public static void log(String msg) {
diff -r a9b41094a261 -r ea72e89cae6d openide.filesystems/src/org/openide/filesystems/FileObject.java
--- a/openide.filesystems/src/org/openide/filesystems/FileObject.java Thu Aug 20 17:55:11 2009 +0200
+++ b/openide.filesystems/src/org/openide/filesystems/FileObject.java Wed Aug 26 11:49:55 2009 +0200
@@ -58,7 +58,9 @@
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
+import java.util.logging.Level;
import org.openide.util.Enumerations;
+import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.UserQuestionException;
@@ -413,6 +415,53 @@
*/
public abstract void removeFileChangeListener(FileChangeListener fcl);
+
+ /** Adds a listener to this {@link FileObject} and all its children and
+ * children or its children.
+ * It is guaranteed that whenever a change
+ * is made via the FileSystem API itself under this {@link FileObject}
+ * that it is notified to the fcl
listener. Whether external
+ * changes (if they make sense) are detected and
+ * notified depends on actual implementation. As some implementations may
+ * need to perform non-trivial amount of work during initialization of
+ * listeners, this methods can take long time. Usage of this method may
+ * consume a lot of system resources and as such it shall be used with care.
+ * Traditional {@link #addFileChangeListener(org.openide.filesystems.FileChangeListener)}
+ * is definitely preferred variant.
+ *
+ * If you are running with the MasterFS module enabled, it guarantees
+ * that for files backed with real {@link File}, the system initializes
+ * itself to detect external changes on the whole subtree.
+ * This requires non-trivial amount of work and especially on slow
+ * disks (aka networks ones) may take a long time to add the listener
+ * and also refresh the system when {@link FileObject#refresh()}
+ * and especially {@link FileUtil#refreshAll()} is requested.
+ *
+ *
+ * @param fcl the listener to register
+ * @since 7.24
+ */
+ public void addRecursiveListener(FileChangeListener fcl) {
+ try {
+ getFileSystem().addFileChangeListener(new RecursiveListener(this, fcl));
+ } catch (FileStateInvalidException ex) {
+ ExternalUtil.LOG.log(Level.FINE, "Cannot remove listener from " + this, ex);
+ }
+ }
+
+ /** Removes listener previously added by {@link #addRecursiveListener(org.openide.filesystems.FileChangeListener)}
+ *
+ * @param fcl the listener to remove
+ * @since 7.24
+ */
+ public void removeRecursiveListener(FileChangeListener fcl) {
+ try {
+ getFileSystem().removeFileChangeListener(new RecursiveListener(this, fcl));
+ } catch (FileStateInvalidException ex) {
+ ExternalUtil.LOG.log(Level.FINE, "Cannot remove listener from " + this, ex);
+ }
+ }
+
/** Fire data creation event.
* @param en listeners that should receive the event
* @param fe the event to fire in this object
diff -r a9b41094a261 -r ea72e89cae6d openide.filesystems/src/org/openide/filesystems/RecursiveListener.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/openide.filesystems/src/org/openide/filesystems/RecursiveListener.java Wed Aug 26 11:49:55 2009 +0200
@@ -0,0 +1,127 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.openide.filesystems;
+
+import java.lang.ref.WeakReference;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class RecursiveListener extends WeakReference
+implements FileChangeListener {
+ private final FileChangeListener fcl;
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final RecursiveListener other = (RecursiveListener) obj;
+ if (this.fcl != other.fcl && (this.fcl == null || !this.fcl.equals(other.fcl))) {
+ return false;
+ }
+ final FileObject otherFo = other.get();
+ final FileObject thisFo = this.get();
+ if (thisFo != otherFo && (thisFo == null || !thisFo.equals(otherFo))) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final FileObject thisFo = this.get();
+ int hash = 3;
+ hash = 37 * hash + (this.fcl != null ? this.fcl.hashCode() : 0);
+ hash = 13 * hash + (thisFo != null ? thisFo.hashCode() : 0);
+ return hash;
+ }
+
+ public RecursiveListener(FileObject source, FileChangeListener fcl) {
+ super(source);
+ this.fcl = fcl;
+ }
+
+ public void fileRenamed(FileRenameEvent fe) {
+ FileObject thisFo = this.get();
+ if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
+ fcl.fileRenamed(fe);
+ }
+ }
+
+ public void fileFolderCreated(FileEvent fe) {
+ FileObject thisFo = this.get();
+ if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
+ fcl.fileFolderCreated(fe);
+ }
+ }
+
+ public void fileDeleted(FileEvent fe) {
+ FileObject thisFo = this.get();
+ if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
+ fcl.fileDeleted(fe);
+ }
+ }
+
+ public void fileDataCreated(FileEvent fe) {
+ FileObject thisFo = this.get();
+ if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
+ fcl.fileDataCreated(fe);
+ }
+ }
+
+ public void fileChanged(FileEvent fe) {
+ FileObject thisFo = this.get();
+ if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
+ fcl.fileChanged(fe);
+ }
+ }
+
+ public void fileAttributeChanged(FileAttributeEvent fe) {
+ FileObject thisFo = this.get();
+ if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
+ fcl.fileAttributeChanged(fe);
+ }
+ }
+}
diff -r a9b41094a261 -r ea72e89cae6d openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java
--- a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java Thu Aug 20 17:55:11 2009 +0200
+++ b/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java Wed Aug 26 11:49:55 2009 +0200
@@ -1569,6 +1569,104 @@
assertNotNull(child);
}
}
+
+ public void testRecursiveListener() throws IOException {
+ checkSetUp();
+
+ FileObject folder1 = getTestFolder1 (root);
+ /** delete first time*/
+ try {
+ FileObject obj = FileUtil.createData(folder1, "my/sub/children/children.java");
+ FileObject sub = obj.getParent().getParent();
+
+ class L implements FileChangeListener {
+ StringBuilder sb = new StringBuilder();
+
+ public void fileFolderCreated(FileEvent fe) {
+ sb.append("FolderCreated");
+ }
+
+ public void fileDataCreated(FileEvent fe) {
+ sb.append("DataCreated");
+ }
+
+ public void fileChanged(FileEvent fe) {
+ sb.append("Changed");
+ }
+
+ public void fileDeleted(FileEvent fe) {
+ sb.append("Deleted");
+ }
+
+ public void fileRenamed(FileRenameEvent fe) {
+ sb.append("Renamed");
+ }
+
+ public void fileAttributeChanged(FileAttributeEvent fe) {
+ sb.append("AttributeChanged");
+ }
+
+ public void assertMessages(String txt, String msg) {
+ assertEquals(txt, msg, sb.toString());
+ sb.setLength(0);
+ }
+ }
+ L recursive = new L();
+ L flat = new L();
+
+ sub.addFileChangeListener(flat);
+ sub.addRecursiveListener(recursive);
+
+ FileObject fo = obj.getParent().createData("sibling.java");
+
+ flat.assertMessages("No messages in flat mode", "");
+ recursive.assertMessages("Creation", "DataCreated");
+
+ fo.setAttribute("jarda", "hello");
+
+ flat.assertMessages("No messages in flat mode", "");
+ recursive.assertMessages("attr", "AttributeChanged");
+
+ final OutputStream os = fo.getOutputStream();
+ os.write(10);
+ os.close();
+
+ flat.assertMessages("No messages in flat mode", "");
+ recursive.assertMessages("written", "Changed");
+
+ fo.delete();
+
+ flat.assertMessages("No messages in flat mode", "");
+ recursive.assertMessages("gone", "Deleted");
+
+ sub.createFolder("testFolder");
+
+ flat.assertMessages("Direct Folder notified", "FolderCreated");
+ recursive.assertMessages("Direct Folder notified", "FolderCreated");
+
+ sub.getParent().createData("unimportant.txt");
+
+ flat.assertMessages("No messages in flat mode", "");
+ recursive.assertMessages("No messages in recursive mode", "");
+
+ sub.removeRecursiveListener(recursive);
+
+ sub.createData("test.data");
+
+ flat.assertMessages("Direct file notified", "DataCreated");
+ recursive.assertMessages("No longer active", "");
+
+ WeakReference ref = new WeakReference(recursive);
+ recursive = null;
+ assertGC("Listener can be GCed", ref);
+
+ } catch (IOException iex) {
+ if (fs.isReadOnly() || root.isReadOnly()) return;
+ throw iex;
+ } finally {
+ }
+ }
+
/** Test of delete method, of class org.openide.filesystems.FileObject. */
public void testCreateDeleteFolderCreate () throws IOException {