? 83343.diff ? manifest-subst.mf ? netbeans Index: projectapi/apichanges.xml =================================================================== RCS file: /cvs/projects/projectapi/apichanges.xml,v retrieving revision 1.9 diff -u -r1.9 apichanges.xml --- projectapi/apichanges.xml 8 Sep 2006 18:33:49 -0000 1.9 +++ projectapi/apichanges.xml 18 Sep 2006 13:58:54 -0000 @@ -81,6 +81,26 @@ + + + Added support for composing project's lookup from multiple sources. + + + + + + +

+ Added interfaces and support classes that can be used to compose the project's lookup + from multiple 3rd party sources. Framework for creating merged instances included. + LookupMerger implementation forSources added. +

+
+ + + + +
Index: projectapi/arch.xml =================================================================== RCS file: /cvs/projects/projectapi/arch.xml,v retrieving revision 1.11 diff -u -r1.11 arch.xml --- projectapi/arch.xml 15 Aug 2006 15:21:28 -0000 1.11 +++ projectapi/arch.xml 18 Sep 2006 13:58:54 -0000 @@ -936,4 +936,60 @@ + + + + + + + + + + + + +

+ XXX no answer for compat-deprecation +

+
+ + + + + +

+ XXX no answer for exec-ant-tasks +

+
+ Index: projectapi/manifest.mf =================================================================== RCS file: /cvs/projects/projectapi/manifest.mf,v retrieving revision 1.14 diff -u -r1.14 manifest.mf --- projectapi/manifest.mf 8 Sep 2006 18:33:49 -0000 1.14 +++ projectapi/manifest.mf 18 Sep 2006 13:58:54 -0000 @@ -1,5 +1,5 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.projectapi/1 -OpenIDE-Module-Specification-Version: 1.11 +OpenIDE-Module-Specification-Version: 1.12 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/projectapi/Bundle.properties Index: projectapi/nbproject/project.properties =================================================================== RCS file: /cvs/projects/projectapi/nbproject/project.properties,v retrieving revision 1.15 diff -u -r1.15 project.properties --- projectapi/nbproject/project.properties 16 Aug 2006 06:28:35 -0000 1.15 +++ projectapi/nbproject/project.properties 18 Sep 2006 13:58:54 -0000 @@ -24,4 +24,4 @@ javadoc.apichanges=${basedir}/apichanges.xml # masterfs needed for the usual reasons; core.jar needed for ArchiveURLMapper: -test.unit.run.cp.extra=${core/startup.dir}/core/core.jar:${core/bootstrap.dir}/lib/boot.jar +#test.unit.run.cp.extra=${core/startup.dir}/core/core.jar:${core/bootstrap.dir}/lib/boot.jar Index: projectapi/nbproject/project.xml =================================================================== RCS file: /cvs/projects/projectapi/nbproject/project.xml,v retrieving revision 1.15 diff -u -r1.15 project.xml --- projectapi/nbproject/project.xml 8 Aug 2006 22:46:44 -0000 1.15 +++ projectapi/nbproject/project.xml 18 Sep 2006 13:58:54 -0000 @@ -40,6 +40,22 @@ + org.openide.loaders + + + + 5.11 + + + + org.openide.nodes + + + + 6.9 + + + org.openide.util @@ -48,24 +64,24 @@ - - - unit - - org.netbeans.modules.projectapi - - - - - org.openide.modules - - - org.netbeans.modules.masterfs - - - - qa-functional - + + + unit + + org.netbeans.modules.projectapi + + + + + org.openide.modules + + + org.netbeans.modules.masterfs + + + + qa-functional + org.netbeans.api.project Index: projectapi/src/org/netbeans/spi/project/LookupMerger.java =================================================================== RCS file: projectapi/src/org/netbeans/spi/project/LookupMerger.java diff -N projectapi/src/org/netbeans/spi/project/LookupMerger.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ projectapi/src/org/netbeans/spi/project/LookupMerger.java 18 Sep 2006 13:58:54 -0000 @@ -0,0 +1,52 @@ +/* + * 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; + +import org.openide.util.Lookup; + +/** + * Allows project lookup to merge instances of known classes and replace them + * with single instance. To be used in conjunction with the {@link org.netbeans.spi.project.LookupProvider} + * and {@link org.netbeans.spi.project.support.LookupProviderSupport} + * The interface is to be implemented by the project owner which decides which contracts make sense to have merged and + * how they are to be merged. + * The 3rd party {@link org.netbeans.spi.project.LookupProvider} implementors provide instances of mergeableClass. + * {@link org.netbeans.spi.project.support.LookupProviderSupport#createCompositeLookup} handles the hiding of individual mergeable instances + * and exposing the merged instance created by the LookupMerger. + * @author mkleint + * @since org.netbeans.modules.projectapi 1.12 + */ +public interface LookupMerger { + + /** + * Returns a class which is merged by this implementation of LookupMerger + * @return Class instance + */ + Class getMergeableClass(); + + /** + * Merge instances of the given class in the given lookup and return merged + * object which substitutes them. + * @param lookup lookup with the instances + * @return object to be used instead of instances in the lookup + */ + T merge(Lookup lookup); + +} Index: projectapi/src/org/netbeans/spi/project/LookupProvider.java =================================================================== RCS file: projectapi/src/org/netbeans/spi/project/LookupProvider.java diff -N projectapi/src/org/netbeans/spi/project/LookupProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ projectapi/src/org/netbeans/spi/project/LookupProvider.java 18 Sep 2006 13:58:54 -0000 @@ -0,0 +1,45 @@ +/* + * 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; + +import org.openide.util.Lookup; + +/** + * interface for inclusion of 3rd party content in project's lookup. Typically, if the + * project type allows composition of lookup from multiple sources, it will make a layer + * location public where 3rd parties will register implementations of this interface. + * @author mkleint + * @since org.netbeans.modules.projectapi 1.12 + */ +public interface LookupProvider { + + /** + * implementations will be asked to create their additional project lookup based on the baseContext + * passed as parameter. The content of baseLookup is undefined on this level, is a contract + * of the actual project type. Can be complete lookup of the project type, a portion of it or + * something completely different that won't appear in the final project lookup. + * Each implementation is only asked once for it's lookup for a given project instance at the time + * when project's lookup is being created. + * @param baseContext implementation shall decide what to return for a given project instance based on context + * passed in. + * @return a {@link org.openide.util.Lookup} instance that is to be added to the project's lookup, never null. + */ + Lookup createAdditionalLookup(Lookup baseContext); +} Index: projectapi/src/org/netbeans/spi/project/support/LookupProviderSupport.java =================================================================== RCS file: projectapi/src/org/netbeans/spi/project/support/LookupProviderSupport.java diff -N projectapi/src/org/netbeans/spi/project/support/LookupProviderSupport.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ projectapi/src/org/netbeans/spi/project/support/LookupProviderSupport.java 18 Sep 2006 13:58:54 -0000 @@ -0,0 +1,283 @@ +/* + * 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.support; + +import com.sun.corba.se.impl.orbutil.LogKeywords; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.netbeans.api.project.Project; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.api.project.Sources; +import org.netbeans.spi.project.LookupMerger; +import org.netbeans.spi.project.LookupProvider; +import org.openide.ErrorManager; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.Repository; +import org.openide.loaders.DataFolder; +import org.openide.loaders.FolderLookup; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; +import org.openide.util.WeakListeners; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; + +/** + * Factory for lookup capable of merging content from registered + * {@link org.netbeans.spi.project.LookupProvider} instances. + * @author mkleint + * @since org.netbeans.modules.projectapi 1.12 + */ +public final class LookupProviderSupport { + + /** Creates a new instance of LookupProviderSupport */ + private LookupProviderSupport() { + } + + /** + * Creates a project lookup instance that combines the content from multiple sources. + * A convenience factory method for implementors of Project + * + * @param baseLookup initial, base content of the project lookup created by the project owner + * @param folderPath the path in the System Filesystem that is used as root for lookup composition. + * The content of the folder is assumed to be {@link org.netbeans.spi.project.LookupProvider} instances + * @return a lookup to be used in project + */ + public static Lookup createCompositeLookup(Lookup baseLookup, String folderPath) { + return new DelegatingLookupImpl(baseLookup, folderPath); + } + + /** + * Factory method for creating {@link org.netbeans.spi.project.LookupMerger} instance that merges + * {@link org.netbeans.api.project.Sources} instances in the project lookup. + * Allows to compose the {@link org.netbeans.api.project.Sources} + * content from multiple sources. + * @return instance to include in project lookup + */ + public static LookupMerger createSourcesMerger() { + return new SourcesMerger(); + } + + //TODO maybe have just one single instance for a given path? + private static Lookup createLookup(String folderPath) { + FileObject root = Repository.getDefault().getDefaultFileSystem().findResource(folderPath); + DataFolder folder = DataFolder.findFolder(root); + return new FolderLookup(folder).getLookup(); + } + + static class DelegatingLookupImpl extends ProxyLookup implements LookupListener { + private Lookup baseLookup; + private Lookup.Result providerResult; + private LookupListener providerListener; + private List old = Collections.emptyList(); + private List currentLookups; + + private Lookup.Result mergers; + private Reference listenerRef; + //#68623: the proxy lookup fires changes only if someone listens on a particular template: + private List> results = new ArrayList>(); + + public DelegatingLookupImpl(Lookup base, String path) { + this(base, createLookup(path)); + } + + public DelegatingLookupImpl(Lookup base, Lookup providerLookup) { + super(); + assert base != null; + baseLookup = base; + providerResult = providerLookup.lookup(new Lookup.Template(LookupProvider.class)); + doDelegate(providerResult.allInstances()); + providerListener = new LookupListener() { + public void resultChanged(LookupEvent ev) { + doDelegate(providerResult.allInstances()); + } + }; + providerResult.addLookupListener(providerListener); + } + + + public void resultChanged(LookupEvent ev) { + doDelegate(providerResult.allInstances()); + } + + + private synchronized void doDelegate(Collection providers) { + //unregister listeners from the old results: + for (Lookup.Result r : results) { + r.removeLookupListener(this); + } + + List newLookups = new ArrayList(); + for (LookupProvider elem : providers) { + if (old.contains(elem)) { + int index = old.indexOf(elem); + newLookups.add(currentLookups.get(index)); + } else { + Lookup newone = elem.createAdditionalLookup(baseLookup); + assert newone != null; + LookupMerger merg = newone.lookup(LookupMerger.class); + if (merg != null) { + ErrorManager.getDefault().log(ErrorManager.WARNING, + "LookupProvider " + elem.getClass().getName() + " provides LookupMerger for " + + merg.getMergeableClass().getName() + + ". That can cause project behaviour changes not anticipated by the project type owner." + + "Please consider making the LookupMerger a contract of the project type." ); + } + newLookups.add(newone); + } + } + old = (List) providers; + currentLookups = newLookups; + newLookups.add(baseLookup); + Lookup lkp = new ProxyLookup(newLookups.toArray(new Lookup[newLookups.size()])); + + //merge: + List> filteredClasses = new ArrayList>(); + List mergedInstances = new ArrayList(); + LookupListener l = listenerRef != null ? listenerRef.get() : null; + if (l != null) { + mergers.removeLookupListener(l); + } + mergers = lkp.lookupResult(LookupMerger.class); + l = WeakListeners.create(LookupListener.class, this, mergers); + listenerRef = new WeakReference(l); + mergers.addLookupListener(l); + for (LookupMerger lm : mergers.allInstances()) { + Class c = lm.getMergeableClass(); + if (filteredClasses.contains(c)) { + ErrorManager.getDefault().log(ErrorManager.WARNING, + "Two LookupMerger registered for class " + c + + ". Only first one will be used"); // NOI18N + continue; + } + filteredClasses.add(c); + mergedInstances.add(lm.merge(lkp)); + + Lookup.Result result = lkp.lookupResult(c); + + result.addLookupListener(this); + results.add(result); + } + lkp = Lookups.exclude(lkp, filteredClasses.toArray(new Class[filteredClasses.size()])); + Lookup fixed = Lookups.fixed(mergedInstances.toArray(new Object[mergedInstances.size()])); + setLookups(fixed, lkp); + } + } + + + private static class SourcesMerger implements LookupMerger { + private SourcesImpl merger; + + public Class getMergeableClass() { + return Sources.class; + } + + public Object merge(Lookup lookup) { + if (merger == null) { + merger = new SourcesImpl(); + } + merger.setLookup(lookup); + return merger; + } + } + + private static class SourcesImpl implements Sources, ChangeListener, LookupListener { + private List listeners = new ArrayList(); + private Lookup.Result delegates; + private Collection currentDelegates = new ArrayList(); + + public SourcesImpl() { + } + + private void setLookup(Lookup lookup) { + if (currentDelegates.size() > 0) { + for (Sources old : currentDelegates) { + old.removeChangeListener(this); + } + currentDelegates.clear(); + } + if (delegates != null) { + delegates.removeLookupListener(this); + } + Lookup.Result srcs = lookup.lookup(new Lookup.Template(Sources.class)); + for (Sources ns : srcs.allInstances()) { + ns.addChangeListener(this); + currentDelegates.add(ns); + } + srcs.addLookupListener(this); + delegates = srcs; + fireChange(); + } + + public SourceGroup[] getSourceGroups(String type) { + assert delegates != null; + Collection result = new ArrayList(); + for (Sources ns : delegates.allInstances()) { + SourceGroup[] grps = ns.getSourceGroups(type); + if (grps != null) { + result.addAll(Arrays.asList(grps)); + } + } + return result.toArray(new SourceGroup[result.size()]); + } + + public void addChangeListener(ChangeListener listener) { + listeners.add(listener); + } + + public void removeChangeListener(ChangeListener listener) { + listeners.remove(listener); + } + + public void stateChanged(ChangeEvent e) { + fireChange(); + } + + private void fireChange() { + for (ChangeListener listener : listeners) { + listener.stateChanged(new ChangeEvent(this)); + } + } + + public void resultChanged(LookupEvent ev) { + if (currentDelegates.size() > 0) { + for (Sources old : currentDelegates) { + old.removeChangeListener(this); + } + currentDelegates.clear(); + } + for (Sources ns : delegates.allInstances()) { + ns.addChangeListener(this); + currentDelegates.add(ns); + } + fireChange(); + } + } + +} Index: projectapi/test/unit/src/org/netbeans/spi/project/support/LookupProviderSupportTest.java =================================================================== RCS file: projectapi/test/unit/src/org/netbeans/spi/project/support/LookupProviderSupportTest.java diff -N projectapi/test/unit/src/org/netbeans/spi/project/support/LookupProviderSupportTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ projectapi/test/unit/src/org/netbeans/spi/project/support/LookupProviderSupportTest.java 18 Sep 2006 13:58:54 -0000 @@ -0,0 +1,303 @@ +/* + * 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.support; + +import java.beans.PropertyChangeListener; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JRadioButton; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.netbeans.api.project.ProjectUtils; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.api.project.Sources; +import org.netbeans.junit.NbTestCase; +import org.netbeans.spi.project.LookupMerger; +import org.netbeans.spi.project.LookupProvider; +import org.openide.ErrorManager; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.Repository; +import org.openide.loaders.DataFolder; +import org.openide.loaders.FolderLookup; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; +import org.openide.util.WeakListeners; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; + +/** + * + * @author mkleint + */ +public class LookupProviderSupportTest extends NbTestCase { + + public LookupProviderSupportTest(String testName) { + super(testName); + } + + protected void setUp() throws Exception { + } + + protected void tearDown() throws Exception { + } + + /** + * Test of createCompositeLookup method, of class org.netbeans.spi.project.support.LookupProviderSupport. + */ + public void testCreateCompositeLookup() { + LookupMergerImpl merger = new LookupMergerImpl(); + Lookup base = Lookups.fixed(new JButton(), new JComboBox(), merger); + LookupProviderImpl pro1 = new LookupProviderImpl(); + LookupProviderImpl pro2 = new LookupProviderImpl(); + LookupProviderImpl pro3 = new LookupProviderImpl(); + + InstanceContent provInst = new InstanceContent(); + Lookup providers = new AbstractLookup(provInst); + provInst.add(pro1); + provInst.add(pro2); + + pro1.ic.add(new JTextField()); + pro2.ic.add(new JTextArea()); + + LookupProviderSupport.DelegatingLookupImpl del = new LookupProviderSupport.DelegatingLookupImpl(base, providers); + + assertNotNull(del.lookup(JTextArea.class)); + assertNotNull(del.lookup(JComboBox.class)); + + // test merger.. + JButton butt = del.lookup(JButton.class); + assertNotNull(butt); + assertEquals("CORRECT", butt.getText()); + assertEquals(1, del.lookup(new Lookup.Template(JButton.class)).allInstances().size()); + assertEquals(1, merger.expectedCount); + + pro3.ic.add(new JButton()); + pro3.ic.add(new JRadioButton()); + provInst.add(pro3); + assertNotNull(del.lookup(JRadioButton.class)); + + // test merger.. + butt = del.lookup(JButton.class); + assertNotNull(butt); + assertEquals("CORRECT", butt.getText()); + assertEquals(1, del.lookup(new Lookup.Template(JButton.class)).allInstances().size()); + assertEquals(2, merger.expectedCount); + + pro1.ic.add(new JButton()); + + // test merger.. + butt = del.lookup(JButton.class); + assertNotNull(butt); + assertEquals("CORRECT", butt.getText()); + assertEquals(1, del.lookup(new Lookup.Template(JButton.class)).allInstances().size()); + assertEquals(3, merger.expectedCount); + + } + + private SourcesImpl createImpl(String id) { + SourcesImpl impl0 = new SourcesImpl(); + SourceGroupImpl grp0 = new SourceGroupImpl(); + grp0.name = id; + impl0.grpMap.put("java", new SourceGroup[] {grp0}); + return impl0; + } + + public void testSourcesMerger() { + SourcesImpl impl0 = createImpl("group0"); + SourcesImpl impl1 = createImpl("group1"); + SourcesImpl impl2 = createImpl("group2"); + SourcesImpl impl3 = createImpl("group3"); + + Lookup base = Lookups.fixed(impl0, LookupProviderSupport.createSourcesMerger()); + LookupProviderImpl2 pro1 = new LookupProviderImpl2(); + LookupProviderImpl2 pro2 = new LookupProviderImpl2(); + LookupProviderImpl2 pro3 = new LookupProviderImpl2(); + + InstanceContent provInst = new InstanceContent(); + Lookup providers = new AbstractLookup(provInst); + provInst.add(pro1); + provInst.add(pro2); + + pro1.ic.add(impl1); + pro2.ic.add(impl2); + pro3.ic.add(impl3); + + LookupProviderSupport.DelegatingLookupImpl del = new LookupProviderSupport.DelegatingLookupImpl(base, providers); + + Sources srcs = del.lookup(Sources.class); + assertNotNull(srcs); + SourceGroup[] grps = srcs.getSourceGroups("java"); + assertEquals(3, grps.length); + + //now let's add another module to the bunch and see if the new SG appears + provInst.add(pro3); + + srcs = del.lookup(Sources.class); + assertNotNull(srcs); + grps = srcs.getSourceGroups("java"); + assertEquals(4, grps.length); + + //now let's remove another module to the bunch and see if the SG disappears + provInst.remove(pro2); + + srcs = del.lookup(Sources.class); + assertNotNull(srcs); + grps = srcs.getSourceGroups("java"); + assertEquals(3, grps.length); + + //lets remove one and listen for changes... + srcs = del.lookup(Sources.class); + ChangeListenerImpl ch = new ChangeListenerImpl(); + srcs.addChangeListener(ch); + provInst.remove(pro1); + + assertTrue(ch.pinged); + grps = srcs.getSourceGroups("java"); + assertEquals(2, grps.length); + + ch.pinged = false; + provInst.add(pro2); + + assertTrue(ch.pinged); + grps = srcs.getSourceGroups("java"); + assertEquals(3, grps.length); + + } + + private class ChangeListenerImpl implements ChangeListener { + boolean pinged = false; + public void stateChanged(ChangeEvent e) { + pinged = true; + } + } + + private class LookupProviderImpl implements LookupProvider { + InstanceContent ic = new InstanceContent(); + boolean wasAlreadyCalled = false; + public Lookup createAdditionalLookup(Lookup baseContext) { + assertNotNull(baseContext.lookup(JButton.class)); + assertNull(baseContext.lookup(JCheckBox.class)); + assertFalse(wasAlreadyCalled); + wasAlreadyCalled = true; + return new AbstractLookup(ic); + } + } + + private class LookupProviderImpl2 implements LookupProvider { + InstanceContent ic = new InstanceContent(); + AbstractLookup l; + public Lookup createAdditionalLookup(Lookup baseContext) { + if (l == null) { + l = new AbstractLookup(ic); + } + return l; + } + } + + private class LookupMergerImpl implements LookupMerger { + + int expectedCount; + + public Class getMergeableClass() { + return JButton.class; + } + + public Object merge(Lookup lookup) { + Lookup.Result res = lookup.lookup(new Lookup.Template(JButton.class)); + int size = res.allInstances().size(); + expectedCount = size; + return new JButton("CORRECT"); + } + + } + + private static class SourcesImpl implements Sources { + public HashMap grpMap = new HashMap(); + private List listeners = new ArrayList(); + + + public SourceGroup[] getSourceGroups(String type) { + return grpMap.get(type); + } + + public void addChangeListener(ChangeListener listener) { + listeners.add(listener); + } + + public void removeChangeListener(ChangeListener listener) { + listeners.remove(listener); + } + + public void fireChange() { + for (ChangeListener listener : listeners) { + listener.stateChanged(new ChangeEvent(this)); + } + } + } + + private static class SourceGroupImpl implements SourceGroup { + + String name; + + String displayName; + public FileObject getRootFolder() { + return null; + } + + public String getName() { + return name; + } + + public String getDisplayName() { + return displayName; + } + + public Icon getIcon(boolean opened) { + return null; + } + + public boolean contains(FileObject file) throws IllegalArgumentException { + return false; + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + } + } + +} Index: projectuiapi/apichanges.xml =================================================================== RCS file: /cvs/projects/projectuiapi/apichanges.xml,v retrieving revision 1.24 diff -u -r1.24 apichanges.xml --- projectuiapi/apichanges.xml 18 Sep 2006 11:25:53 -0000 1.24 +++ projectuiapi/apichanges.xml 18 Sep 2006 13:58:54 -0000 @@ -81,6 +81,25 @@ + + + Added LookupMerger implementations for PrivilegedTemplates and RecommendedTemplates + + + + + + +

