Index: projects/projectapi/src/org/netbeans/api/project/ProjectManager.java =================================================================== RCS file: /cvs/projects/projectapi/src/org/netbeans/api/project/ProjectManager.java,v retrieving revision 1.21 diff -u -r1.21 ProjectManager.java --- projects/projectapi/src/org/netbeans/api/project/ProjectManager.java 4 Apr 2006 04:05:56 -0000 1.21 +++ projects/projectapi/src/org/netbeans/api/project/ProjectManager.java 13 Jun 2006 05:43:53 -0000 @@ -15,13 +15,16 @@ import java.io.IOException; import java.lang.ref.Reference; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import org.netbeans.modules.projectapi.TimedWeakReference; +import org.netbeans.spi.project.NestedProjectFactory; import org.netbeans.spi.project.ProjectFactory; import org.netbeans.spi.project.ProjectState; import org.openide.ErrorManager; @@ -348,6 +351,14 @@ * @throws IllegalArgumentException if the supplied file object is null or not a folder */ public boolean isProject(final FileObject projectDirectory) throws IllegalArgumentException { + return isProject (projectDirectory, false); + } + + public boolean isNestedProject(final FileObject projectDirectory) throws IllegalArgumentException { + return isProject (projectDirectory, true); + } + + private boolean isProject(final FileObject projectDirectory, final boolean nested) throws IllegalArgumentException { if (projectDirectory == null) { throw new IllegalArgumentException("Attempted to pass a null directory to isProject"); // NOI18N } @@ -380,7 +391,9 @@ } boolean resetLP = false; try { - boolean p = checkForProject(projectDirectory); + boolean p = nested ? checkForNestedProject(projectDirectory) : + checkForProject(projectDirectory); + synchronized (dir2Proj) { resetLP = true; dir2Proj.notifyAll(); @@ -402,6 +415,23 @@ } })).booleanValue(); } + + public Project[] getNestedProjects(FileObject fld) { + FileObject[] kids = fld.getChildren(); + List result = new ArrayList(3); + for (int i = 0; i < kids.length; i++) { + if (isNestedProject(kids[i])) { + try { + result.add (createProject(kids[i])); + } catch (IOException ioe) { + ErrorManager.getDefault().notify (ErrorManager.WARNING, + ioe); + } + } + } + Project[] p = (Project[]) result.toArray(new Project[result.size()]); + return p; + } private boolean checkForProject(FileObject dir) { assert dir != null; @@ -416,6 +446,23 @@ } return false; } + + private boolean checkForNestedProject(FileObject dir) { + assert dir != null; + assert dir.isFolder() : dir; + assert mutex().isReadAccess(); + Iterator it = factories.allInstances().iterator(); + while (it.hasNext()) { + ProjectFactory factory = (ProjectFactory)it.next(); + if (factory instanceof NestedProjectFactory && + ((NestedProjectFactory)factory).isNestedProject(dir)) { + + return true; + } + } + return false; + } + /** * Clear the cached list of folders thought not to be projects. Index: projects/projectapi/src/org/netbeans/spi/project/NestedProjectFactory.java =================================================================== RCS file: projects/projectapi/src/org/netbeans/spi/project/NestedProjectFactory.java diff -N projects/projectapi/src/org/netbeans/spi/project/NestedProjectFactory.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ projects/projectapi/src/org/netbeans/spi/project/NestedProjectFactory.java 13 Jun 2006 05:43:53 -0000 @@ -0,0 +1,26 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project; + +import org.openide.filesystems.FileObject; + +/** + * ProjectFactory which allows projects to say if they should be exposed + * under the node of the project directory below them (if any). + * + * @author Tim Boudreau + */ +public interface NestedProjectFactory extends ProjectFactory { + public boolean isNestedProject (FileObject dir); +} Index: projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java =================================================================== RCS file: /cvs/projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java,v retrieving revision 1.37 diff -u -r1.37 ProjectsRootNode.java --- projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java 4 Apr 2006 04:05:57 -0000 1.37 +++ projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java 13 Jun 2006 05:43:58 -0000 @@ -16,6 +16,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.CharConversionException; +import java.io.IOException; import java.lang.ref.WeakReference; import java.text.MessageFormat; import java.util.ArrayList; @@ -33,6 +34,7 @@ import javax.swing.event.ChangeListener; import org.netbeans.api.project.FileOwnerQuery; import org.netbeans.api.project.Project; +import org.netbeans.api.project.ProjectManager; import org.netbeans.api.project.ProjectUtils; import org.netbeans.api.project.Sources; import org.netbeans.spi.project.ui.LogicalViewProvider; @@ -40,7 +42,6 @@ import org.openide.filesystems.FileObject; import org.openide.filesystems.Repository; import org.openide.loaders.DataFolder; -import org.openide.loaders.DataObject; import org.openide.loaders.FolderLookup; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; @@ -239,7 +240,9 @@ } } else { - nodes = new Node[] { lvp.createLogicalView() }; + Node logical = lvp.createLogicalView(); + nodes = new Node[] { new FilterNode ( logical, + new SubprojectFilterChildren( logical, project )) }; if (nodes[0].getLookup().lookup(Project.class) != project) { // Various actions, badging, etc. are not going to work. ErrorManager.getDefault().log(ErrorManager.WARNING, "Warning - project " + ProjectUtils.getInformation(project).getName() + " failed to supply itself in the lookup of the root node of its own logical view"); // NOI18N @@ -377,5 +380,41 @@ } } - + + private static final class SubprojectFilterChildren extends Children.Keys { + private final Node orig; + private final Project proj; + SubprojectFilterChildren (Node orig, Project p) { + this.orig = orig; + this.proj = p; + } + + protected Node[] createNodes(Object key) { + Node n = (Node) key; + return new Node[] { ( + n.getChildren() instanceof SubprojectFilterChildren ? + n : new FilterNode ( n )) }; + } + + protected void addNotify() { + final List keys = new ArrayList ( + Arrays.asList(orig.getChildren().getNodes())); + + final FileObject[] kids = proj.getProjectDirectory().getChildren(); + final ProjectManager mgr = ProjectManager.getDefault(); + Project[] p = mgr.getNestedProjects(proj.getProjectDirectory()); + for (int i = 0; i < kids.length; i++) { + LogicalViewProvider lvp = (LogicalViewProvider) + p[i].getLookup().lookup ( + LogicalViewProvider.class ); + + if (lvp != null) { + Node n = lvp.createLogicalView(); + keys.add ( new FilterNode ( n, + new SubprojectFilterChildren( n, p[i] )) ); + } + } + setKeys (keys); + } + } }