Index: openide/src/org/openide/util/lookup/ExcludingLookup.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/ExcludingLookup.java,v retrieving revision 1.1 diff -u -r1.1 ExcludingLookup.java --- openide/src/org/openide/util/lookup/ExcludingLookup.java 14 Jan 2005 13:09:34 -0000 1.1 +++ openide/src/org/openide/util/lookup/ExcludingLookup.java 21 Jan 2005 09:39:40 -0000 @@ -53,7 +53,7 @@ // empty result return Lookup.EMPTY.lookup (template); } - return new R (delegate.lookup (template)); + return new R (template.getType (), delegate.lookup (template)); } public Object lookup(Class clazz) { @@ -62,31 +62,23 @@ } Object res = delegate.lookup (clazz); - if (res == null) { + if (isObjectAccessible (clazz, res, 0)) { + return res; + } else { 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) { + if (areSubclassesOfThisClassAlwaysExcluded (template.getType ())) { + return null; + } 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; - } + if (isObjectAccessible (template.getType (), retValue, 2)) { + return retValue; + } else { + return null; } - - return retValue; } /** @return true if the instance of class c shall never be returned from this lookup @@ -103,7 +95,7 @@ /** Returns the array of classes this lookup filters. */ - private Class[] classes () { + final Class[] classes () { if (classes instanceof Class[]) { return (Class[])classes; } else { @@ -111,15 +103,75 @@ } } + /** Does a check whether two classes are accessible (in the super/sub class) + * releation ship without walking thru any of the classes mentioned in the + * barrier. + */ + private static boolean isAccessible (Class[] barriers, Class from, Class to) { + if (to == null || !from.isAssignableFrom (to)) { + // no way to reach each other by walking up + return false; + } + + for (int i = 0; i < barriers.length; i++) { + if (to == barriers[i]) { + return false; + } + } + + if (from == to) { + return true; + } + + // + // depth first search + // + + if (isAccessible (barriers, from, to.getSuperclass ())) { + return true; + } + + Class[] interfaces = to.getInterfaces (); + for (int i = 0; i < interfaces.length; i++) { + if (isAccessible (barriers, from, interfaces[i])) { + return true; + } + } + + return false; + } + /** based on type decides whether the class accepts or not anObject + * @param from the base type of the query + * @param to depending on value of type either Object, Class or Item + * @param type 0,1,2 for Object, Class or Item + * @return true if we can access the to from from by walking around the bariers */ - static boolean acceptFilter (Class filter, Object anObject, int type) { + private final boolean isObjectAccessible (Class from, Object to, int type) { + if (to == null) { + return false; + } + return isObjectAccessible (classes(), from, to, type); + } + + /** based on type decides whether the class accepts or not anObject + * @param barriers classes to avoid when testing reachability + * @param from the base type of the query + * @param to depending on value of type either Object, Class or Item + * @param type 0,1,2 for Object, Class or Item + * @return true if we can access the to from from by walking around the bariers + */ + static final boolean isObjectAccessible (Class[] barriers, Class from, Object to, int type) { + if (to == null) { + return false; + } + switch (type) { - case 0: return filter.isInstance (anObject); - case 1: return filter.isAssignableFrom ((Class)anObject); + case 0: return isAccessible (barriers, from, to.getClass ()); + case 1: return isAccessible (barriers, from, (Class)to); case 2: { - Item item = (Item)anObject; - return filter.isAssignableFrom (item.getType ()); + Item item = (Item)to; + return isAccessible (barriers, from, item.getType ()); } default: throw new IllegalStateException ("Type: " + type); } @@ -127,27 +179,26 @@ /** Filters collection accroding to set of given filters. */ - static java.util.Collection filter (Class[] arr, java.util.Collection c, int type) { + final java.util.Collection filter (Class[] arr, Class from, java.util.Collection c, int type) { java.util.Collection ret = null; + // optimistic strategy expecting we will not need to filter 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; + if (!isObjectAccessible (arr, from, 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 BIG; + continue TWICE; } + continue BIG; } if (ret != null) { // if we are running the second round from TWICE @@ -167,8 +218,10 @@ private final class R extends WaitableResult implements LookupListener { private Result result; private Object listeners; + private Class from; - R (Result delegate) { + R (Class from, Result delegate) { + this.from = from; this.result = delegate; } @@ -201,15 +254,15 @@ } public java.util.Collection allInstances() { - return filter (classes (), result.allInstances (), 0); + return filter (classes (), from, result.allInstances (), 0); } public Set allClasses () { - return (Set)filter (classes (), result.allClasses (), 1); + return (Set)filter (classes (), from, result.allClasses (), 1); } public Collection allItems () { - return filter (classes (), result.allItems (), 2); + return filter (classes (), from, result.allItems (), 2); } public void resultChanged (org.openide.util.LookupEvent ev) { Index: openide/test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java,v retrieving revision 1.1 diff -u -r1.1 ExcludingLookupTest.java --- openide/test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java 14 Jan 2005 13:09:35 -0000 1.1 +++ openide/test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java 21 Jan 2005 09:39:42 -0000 @@ -51,8 +51,33 @@ 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 testWeCanGetInstanceOfSerializableEvenItIsExcludedIfWeAskForClassNotExtendingIt () throws Exception { + Lookup lookup = Lookups.exclude (this.instanceLookup, new Class[] { java.io.Serializable.class }); + Lookup.Template t = new Lookup.Template (Object.class); + 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 ("Not Filtered out", inst, lookup.lookup (Object.class)); + assertEquals ("Not Filtered out2", inst, lookup.lookupItem (t).getInstance ()); + 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", 1, 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 (Object.class)); + assertEquals ("Right # of events", 1, ll.getCount ()); } public void testIntegersQueriedThruObject () throws Exception { @@ -150,5 +175,31 @@ assertEquals ("Of course it is not there", null, lookup.lookup (theQuery)); assertEquals ("Right # of events", numberOfExcpectedEventsAfterOneChange, ll.getCount ()); } - + + public void testTheBehaviourAsRequestedByDavidAndDescribedByJesse () throws Exception { + class C implements Runnable, java.io.Serializable { + public void run () {} + } + Object c = new C(); + Lookup l1 = Lookups.singleton(c); + Lookup l2 = Lookups.exclude(l1, new Class[] {Runnable.class}); + assertNull(l2.lookup(Runnable.class)); + assertEquals(c, l2.lookup(java.io.Serializable.class)); + } + + public void testTheBehaviourAsRequestedByDavidAndDescribedByJesseWithUsageOfResult () throws Exception { + class C implements Runnable, java.io.Serializable { + public void run () {} + } + Object c = new C(); + Lookup l1 = Lookups.singleton(c); + Lookup l2 = Lookups.exclude(l1, new Class[] {Runnable.class}); + + Lookup.Result run = l2.lookup (new Lookup.Template (Runnable.class)); + Lookup.Result ser = l2.lookup (new Lookup.Template (java.io.Serializable.class)); + + assertEquals ("Runnables filtered out", 0, run.allItems ().size ()); + assertEquals ("One serialiazble", 1, ser.allItems ().size ()); + assertEquals ("And it is c", c, ser.allInstances ().iterator ().next ()); + } }