+ Related to 1.12 change in Project API. + LookupMerger implementation forPrivilegedTemplates and + RecommendedTemplates added. +

+
+ + +
+ Ability to construct project node's children from multiple sources. Index: projectuiapi/src/org/netbeans/spi/project/ui/support/UILookupMergerSupport.java =================================================================== RCS file: projectuiapi/src/org/netbeans/spi/project/ui/support/UILookupMergerSupport.java diff -N projectuiapi/src/org/netbeans/spi/project/ui/support/UILookupMergerSupport.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ projectuiapi/src/org/netbeans/spi/project/ui/support/UILookupMergerSupport.java 18 Sep 2006 13:58:54 -0000 @@ -0,0 +1,119 @@ +/* + * 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.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; +import org.netbeans.spi.project.LookupMerger; +import org.netbeans.spi.project.ui.PrivilegedTemplates; +import org.netbeans.spi.project.ui.RecommendedTemplates; +import org.openide.util.Lookup; + +/** + * Factory class for creation of {@link org.netbeans.spi.project.LookupMerger} instances. + * @author mkleint + * @since org.netbeans.modules.projectuiapi 1.19 + */ +public final class UILookupMergerSupport { + + /** Creates a new instance of LookupMergerSupport */ + private UILookupMergerSupport() { + } + + /** + * Create a {@link org.netbeans.spi.project.LookupMerger} instance + * for {@link org.netbeans.spi.project.ui.RecommendedTemplates}. Allows to merge + * templates from multiple sources. + * @return instance to include in project lookup + */ + public static LookupMerger createRecommendedTemplatesMerger() { + return new RecommendedMerger(); + } + + /** + * Create a {@link org.netbeans.spi.project.LookupMerger} instance + * for {@link org.netbeans.spi.project.ui.PrivilegedTemplates}. Allows to merge + * templates from multiple sources. + * @return instance to include in project lookup + */ + public static LookupMerger createPrivilegedTemplatesMerger() { + return new PrivilegedMerger(); + } + + private static class PrivilegedMerger implements LookupMerger { + public Class getMergeableClass() { + return PrivilegedTemplates.class; + } + + public Object merge(Lookup lookup) { + return new PrivilegedTemplatesImpl(lookup); + } + } + + private static class RecommendedMerger implements LookupMerger { + + public Class getMergeableClass() { + return RecommendedTemplates.class; + } + + public Object merge(Lookup lookup) { + return new RecommendedTemplatesImpl(lookup); + } + } + + private static class PrivilegedTemplatesImpl implements PrivilegedTemplates { + + private Lookup lkp; + + public PrivilegedTemplatesImpl(Lookup lkp) { + this.lkp = lkp; + } + + public String[] getPrivilegedTemplates() { + Set templates = new LinkedHashSet(); + for (PrivilegedTemplates pt : lkp.lookupAll(PrivilegedTemplates.class)) { + templates.addAll(Arrays.asList(pt.getPrivilegedTemplates())); + } + return templates.toArray(new String[templates.size()]); + } + } + + private static class RecommendedTemplatesImpl implements RecommendedTemplates { + + private Lookup lkp; + + public RecommendedTemplatesImpl(Lookup lkp) { + this.lkp = lkp; + } + + public String[] getRecommendedTypes() { + Set templates = new LinkedHashSet(); + for (RecommendedTemplates pt : lkp.lookupAll(RecommendedTemplates.class)) { + templates.addAll(Arrays.asList(pt.getRecommendedTypes())); + } + return templates.toArray(new String[templates.size()]); + } + + } + + +}