Index: src/org/openide/util/lookup/Lookups.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/Lookups.java,v retrieving revision 1.3 diff -c -r1.3 Lookups.java *** src/org/openide/util/lookup/Lookups.java 3 Jul 2002 00:02:36 -0000 1.3 --- src/org/openide/util/lookup/Lookups.java 18 Sep 2002 17:05:30 -0000 *************** *** 94,97 **** --- 94,118 ---- return new SimpleLookup(Arrays.asList(keys), convertor); } + + /** Creates a lookup that delegates to another one but that one can change + * from time to time. The returned lookup checks every time somebody calls + * lookup or lookupItem method whether the + * provider still returns the same lookup. If not, it updates state of + * all Lookup.Results that it created (and that still exists). + *

+ * The user of this method has to implement its provider's getLookup + * method (must be thread safe and fast, will be called often and from any thread) + * pass it to this method and use the returned lookup. Whenever the user + * changes the return value from the getLookup method and wants + * to notify listeners on the lookup about that it should trigger the event + * firing, for example by calling lookup.lookup (Object.class) + * that forces check of the return value of getLookup. + * + * @param provider the provider that returns a lookup to delegate to + * @return lookup delegating to the lookup returned by the provider + */ + public static Lookup proxy (Lookup.Provider provider) { + return new SimpleProxyLookup (provider); + } } Index: src/org/openide/util/lookup/SimpleProxyLookup.java =================================================================== RCS file: src/org/openide/util/lookup/SimpleProxyLookup.java diff -N src/org/openide/util/lookup/SimpleProxyLookup.java *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/org/openide/util/lookup/SimpleProxyLookup.java 18 Sep 2002 17:05:30 -0000 *************** *** 0 **** --- 1,196 ---- + /* + * 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-2002 Sun + * Microsystems, Inc. All Rights Reserved. + */ + + package org.openide.util.lookup; + + import java.util.*; + + import org.openide.util.Lookup; + import org.openide.util.LookupListener; + import org.openide.util.LookupEvent; + + /** + * Simple proxy lookup. Keeps reference to a lookup it delegates to and + * forwards all requests. + * + * @author Jaroslav Tulach + */ + final class SimpleProxyLookup extends org.openide.util.Lookup { + /** the provider to check for the status */ + private Provider provider; + /** the lookup we currently delegate to */ + private Lookup delegate; + /** set of all results associated to this lookup */ + private org.openide.util.WeakSet results; + + /** + * @param provider provider to delegate to + */ + SimpleProxyLookup(Provider provider) { + this.provider = provider; + } + + /** Checks whether we still delegate to the same lookup */ + private Lookup checkLookup () { + Lookup l = provider.getLookup (); + + + Iterator toCheck = null; + synchronized (this) { + if (l != delegate) { + this.delegate = l; + if (results != null) { + toCheck = Arrays.asList (results.toArray ()).iterator(); + } + } + } + + if (toCheck != null) { + // update + Iterator it = toCheck; + while (it.hasNext()) { + ProxyResult p = (ProxyResult)it.next (); + if (p.updateLookup (l)) { + p.resultChanged (null); + } + } + } + + return delegate; + } + + public Result lookup(Template template) { + ProxyResult p = new ProxyResult (template); + + synchronized (this) { + if (results == null) { + results = new org.openide.util.WeakSet (); + } + results.add (p); + } + + return p; + } + + public Object lookup(Class clazz) { + return checkLookup ().lookup (clazz); + } + + public Item lookupItem(Template template) { + return checkLookup ().lookupItem (template); + } + + /** + * Result used in SimpleLookup. It holds a reference to the collection + * passed in constructor. As the contents of this lookup result never + * changes the addLookupListener and removeLookupListener are empty. + */ + private final class ProxyResult extends WaitableResult + implements LookupListener { + /** Template used for this result. It is never null.*/ + private Template template; + /** result to delegate to */ + private Lookup.Result delegate; + /** listeners set */ + private javax.swing.event.EventListenerList listeners; + + /** Just remembers the supplied argument in variable template.*/ + ProxyResult (Template template) { + this.template = template; + } + + /** Checks state of the result + */ + private Result checkResult () { + updateLookup (checkLookup ()); + return this.delegate; + } + + /** Updates the state of the lookup. + * @return true if the lookup really changed + */ + public boolean updateLookup (Lookup l) { + Collection oldPairs = delegate != null ? delegate.allItems () : null; + + synchronized (this) { + if (delegate != null) { + delegate.removeLookupListener (this); + } + delegate = l.lookup (template); + delegate.addLookupListener (this); + } + + if (oldPairs == null) { + // nobody knows about a change + return false; + } + + Collection newPairs = delegate.allItems (); + + return !oldPairs.equals (newPairs); + } + + + public synchronized void addLookupListener(LookupListener l) { + if (listeners == null) { + listeners = new javax.swing.event.EventListenerList (); + } + listeners.add (LookupListener.class, l); + } + + public synchronized void removeLookupListener(LookupListener l) { + if (listeners != null) { + listeners.remove (LookupListener.class, l); + } + } + + public java.util.Collection allInstances() { + return checkResult ().allInstances (); + } + + public Set allClasses () { + return checkResult ().allClasses (); + } + + public Collection allItems () { + return checkResult ().allItems (); + } + + protected void beforeLookup(Lookup.Template t) { + Lookup.Result r = checkResult (); + if (r instanceof WaitableResult) { + ((WaitableResult)r).beforeLookup (t); + } + } + + /** A change in lookup occured. + * @param ev event describing the change + * + */ + public void resultChanged(LookupEvent anEvent) { + javax.swing.event.EventListenerList l = this.listeners; + if (l == null) return; + + Object[] listeners = l.getListenerList(); + if (listeners.length == 0) return; + + LookupEvent ev = new LookupEvent (this); + + for (int i = listeners.length - 1; i >= 0; i -= 2) { + LookupListener ll = (LookupListener)listeners[i]; + ll.resultChanged(ev); + } + } + + } // end of ProxyResult + } Index: test/unit/src/org/openide/util/lookup/LookupsProxyTest.java =================================================================== RCS file: test/unit/src/org/openide/util/lookup/LookupsProxyTest.java diff -N test/unit/src/org/openide/util/lookup/LookupsProxyTest.java *** /dev/null 1 Jan 1970 00:00:00 -0000 --- test/unit/src/org/openide/util/lookup/LookupsProxyTest.java 18 Sep 2002 17:05:30 -0000 *************** *** 0 **** --- 1,105 ---- + /* + * 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 LookupsProxyTest extends AbstractLookupTest { + public LookupsProxyTest(java.lang.String testName) { + super(testName); + } + + public static void main(java.lang.String[] args) { + junit.textui.TestRunner.run(new NbTestSuite (LookupsProxyTest.class)); + } + + /** Creates an lookup for given lookup. This class just returns + * the object passed in, but subclasses can be different. + * @param lookup in lookup + * @return a lookup to use + */ + protected Lookup createLookup (final Lookup lookup) { + return org.openide.util.lookup.Lookups.proxy ( + new Lookup.Provider () { + public Lookup getLookup () { + return lookup; + } + } + ); + } + + + /** Check whether setLookups method does not fire when there is no + * change in the lookups. + */ + public void testProxyListener () { + Changer ch = new Changer (Lookup.EMPTY); + + Lookup lookup = Lookups.proxy(ch); + Lookup.Result res = lookup.lookup (new Lookup.Template (Object.class)); + + LL ll = new LL (); + res.addLookupListener (ll); + Collection allRes = res.allInstances (); + + ch.setLookup (new AbstractLookup (new InstanceContent ())); // another empty lookup + lookup.lookup (Object.class); // does the refresh + + if (ll.getCount () != 0) { + fail ("Replacing an empty by empty does not generate an event"); + } + + InstanceLookup del = new InstanceLookup (); + del.add (this); + ch.setLookup (del); + lookup.lookup (Object.class); + + if (ll.getCount () != 1) { + fail ("Changing lookups with different content generates an event"); + } + + ch.setLookup (del); + lookup.lookup (Object.class); + + if (ll.getCount () != 0) { + fail ("Not changing the lookups does not generate any event"); + } + } + + + private static final class Changer implements Lookup.Provider { + private Lookup lookup; + + public Changer (Lookup lookup) { + setLookup (lookup); + } + + public void setLookup (Lookup lookup) { + this.lookup = lookup; + } + + public Lookup getLookup() { + return lookup; + } + } + }