Index: apichanges.xml =================================================================== RCS file: /cvs/projects/projectuiapi/apichanges.xml,v retrieving revision 1.22 diff -u -r1.22 apichanges.xml --- apichanges.xml 30 Jun 2006 21:27:26 -0000 1.22 +++ apichanges.xml 28 Aug 2006 08:15:30 -0000 @@ -81,6 +81,25 @@ + + + Ability to construct project node's children from multiple sources. + + + + + + + Added way to declaratively layout the subnodes of the project node, allowing 3rd party contributions + to project's Logical/Projects View. + The project's node is constructed based on layer folder content. This api change provides just + the infrastructure, the actual extension point location is up to the project type implementations. + + + + + + Index: nbproject/project.properties =================================================================== RCS file: /cvs/projects/projectuiapi/nbproject/project.properties,v retrieving revision 1.23 diff -u -r1.23 project.properties --- nbproject/project.properties 22 Aug 2006 20:48:33 -0000 1.23 +++ nbproject/project.properties 28 Aug 2006 08:15:30 -0000 @@ -15,7 +15,7 @@ # Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun # Microsystems, Inc. All Rights Reserved. -spec.version.base=1.16.0 +spec.version.base=1.17.0 is.autoload=true javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml Index: src/org/netbeans/spi/project/ui/support/NodeFactory.java =================================================================== RCS file: src/org/netbeans/spi/project/ui/support/NodeFactory.java diff -N src/org/netbeans/spi/project/ui/support/NodeFactory.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/netbeans/spi/project/ui/support/NodeFactory.java 28 Aug 2006 08:15:30 -0000 @@ -0,0 +1,55 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (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.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project.ui.support; + +import org.netbeans.api.project.Project; +import org.openide.nodes.Node; + +/** + * Factory interface for distributed creation of project node's children. Implementation + * instances are assumed to be registered in layers at a location specific for the particular + * project type. Project types wanting to make use of NodeFactory can use the + * {@link org.netbeans.spi.project.ui.support.NodeFactorySupport#createCompositeChildren} + * method to create the Project nodes's children. + * +
+public class FooBarLogicalViewProvider implements LogicalViewProvider {
+    public Node createLogicalView() {
+        return new FooBarRootNode(NodeFactorySupport.createCompositeChildren("Projects/org-foo-bar-project/Nodes");
+    }
+  
+}
+
+ * @author RichUnger + * @since org.netbeans.modules.projectuiapi 1.17 + */ +public interface NodeFactory { + + /** + * Create a list of children nodes for the given project. If the list is to be static, + * use the {@link org.netbeans.spi.project.ui.support.NodeFactorySupport#fixedNodeList} + * @return never return null, if the project is not relevant to the NodeFactory, + * use {@link org.netbeans.spi.project.ui.support.NodeFactorySupport#emptyNodeList} empty value. + */ + NodeList createNodes(Project p); + +// Node findPath(Project p, Node root, Object target); + +} Index: src/org/netbeans/spi/project/ui/support/NodeFactorySupport.java =================================================================== RCS file: src/org/netbeans/spi/project/ui/support/NodeFactorySupport.java diff -N src/org/netbeans/spi/project/ui/support/NodeFactorySupport.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/netbeans/spi/project/ui/support/NodeFactorySupport.java 28 Aug 2006 08:15:30 -0000 @@ -0,0 +1,215 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (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.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project.ui.support; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.netbeans.api.project.Project; +import org.openide.ErrorManager; +import org.openide.cookies.InstanceCookie; +import org.openide.filesystems.FileAttributeEvent; +import org.openide.filesystems.FileChangeListener; +import org.openide.filesystems.FileEvent; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileRenameEvent; +import org.openide.filesystems.Repository; +import org.openide.loaders.DataFolder; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; +import org.openide.loaders.FolderLookup; +import org.openide.loaders.InstanceDataObject; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; + +/** + * Support class for creating Project node's children nodes from NodeFactory instances + * in layers. + * @author mkleint + * @since org.netbeans.modules.projectuiapi 1.17 + */ +public class NodeFactorySupport { + + /** + * Creates a new instance of NodeFactorySupport + */ + private NodeFactorySupport() { + } + + /** + * create Node' Children instance that works on top of NodeFactory instances + * in the layers. + */ + public static Children createCompositeChildren(Project project, String folderPath) { + return new DelegateChildren(project, folderPath); + } + /** + * Utility method for creating a non variable NodeList instance. + */ + public static NodeList fixedNodeList(Node[] nodes) { + return new FixedNodeList(nodes); + } + + /** + * Utility method to create empty NodeList instance. Useful in case when a particular project instance is not + * relevant to the NodeFactory + */ + public static NodeList emptyNodeList() { + return new FixedNodeList(new Node[0]); + } + + + private static class FixedNodeList implements NodeList { + + private List nodes; + + FixedNodeList(Node[] nds) { + nodes = Arrays.asList(nds); + } + public List keys() { + return nodes; + } + + public void addChangeListener(ChangeListener l) { } + + public void removeChangeListener(ChangeListener l) { } + + public Node node(Object key) { + return (Node)key; + } + + public void addNotify() { + } + + public void removeNotify() { + } + } + + static class DelegateChildren extends Children.Keys implements LookupListener, ChangeListener { + + private String folderPath; + private Project project; + private List nodeLists = new ArrayList(); + private List factories = new ArrayList(); + private Lookup.Result result; + + public DelegateChildren(Project proj, String path) { + folderPath = path; + project = proj; + } + + // protected for tests.. + protected Lookup createLookup() { + FileObject root = Repository.getDefault().getDefaultFileSystem().findResource(folderPath); + DataFolder folder = DataFolder.findFolder(root); + return new FolderLookup(folder).getLookup(); + } + + protected Node[] createNodes(Object key) { + NodeList lst = (NodeList)key; + Iterator it = lst.keys().iterator(); + List toRet = new ArrayList(); + while (it.hasNext()) { + Object elem = it.next(); + Node nd = lst.node(elem); + if (nd != null) { + toRet.add(nd); + } + } + return (Node[])toRet.toArray(new Node[toRet.size()]); + } + + protected void addNotify() { + super.addNotify(); + result = createLookup().lookup(new Lookup.Template(NodeFactory.class)); + Iterator it = result.allInstances().iterator(); + while (it.hasNext()) { + NodeFactory factory = (NodeFactory) it.next(); + NodeList lst = factory.createNodes(project); + assert lst != null; + nodeLists.add(lst); + lst.addNotify(); + lst.addChangeListener(this); + factories.add(factory); + } + result.addLookupListener(this); + setKeys(nodeLists); + } + + protected void removeNotify() { + super.removeNotify(); + setKeys(Collections.EMPTY_LIST); + Iterator it = nodeLists.iterator(); + while (it.hasNext()) { + NodeList elem = (NodeList) it.next(); + elem.removeChangeListener(this); + elem.removeNotify(); + } + nodeLists.clear(); + factories.clear(); + if (result != null) { + result.removeLookupListener(this); + result = null; + } + } + + public void stateChanged(ChangeEvent e) { + refreshKey(e.getSource()); + } + + public void resultChanged(LookupEvent ev) { + Iterator it = result.allInstances().iterator(); + int index = 0; + while (it.hasNext()) { + NodeFactory factory = (NodeFactory) it.next(); + if (!factories.contains(factory)) { + factories.add(index, factory); + NodeList lst = factory.createNodes(project); + assert lst != null; + nodeLists.add(index, lst); + lst.addNotify(); + lst.addChangeListener(this); + } else { + while (!factory.equals(factories.get(index))) { + factories.remove(index); + NodeList lst = (NodeList)nodeLists.remove(index); + lst.removeNotify(); + lst.removeChangeListener(this); + } + } + index++; + } + setKeys(nodeLists); + } + + } + +} Index: src/org/netbeans/spi/project/ui/support/NodeList.java =================================================================== RCS file: src/org/netbeans/spi/project/ui/support/NodeList.java diff -N src/org/netbeans/spi/project/ui/support/NodeList.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/netbeans/spi/project/ui/support/NodeList.java 28 Aug 2006 08:15:30 -0000 @@ -0,0 +1,62 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (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.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project.ui.support; + +import java.util.List; +import javax.swing.event.ChangeListener; +import org.openide.nodes.Node; + +/** + * A Children.Keys-like abstration for use in + * {@link org.netbeans.spi.project.ui.support.NodeFactory} + * instances. For utility methods of creating a NodeList instance, see + * {@link org.netbeans.spi.project.ui.support.NodeFactorySupport} + + * @author mkleint + * @since org.netbeans.modules.projectuiapi 1.17 + */ +public interface NodeList { + /** + * child keys for which we later create a {@link org.openide.nodes.Node} + * in the node() method. If the change set of keys changes based on external + * events, fire a ChangeEvent to notify the parent Node. + */ + List keys(); + /** + * add a change listener, primarily to be used by the infrastructure + */ + void addChangeListener(ChangeListener l); // change in keys() + /** + * remove a change listener, primarily to be used by the infrastructure + */ + void removeChangeListener(ChangeListener l); + /** + * create Node for a given key, equal in semantics to Children.Keys.createNode() + */ + Node node(Object key); + /** + * callback from Children instance of the parent node, equal in semantics to Children.addNotify() + */ + void addNotify(); + /** + * callback from Children instance of the parent node, equal in semantics to Children.removeNotify() + */ + void removeNotify(); +} Index: test/unit/src/org/netbeans/spi/project/ui/support/NodeFactorySupportTest.java =================================================================== RCS file: test/unit/src/org/netbeans/spi/project/ui/support/NodeFactorySupportTest.java diff -N test/unit/src/org/netbeans/spi/project/ui/support/NodeFactorySupportTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/netbeans/spi/project/ui/support/NodeFactorySupportTest.java 28 Aug 2006 08:15:30 -0000 @@ -0,0 +1,146 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (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.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project.ui.support; + +import com.sun.org.apache.bcel.internal.generic.LOOKUPSWITCH; +import junit.framework.TestCase; +import junit.framework.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.netbeans.api.project.Project; +import org.netbeans.junit.MockServices; +import org.openide.ErrorManager; +import org.openide.cookies.InstanceCookie; +import org.openide.filesystems.FileAttributeEvent; +import org.openide.filesystems.FileChangeListener; +import org.openide.filesystems.FileEvent; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileRenameEvent; +import org.openide.filesystems.Repository; +import org.openide.loaders.DataFolder; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; +import org.openide.loaders.FolderLookup; +import org.openide.loaders.InstanceDataObject; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; + +/** + * + * @author mkleint + */ +public class NodeFactorySupportTest extends TestCase { + + public NodeFactorySupportTest(String testName) { + super(testName); + } + + protected void setUp() throws Exception { + } + + protected void tearDown() throws Exception { + } + + /** + * Test of createCompositeChildren method, of class org.netbeans.spi.project.ui.support.NodeFactorySupport. + */ + public void testCreateCompositeChildren() { + System.out.println("createCompositeChildren"); + InstanceContent ic = new InstanceContent(); + TestDelegates dels = new TestDelegates(ic); + Node node1 = new AbstractNode(Children.LEAF); + Node node2 = new AbstractNode(Children.LEAF); + Node node3 = new AbstractNode(Children.LEAF); + Node node4 = new AbstractNode(Children.LEAF); + node1.setName("node1"); + node2.setName("node2"); + node3.setName("node3"); + node4.setName("node4"); + NodeFactory fact1 = new TestNodeFactory(node1); + NodeFactory fact2 = new TestNodeFactory(node2); + NodeFactory fact3 = new TestNodeFactory(node3); + NodeFactory fact4 = new TestNodeFactory(node4); + List col = new ArrayList(); + col.add(fact1); + col.add(fact2); + ic.set(col, null); + + Node[] nds = dels.getNodes(); + assertEquals(nds[0], node1); + assertEquals(nds[1], node2); + + col.add(0, fact4); + col.add(fact3); + col.remove(fact2); + ic.set(col, null); + nds = dels.getNodes(); + assertEquals(nds[0], node4); + assertEquals(nds[1], node1); + assertEquals(nds[2], node3); + + } + + private class TestNodeFactory implements NodeFactory { + + Node node; + public TestNodeFactory(Node node) { + this.node = node; + } + public NodeList createNodes(Project p) { + return NodeFactorySupport.fixedNodeList(new Node[] {node}); + } + } + + private class TestDelegates extends NodeFactorySupport.DelegateChildren { + public AbstractLookup.Content content; + TestDelegates(AbstractLookup.Content cont) { + super(null, null); + content = cont; + } + + protected Lookup createLookup() { + return new AbstractLookup(content); + } + + public void addNotify() { + super.addNotify(); + } + + public void removeNotify() { + super.removeNotify(); + } + } + +}