Index: src/org/openide/util/lookup/AbstractLookup.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/AbstractLookup.java,v retrieving revision 1.46 diff -u -r1.46 AbstractLookup.java --- src/org/openide/util/lookup/AbstractLookup.java 25 Jun 2004 10:51:42 -0000 1.46 +++ src/org/openide/util/lookup/AbstractLookup.java 6 Jan 2005 18:34:55 -0000 @@ -529,6 +529,42 @@ protected abstract boolean creatorOf (Object obj); } + /** Generic support for listeners, so it can be used in other results + * as well. + * @param add true to add it, false to modify + * @param l listener to modify + * @param ref the value of the reference to listener or listener list + * @return new value to the reference to listener or list + */ + static Object modifyListenerList (boolean add, LookupListener l, Object ref) { + if (add) { + if (ref == null) { + return l; + } + + if (ref instanceof LookupListener) { + ArrayList arr = new ArrayList (); + arr.add (ref); + ref = arr; + } + ((ArrayList)ref).add (l); + return ref; + } else { + // remove + if (ref == null) return null; + if (ref == l) return null; + ArrayList arr = (ArrayList)ref; + arr.remove (l); + if (arr.size () == 1) { + return arr.iterator().next (); + } else { + return arr; + } + + } + + } + /** Result based on one instance returned. */ @@ -608,36 +644,16 @@ } } - /** Ok, register listeners to all classes and super classes. */ public synchronized void addLookupListener (LookupListener l) { - if (listeners == null) { - listeners = l; - return; - } - - if (listeners instanceof LookupListener) { - ArrayList arr = new ArrayList (); - arr.add (listeners); - listeners = arr; - } - ((ArrayList)listeners).add (l); + listeners = modifyListenerList (true, l, listeners); } /** Ok, register listeners to all classes and super classes. */ public synchronized void removeLookupListener (LookupListener l) { - if (listeners == null) return; - if (listeners == l) { - listeners = null; - return; - } - ArrayList arr = (ArrayList)listeners; - arr.remove (l); - if (arr.size () == 1) { - listeners = arr.iterator().next (); - } + listeners = modifyListenerList (false, l, listeners); } Index: src/org/openide/util/lookup/ExcludingLookup.java =================================================================== RCS file: src/org/openide/util/lookup/ExcludingLookup.java diff -N src/org/openide/util/lookup/ExcludingLookup.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/openide/util/lookup/ExcludingLookup.java 6 Jan 2005 18:34:55 -0000 @@ -0,0 +1,239 @@ +/* + * 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-2005 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.util.lookup; + +import java.util.*; + +import org.openide.util.Lookup; +import org.openide.util.LookupListener; + +/** Allows exclusion of certain instances from lookup. + * + * @author Jaroslav Tulach + */ +final class ExcludingLookup extends org.openide.util.Lookup { + /** the other lookup that we delegate to */ + private Lookup delegate; + /** classes to exclude (Class[]) or just one class (Class) */ + private Object classes; + + /** + * Creates new Result object with supplied instances parameter. + * @param instances to be used to return from the lookup + */ + ExcludingLookup(Lookup delegate, Class[] classes) { + this.delegate = delegate; + if (classes.length == 1) { + this.classes = classes[0]; + } else { + this.classes = classes; + } + } + + public String toString() { + return "ExcludingLookup: " + delegate + " excludes: " + Arrays.asList (classes()); // NOI18N + } + + public Result lookup(Template template) { + if (template == null) { + throw new NullPointerException(); + } + if (areSubclassesOfThisClassAlwaysExcluded (template.getType ())) { + // empty result + return Lookup.EMPTY.lookup (template); + } + return new R (delegate.lookup (template)); + } + + public Object lookup(Class clazz) { + if (areSubclassesOfThisClassAlwaysExcluded (clazz)) { + return null; + } + + Object res = delegate.lookup (clazz); + if (res == null) { + return null; + } + + Class[] arr = classes(); + for (int i = 0; i < arr.length; i++) { + if (arr[i].isInstance (res)) { + return null; + } + } + return res; + } + + public org.openide.util.Lookup.Item lookupItem (org.openide.util.Lookup.Template template) { + org.openide.util.Lookup.Item retValue = delegate.lookupItem (template); + if (retValue == null) return null; + + Class[] arr = classes(); + for (int i = 0; i < arr.length; i++) { + if (acceptFilter (arr[i], retValue, 2)) { + return null; + } + } + + return retValue; + } + + /** @return true if the instance of class c shall never be returned from this lookup + */ + private boolean areSubclassesOfThisClassAlwaysExcluded (Class c) { + Class[] arr = classes (); + for (int i = 0; i < arr.length; i++) { + if (arr[i].isAssignableFrom (c)) { + return true; + } + } + return false; + } + + /** Returns the array of classes this lookup filters. + */ + private Class[] classes () { + if (classes instanceof Class[]) { + return (Class[])classes; + } else { + return new Class[] { (Class)classes }; + } + } + + /** based on type decides whether the class accepts or not anObject + */ + static boolean acceptFilter (Class filter, Object anObject, int type) { + switch (type) { + case 0: return filter.isInstance (anObject); + case 1: return filter.isAssignableFrom ((Class)anObject); + case 2: { + Item item = (Item)anObject; + return filter.isAssignableFrom (item.getType ()); + } + default: throw new IllegalStateException ("Type: " + type); + } + } + + /** Filters collection accroding to set of given filters. + */ + static java.util.Collection filter (Class[] arr, java.util.Collection c, int type) { + java.util.Collection ret = null; + + TWICE: for (;;) { + Iterator it = c.iterator (); + BIG: while (it.hasNext ()) { + Object res = it.next (); + for (int i = 0; i < arr.length; i++) { + if (acceptFilter (arr[i], res, type)) { + if (ret == null) { + // we need to restart the scanning again + // as there is an active filter + if (type == 1) { + ret = new java.util.HashSet (); + } else { + ret = new ArrayList (c.size ()); + } + continue TWICE; + } + continue BIG; + } + } + if (ret != null) { + // if we are running the second round from TWICE + ret.add (res); + } + } + // ok, processed + break TWICE; + } + + return ret != null ? ret : c; + } + + + /** Delegating result that filters unwanted items and instances. + */ + private final class R extends WaitableResult implements LookupListener { + private Result result; + private Object listeners; + + R (Result delegate) { + this.result = delegate; + } + + protected void beforeLookup (Template t) { + if (result instanceof WaitableResult) { + ((WaitableResult)result).beforeLookup (t); + } + } + + public void addLookupListener (LookupListener l) { + boolean add; + synchronized (this) { + listeners = AbstractLookup.modifyListenerList (true, l, listeners); + add = listeners != null; + } + if (add) { + result.addLookupListener (this); + } + } + + public void removeLookupListener (LookupListener l) { + boolean remove; + synchronized (this) { + listeners = AbstractLookup.modifyListenerList (false, l, listeners); + remove = listeners == null; + } + if (remove) { + result.removeLookupListener (this); + } + } + + public java.util.Collection allInstances() { + return filter (classes (), result.allInstances (), 0); + } + + public Set allClasses () { + return (Set)filter (classes (), result.allClasses (), 1); + } + + public Collection allItems () { + return filter (classes (), result.allItems (), 2); + } + + public void resultChanged (org.openide.util.LookupEvent ev) { + if (ev.getSource () == result) { + LookupListener[] arr; + synchronized (this) { + if (listeners == null) return; + + if (listeners instanceof LookupListener) { + arr = new LookupListener[] { (LookupListener) listeners }; + } else { + ArrayList l = (ArrayList)listeners; + arr = (LookupListener[])l.toArray ( + new LookupListener[l.size ()] + ); + } + } + + final LookupListener[] ll = arr; + final org.openide.util.LookupEvent newev = new org.openide.util.LookupEvent (this); + for (int i = 0; i < ll.length; i++) { + ll[i].resultChanged(newev); + } + } + } + } +} Index: src/org/openide/util/lookup/Lookups.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/Lookups.java,v retrieving revision 1.10 diff -u -r1.10 Lookups.java --- src/org/openide/util/lookup/Lookups.java 25 Aug 2003 19:24:32 -0000 1.10 +++ src/org/openide/util/lookup/Lookups.java 6 Jan 2005 18:34:55 -0000 @@ -7,7 +7,7 @@ * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original - * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2005 Sun * Microsystems, Inc. All Rights Reserved. */ @@ -128,6 +128,26 @@ */ public static Lookup metaInfServices(ClassLoader classLoader) { return new MetaInfServicesLookup(classLoader); + } + + /** Creates a lookup that wraps another one and filters out all instances + * of classe specified as second argument. If you have a lookup and + * wnat to remove all instances of ActionMap you can use: + *
+      * Lookups.exclude (lookup, new Class[] { ActionMap.class });
+      * 
+ * To create empty lookup (well, just an example, otherwise use {@link Lookup#EMPTY}) one could use: + *
+      * Lookup.exclude (anyLookup, new Class[] { Object.class });
+      * 
+ * as any instance in any lookup is of type Object and thus would be excluded. + * + * @param lookup the original lookup that should be filtered + * @param classes array of classes those instances should be excluded + * @since JST-PENDING + */ + public static Lookup exclude (Lookup lookup, Class[] classes) { + return new ExcludingLookup (lookup, classes); } /** Creates Lookup.Item representing the instance passed in. Index: test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java =================================================================== RCS file: test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java diff -N test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java 6 Jan 2005 18:34:55 -0000 @@ -0,0 +1,154 @@ +/* + * 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-2000 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.util.lookup; + +import org.openide.filesystems.*; +import org.openide.loaders.*; +import org.openide.util.*; + +import java.lang.ref.WeakReference; +import java.util.*; +import junit.framework.*; +import org.netbeans.junit.*; + +/** Runs all NbLookupTest tests on ProxyLookup and adds few additional. + */ +public class ExcludingLookupTest extends AbstractLookupBaseHid +implements AbstractLookupBaseHid.Impl { + public ExcludingLookupTest(java.lang.String testName) { + super(testName, null); + } + + public Lookup createLookup (final Lookup lookup) { + return Lookups.exclude (lookup, new Class[0]); + } + + public Lookup createInstancesLookup (InstanceContent ic) { + return new AbstractLookup (ic); + } + + public void clearCaches () { + } + + public void testWeCanRemoveInteger () throws Exception { + doBasicFilteringTest (Integer.class, Integer.class, 0); + } + + public void testWeCanRemoveIntegersEvenByAskingForRemoveOfAllNumbers () throws Exception { + doBasicFilteringTest (Number.class, Integer.class, 0); + } + public void testFunWithInterfaces () throws Exception { + doBasicFilteringTest (java.io.Serializable.class, Integer.class, 0); + } + public void testFunWithInterfaces2 () throws Exception { + doBasicFilteringTest (java.io.Serializable.class, Object.class, 1); + } + + public void testIntegersQueriedThruObject () throws Exception { + doBasicFilteringTest (Number.class, Object.class, 1); + } + + private void doBasicFilteringTest (Class theFilter, Class theQuery, int numberOfExcpectedEventsAfterOneChange) throws Exception { + Lookup lookup = Lookups.exclude (this.instanceLookup, new Class[] { theFilter }); + Lookup.Template t = new Lookup.Template (theQuery); + Lookup.Result res = lookup.lookup (t); + + LL ll = new LL (); + res.addLookupListener (ll); + assertEquals ("Nothing is there", 0, res.allItems ().size ()); + + Object inst = new Integer (3); + ic.add (inst); + + assertEquals ("Filtered out", null, lookup.lookup (theQuery)); + assertEquals ("Filtered out2", null, lookup.lookupItem (t)); + assertEquals ("Nothing is there - 2", 0, res.allItems ().size ()); + assertEquals ("Nothing is there - 2a", 0, res.allInstances ().size ()); + assertEquals ("Nothing is there - 2b", 0, res.allClasses ().size ()); + assertEquals ("Right # of events", numberOfExcpectedEventsAfterOneChange, ll.getCount ()); + + ic.remove (inst); + assertEquals ("Filtered out3", null, lookup.lookupItem (t)); + assertEquals ("Nothing is there - 3", 0, res.allItems ().size ()); + assertEquals ("Nothing is there - 3a", 0, res.allInstances ().size ()); + assertEquals ("Nothing is there - 3b", 0, res.allClasses ().size ()); + assertEquals ("Of course it is not there", null, lookup.lookup (theQuery)); + assertEquals ("Right # of events", numberOfExcpectedEventsAfterOneChange, ll.getCount ()); + + } + + public void testSizeOfTheLookup () throws Exception { + Class exclude = String.class; + + Lookup lookup = Lookups.exclude (this.instanceLookup, new Class[] { exclude }); + + assertSize ("Should be pretty lightweight", Collections.singleton (lookup), 16, + new Object[] { this.instanceLookup, exclude }); + } + public void testSizeOfTheLookupForMultipleFiltersIsHigher () throws Exception { + Class exclude = String.class; + Class exclude2 = Integer.class; + Class[] arr = new Class[] { exclude, exclude2 }; + + Lookup lookup = Lookups.exclude (this.instanceLookup, arr); + + assertSize ("Is fatter", Collections.singleton (lookup), 40, + new Object[] { this.instanceLookup, exclude, exclude2 }); + assertSize ("But only due to the array", Collections.singleton (lookup), 16, + new Object[] { this.instanceLookup, exclude, exclude2, arr }); + } + + public void testFilteringOfSomething () throws Exception { + doFilteringOfSomething (Runnable.class, java.io.Serializable.class, 1); + } + + private void doFilteringOfSomething (Class theFilter, Class theQuery, int numberOfExcpectedEventsAfterOneChange) throws Exception { + Lookup lookup = Lookups.exclude (this.instanceLookup, new Class[] { theFilter }); + Lookup.Template t = new Lookup.Template (theQuery); + Lookup.Result res = lookup.lookup (t); + + LL ll = new LL (); + res.addLookupListener (ll); + assertEquals ("Nothing is there", 0, res.allItems ().size ()); + + Object inst = new Integer (3); + ic.add (inst); + + assertEquals ("Accepted", inst, lookup.lookup (theQuery)); + assertNotNull ("Accepted too", lookup.lookupItem (t)); + assertEquals ("One is there - 2", 1, res.allItems ().size ()); + assertEquals ("One is there - 2a", 1, res.allInstances ().size ()); + assertEquals ("One is there - 2b", 1, res.allClasses ().size ()); + assertEquals ("Right # of events", numberOfExcpectedEventsAfterOneChange, ll.getCount ()); + + Object inst2 = new Thread (); // implements Runnable + ic.add (inst2); + assertEquals ("Accepted - 2", inst, lookup.lookup (theQuery)); + assertNotNull ("Accepted too -2", lookup.lookupItem (t)); + assertEquals ("One is there - 3", 1, res.allItems ().size ()); + assertEquals ("One is there - 3a", 1, res.allInstances ().size ()); + assertEquals ("One is there - 3b", 1, res.allClasses ().size ()); + assertEquals ("Right # of events", 0, ll.getCount ()); + + + ic.remove (inst); + assertEquals ("Filtered out3", null, lookup.lookupItem (t)); + assertEquals ("Nothing is there - 3", 0, res.allItems ().size ()); + assertEquals ("Nothing is there - 3a", 0, res.allInstances ().size ()); + assertEquals ("Nothing is there - 3b", 0, res.allClasses ().size ()); + assertEquals ("Of course it is not there", null, lookup.lookup (theQuery)); + assertEquals ("Right # of events", numberOfExcpectedEventsAfterOneChange, ll.getCount ()); + } + +}