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.Result
s 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;
+ }
+ }
+ }