Index: projectapi/apichanges.xml =================================================================== RCS file: /cvs/projects/projectapi/apichanges.xml,v retrieving revision 1.2 diff -u -r1.2 apichanges.xml --- projectapi/apichanges.xml 23 Dec 2004 22:01:28 -0000 1.2 +++ projectapi/apichanges.xml 13 May 2005 14:44:12 -0000 @@ -76,6 +76,30 @@ + + + The FileOwnerQuery.markExternalOwner allows registration of individal files and URIs + + + + +

+ This changes add a new method FileOwnerQuery.markExternalOwner(URI, FileObject, int), + and also extends sematics of the existing + FileOwnerQuery.markExternalOwner(FileObject, FileObject, int) method to allow registration + of individual files. +

+
+ +

+ It is possible to register owner of an individual file using FileOwnerQuery.markExternalOwner. + It is possible to register owner of a folder or file using URI, so the folder or file does not + have to exist at the time of registration. +

+
+ +
+ Switched to major release version 1 Index: projectapi/nbproject/project.properties =================================================================== RCS file: /cvs/projects/projectapi/nbproject/project.properties,v retrieving revision 1.10 diff -u -r1.10 project.properties --- projectapi/nbproject/project.properties 25 Apr 2005 12:44:42 -0000 1.10 +++ projectapi/nbproject/project.properties 13 May 2005 14:44:12 -0000 @@ -21,3 +21,4 @@ ${openide/masterfs.dir}/modules/org-netbeans-modules-masterfs.jar:\ ${core.dir}/core/core.jar:\ ${core.dir}/lib/boot.jar + Index: projectapi/src/org/netbeans/api/project/FileOwnerQuery.java =================================================================== RCS file: /cvs/projects/projectapi/src/org/netbeans/api/project/FileOwnerQuery.java,v retrieving revision 1.6 diff -u -r1.6 FileOwnerQuery.java --- projectapi/src/org/netbeans/api/project/FileOwnerQuery.java 13 Jan 2005 16:42:49 -0000 1.6 +++ projectapi/src/org/netbeans/api/project/FileOwnerQuery.java 13 May 2005 14:44:12 -0000 @@ -138,9 +138,9 @@ public static final int EXTERNAL_ALGORITHM_TRANSIENT = 0; /** - * Mark an external folder as being owned by a particular project. + * Mark an external folder or file as being owned by a particular project. * After this call is made, for the duration appropriate to the selected - * algorithm, that folder and its ancestors will be considered owned + * algorithm, that folder or file and its ancestors will be considered owned * by the project (if any) matching the named project directory, except in * the case that a lower enclosing project directory can be found. *

@@ -149,15 +149,15 @@ * algorithm is selected, or only when the project is created, if a reliable * persistent algorithm is selected. *

- * @param root a folder which should be considered part of a project + * @param root a folder or a file which should be considered part of a project * @param owner a project which should be considered to own that folder tree * (any prior marked external owner is overridden), * or null to cancel external ownership for this folder root * @param algorithm an algorithm to use for retaining this information; * currently may only be {@link #EXTERNAL_ALGORITHM_TRANSIENT} * @throws IllegalArgumentException if the root or owner is null, if an unsupported - * algorithm is requested, if the root is not a - * folder, if the root is already a project directory, + * algorithm is requested, + * if the root is already a project directory, * or if the root is already equal to or inside the owner's * project directory (it may however be an ancestor) * @see SourcesHelper @@ -173,8 +173,41 @@ } } - // XXX may need markExternalOwner(URI root, Project, int)? in case root dir does not exist yet... - // XXX is it useful to mark external owners for individual files? + /** + * Mark an external URI (folder or file) as being owned by a particular project. + * After this call is made, for the duration appropriate to the selected + * algorithm, that folder or file and its ancestors will be considered owned + * by the project (if any) matching the named project directory, except in + * the case that a lower enclosing project directory can be found. + *

+ * Typical usage would be to call this method for each external source root + * of a project (if any) as soon as the project is loaded, if a transient + * algorithm is selected, or only when the project is created, if a reliable + * persistent algorithm is selected. + *

+ * @param root an URI of a folder or a file which should be considered part of a project + * @param owner a project which should be considered to own that folder tree + * (any prior marked external owner is overridden), + * or null to cancel external ownership for this folder root + * @param algorithm an algorithm to use for retaining this information; + * currently may only be {@link #EXTERNAL_ALGORITHM_TRANSIENT} + * @throws IllegalArgumentException if the root or owner is null, if an unsupported + * algorithm is requested, + * if the root is already a project directory, + * or if the root is already equal to or inside the owner's + * project directory (it may however be an ancestor) + * @see SourcesHelper + */ + public static void markExternalOwner(URI root, Project owner, int algorithm) throws IllegalArgumentException { + switch (algorithm) { + case EXTERNAL_ALGORITHM_TRANSIENT: + // XXX check args + SimpleFileOwnerQueryImplementation.markExternalOwnerTransient(root, owner); + break; + default: + throw new IllegalArgumentException("No such algorithm: " + algorithm); // NOI18N + } + } /* TBD whether this is necessary: public static FileObject getMarkedExternalOwner(FileObject root) {} Index: projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java =================================================================== RCS file: /cvs/projects/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java,v retrieving revision 1.8 diff -u -r1.8 SimpleFileOwnerQueryImplementation.java --- projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java 27 Apr 2004 05:28:23 -0000 1.8 +++ projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java 13 May 2005 14:44:13 -0000 @@ -14,11 +14,13 @@ package org.netbeans.modules.projectapi; import java.io.IOException; +import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import org.netbeans.api.project.Project; @@ -54,31 +56,37 @@ } public Project getOwner(FileObject f) { - if (f.isData()) { - f = f.getParent(); - } while (f != null) { - Project p; - try { - p = ProjectManager.getDefault().findProject(f); - } catch (IOException e) { - // There is a project here, but we cannot load it... - ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); - return null; - } - if (p != null) { - return p; - } - FileObject externalOwner = (FileObject)externalOwners.get(f); - if (externalOwner != null) { + if (f.isFolder()) { + Project p; try { - // Note: will be null if there is no such project. - return ProjectManager.getDefault().findProject(externalOwner); + p = ProjectManager.getDefault().findProject(f); } catch (IOException e) { - // There is a project there, but we cannot load it... + // There is a project here, but we cannot load it... ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); return null; } + if (p != null) { + return p; + } + } + + WeakReference/**/ externalOwnersReference = + (WeakReference/**/) externalOwners.get(fileObject2URI(f)); + + if (externalOwnersReference != null) { + FileObject externalOwner = (FileObject) externalOwnersReference.get(); + + if (externalOwner != null) { + try { + // Note: will be null if there is no such project. + return ProjectManager.getDefault().findProject(externalOwner); + } catch (IOException e) { + // There is a project there, but we cannot load it... + ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); + return null; + } + } } f = f.getParent(); } @@ -88,7 +96,9 @@ /** * Map from external source roots to the owning project directories. */ - private static final Map/**/ externalOwners = + private static final Map/*>*/ externalOwners = + Collections.synchronizedMap(new WeakHashMap()); + private static final Map/**/ project2External = Collections.synchronizedMap(new WeakHashMap()); /** @see FileOwnerQuery#reset */ @@ -96,12 +106,35 @@ externalOwners.clear(); } + private static URI fileObject2URI(FileObject f) { + try { + return new URI(f.getURL().toString()); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + /** @see FileOwnerQuery#markExternalOwner */ public static void markExternalOwnerTransient(FileObject root, Project owner) { + markExternalOwnerTransient(fileObject2URI(root), owner); + } + + /** @see FileOwnerQuery#markExternalOwner */ + public static void markExternalOwnerTransient(URI root, Project owner) { if (owner != null) { - externalOwners.put(root, owner.getProjectDirectory()); + externalOwners.put(root, new WeakReference(owner.getProjectDirectory())); + project2External.put(owner.getProjectDirectory(), root); } else { - externalOwners.remove(root); + WeakReference/**/ ownerReference = (WeakReference/**/) externalOwners.remove(root); + + if (ownerReference != null) { + FileObject ownerFO = (FileObject) ownerReference.get(); + + if (ownerFO != null) { + project2External.remove(ownerFO); + } + } } } Index: projectapi/test/unit/src/org/netbeans/api/project/FileOwnerQueryTest.java =================================================================== RCS file: /cvs/projects/projectapi/test/unit/src/org/netbeans/api/project/FileOwnerQueryTest.java,v retrieving revision 1.7 diff -u -r1.7 FileOwnerQueryTest.java --- projectapi/test/unit/src/org/netbeans/api/project/FileOwnerQueryTest.java 5 Apr 2005 00:19:20 -0000 1.7 +++ projectapi/test/unit/src/org/netbeans/api/project/FileOwnerQueryTest.java 13 May 2005 14:44:13 -0000 @@ -12,8 +12,8 @@ */ package org.netbeans.api.project; - import java.io.OutputStream; +import java.lang.ref.WeakReference; import java.net.URI; import java.util.zip.CRC32; import java.util.zip.ZipEntry; @@ -22,7 +22,6 @@ import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; -import org.openide.util.Lookup; /** * Test functionality of FileOwnerQuery. @@ -156,6 +155,132 @@ assertEquals("but subprojects are not part of it", ProjectManager.getDefault().findProject(ext3subproj), p3); FileOwnerQuery.markExternalOwner(ext3, null, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); assertEquals("unmarking an owner works", null, FileOwnerQuery.getOwner(ext3)); + } + + public void testExternalOwnerFile() throws Exception { + FileObject ext1 = scratch.getFileObject("external1"); + FileObject extfile1 = ext1.getFileObject("subdir/file"); + assertEquals("no owner yet", null, FileOwnerQuery.getOwner(extfile1)); + FileOwnerQuery.markExternalOwner(extfile1, p, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("now have an owner", p, FileOwnerQuery.getOwner(extfile1)); + assertEquals("not for the projdir", null, FileOwnerQuery.getOwner(ext1)); + assertEquals("and not for something else", null, FileOwnerQuery.getOwner(scratch)); + FileObject ext2 = scratch.getFileObject("external2"); + FileObject extfile2 = ext2.getFileObject("subdir/file"); + assertEquals("no owner yet", null, FileOwnerQuery.getOwner(extfile2)); + Project p2 = ProjectManager.getDefault().findProject(subprojdir); + FileOwnerQuery.markExternalOwner(extfile2, p2, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("now have an owner", p2, FileOwnerQuery.getOwner(extfile2)); + assertEquals("not for the projdir", null, FileOwnerQuery.getOwner(ext2)); + assertEquals("and not for something else", null, FileOwnerQuery.getOwner(scratch)); + assertEquals("still correct for first proj", p, FileOwnerQuery.getOwner(extfile1)); + + //XXX: unmarking files. + } + + public void testExternalOwnerURI() throws Exception { + FileObject ext1 = scratch.getFileObject("external1"); + FileObject extfile1 = ext1.getFileObject("subdir/file"); + assertEquals("no owner yet through FileObjects", null, FileOwnerQuery.getOwner(extfile1)); + assertEquals("no owner yet through URI", null, FileOwnerQuery.getOwner(extfile1)); + FileOwnerQuery.markExternalOwner(fileObject2URI(ext1), p, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("now have an owner through FileObjects", p, FileOwnerQuery.getOwner(extfile1)); + assertEquals("now have an owner through URI", p, FileOwnerQuery.getOwner(fileObject2URI(extfile1))); + assertEquals("even for the projdir throught FileObjects", p, FileOwnerQuery.getOwner(ext1)); + assertEquals("even for the projdir throught URI", p, FileOwnerQuery.getOwner(fileObject2URI(ext1))); + assertEquals("but not for something else throught FileObjects", null, FileOwnerQuery.getOwner(scratch)); + assertEquals("but not for something else throught URI", null, FileOwnerQuery.getOwner(fileObject2URI(scratch))); + FileObject ext2 = scratch.getFileObject("external2"); + FileObject extfile2 = ext2.getFileObject("subdir/file"); + assertEquals("no owner yet through FileObjects", null, FileOwnerQuery.getOwner(extfile2)); + assertEquals("no owner yet through URI", null, FileOwnerQuery.getOwner(fileObject2URI(extfile2))); + Project p2 = ProjectManager.getDefault().findProject(subprojdir); + FileOwnerQuery.markExternalOwner(fileObject2URI(ext2), p2, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("now have an owner through FileObjects", p2, FileOwnerQuery.getOwner(extfile2)); + assertEquals("now have an owner through URI", p2, FileOwnerQuery.getOwner(fileObject2URI(extfile2))); + assertEquals("even for the projdir through FileObjects", p2, FileOwnerQuery.getOwner(ext2)); + assertEquals("even for the projdir through URI", p2, FileOwnerQuery.getOwner(ext2)); + assertEquals("but not for something else through FileObjects", null, FileOwnerQuery.getOwner(scratch)); + assertEquals("but not for something else through URI", null, FileOwnerQuery.getOwner(fileObject2URI(scratch))); + assertEquals("still correct for first proj through FileObjects", p, FileOwnerQuery.getOwner(extfile1)); + assertEquals("still correct for first proj through URI", p, FileOwnerQuery.getOwner(fileObject2URI(extfile1))); + FileObject ext3 = scratch.getFileObject("external3"); + assertEquals("no owner yet through FileObjects", null, FileOwnerQuery.getOwner(ext3)); + assertEquals("no owner yet through URI", null, FileOwnerQuery.getOwner(fileObject2URI(ext3))); + FileOwnerQuery.markExternalOwner(fileObject2URI(ext3), p, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("now have an owner through FileObjects", p, FileOwnerQuery.getOwner(ext3)); + assertEquals("now have an owner through URI", p, FileOwnerQuery.getOwner(fileObject2URI(ext3))); + FileObject ext3subproj = ext3.getFileObject("subproject"); + Project p3 = FileOwnerQuery.getOwner(ext3subproj); + assertNotSame("different project", p, p3); + assertEquals("but subprojects are not part of it", ProjectManager.getDefault().findProject(ext3subproj), p3); + FileOwnerQuery.markExternalOwner(fileObject2URI(ext3), null, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("unmarking an owner works through FileObjects", null, FileOwnerQuery.getOwner(ext3)); + assertEquals("unmarking an owner works through URI", null, FileOwnerQuery.getOwner(fileObject2URI(ext3))); + } + + public void testExternalOwnerFileURI() throws Exception { + FileObject ext1 = scratch.getFileObject("external1"); + FileObject extfile1 = ext1.getFileObject("subdir/file"); + assertEquals("no owner yet through FileObjects", null, FileOwnerQuery.getOwner(extfile1)); + assertEquals("no owner yet through URI", null, FileOwnerQuery.getOwner(fileObject2URI(extfile1))); + FileOwnerQuery.markExternalOwner(fileObject2URI(extfile1), p, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("now have an owner through FileObjects", p, FileOwnerQuery.getOwner(extfile1)); + assertEquals("now have an owner through URI", p, FileOwnerQuery.getOwner(fileObject2URI(extfile1))); + assertEquals("not for the projdir through FileObjects", null, FileOwnerQuery.getOwner(ext1)); + assertEquals("not for the projdir through URI", null, FileOwnerQuery.getOwner(fileObject2URI(ext1))); + assertEquals("and not for something else through FileObjects", null, FileOwnerQuery.getOwner(scratch)); + assertEquals("and not for something else through URI", null, FileOwnerQuery.getOwner(fileObject2URI(scratch))); + FileObject ext2 = scratch.getFileObject("external2"); + FileObject extfile2 = ext2.getFileObject("subdir/file"); + assertEquals("no owner yet through FileObjects", null, FileOwnerQuery.getOwner(extfile2)); + assertEquals("no owner yet through URI", null, FileOwnerQuery.getOwner(fileObject2URI(extfile2))); + Project p2 = ProjectManager.getDefault().findProject(subprojdir); + FileOwnerQuery.markExternalOwner(fileObject2URI(extfile2), p2, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + assertEquals("now have an owner through FileObjects", p2, FileOwnerQuery.getOwner(extfile2)); + assertEquals("now have an owner through URI", p2, FileOwnerQuery.getOwner(fileObject2URI(extfile2))); + assertEquals("not for the projdir through FileObjects", null, FileOwnerQuery.getOwner(ext2)); + assertEquals("not for the projdir through URI", null, FileOwnerQuery.getOwner(fileObject2URI(ext2))); + assertEquals("and not for something else through FileObjects", null, FileOwnerQuery.getOwner(scratch)); + assertEquals("and not for something else through URI", null, FileOwnerQuery.getOwner(fileObject2URI(scratch))); + assertEquals("still correct for first proj through FileObjects", p, FileOwnerQuery.getOwner(extfile1)); + assertEquals("still correct for first proj through URI", p, FileOwnerQuery.getOwner(fileObject2URI(extfile1))); + + //XXX: unmarking files. + } + + public void testIsProjectDirCollectable() throws Exception { + Project p2 = ProjectManager.getDefault().findProject(subprojdir); + FileObject root = p2.getProjectDirectory(); + FileObject ext2 = scratch.getFileObject("external2"); + FileObject extfile2 = ext2.getFileObject("subdir/file"); + + FileOwnerQuery.markExternalOwner(fileObject2URI(extfile2), p2, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT); + + WeakReference p2WR = new WeakReference(p2); + WeakReference rootWR = new WeakReference(root); + + p2 = null; + root = null; + ext2 = null; + extfile2 = null; + subprojdir = null; + subprojfile = null; + + //there are timed references holding the project, let them time-out. + Thread.sleep(30000); + + assertGC("project 2 collected", p2WR); + assertGC("project 2's project dir collected", rootWR); + } + + private static URI fileObject2URI(FileObject f) { + try { + return new URI(f.getURL().toString()); + } catch (Exception e) { + e.printStackTrace(); + return null; + } } // XXX test URI usage of external owner