diff -r 002c8f68e8df -r eb844a3d737f .hgtags --- a/.hgtags Thu Jun 19 01:13:25 2008 +0200 +++ b/.hgtags Fri Jun 20 15:26:08 2008 +0200 @@ -893,3 +893,4 @@ 2fe642f5246f9e464deb29395b52fbedd3e0c120 2fe642f5246f9e464deb29395b52fbedd3e0c120 release61_base e08db7ef4b64aa1369693ab815ecfd614eb6363b merge_php_to_main d7584415c0aa6226b2cd511f65942827a0da78d3 JavaScriptLibrarySupportTechnologyPreview +002c8f68e8dfb283e0e17eba9fe455a4a43e58ea asynchronous_lookup_events_base diff -r 002c8f68e8df -r eb844a3d737f openide.loaders/apichanges.xml --- a/openide.loaders/apichanges.xml Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.loaders/apichanges.xml Fri Jun 20 15:26:08 2008 +0200 @@ -106,6 +106,25 @@ is the proper place. + + + Changes in FolderLookup are delivered in dedicated thread + + + + + +

+ Changes found in FolderLookup are delivered in own + thread. This is a change to previous behaviour which used + Folder recognizer thread for the event delivery, which could + caused deadlocks, if mallicious code decided to do something wild + in the resultChanged method. +

+
+ + +
DataObjects without DataLoaders diff -r 002c8f68e8df -r eb844a3d737f openide.loaders/manifest.mf --- a/openide.loaders/manifest.mf Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.loaders/manifest.mf Fri Jun 20 15:26:08 2008 +0200 @@ -1,6 +1,6 @@ Manifest-Version: 1.0 Manifest-Version: 1.0 OpenIDE-Module: org.openide.loaders -OpenIDE-Module-Specification-Version: 7.1 +OpenIDE-Module-Specification-Version: 7.2 OpenIDE-Module-Localizing-Bundle: org/openide/loaders/Bundle.properties OpenIDE-Module-Recommends: org.netbeans.modules.templates.v1_0 AutoUpdate-Essential-Module: true diff -r 002c8f68e8df -r eb844a3d737f openide.loaders/nbproject/project.xml --- a/openide.loaders/nbproject/project.xml Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.loaders/nbproject/project.xml Fri Jun 20 15:26:08 2008 +0200 @@ -133,7 +133,7 @@ made subject to such option by the copyr - 7.8 + 7.16 diff -r 002c8f68e8df -r eb844a3d737f openide.loaders/src/org/openide/loaders/FolderLookup.java --- a/openide.loaders/src/org/openide/loaders/FolderLookup.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.loaders/src/org/openide/loaders/FolderLookup.java Fri Jun 20 15:26:08 2008 +0200 @@ -280,6 +280,7 @@ public class FolderLookup extends Folder /** ProxyLookup delegate so we can change the lookups on fly. */ static final class ProxyLkp extends ProxyLookup implements Serializable { + static final RequestProcessor DISPATCH = new RequestProcessor("Lookup Dispatch Thread"); // NOI18N private static final long serialVersionUID = 1L; @@ -294,7 +295,7 @@ public class FolderLookup extends Folder /** Constructs lookup which holds all items+lookups from underlying world. * @param folder FolderLookup to associate to */ public ProxyLkp(FolderLookup folder) { - this(folder, new AbstractLookup.Content()); + this(folder, new AbstractLookup.Content(DISPATCH)); } /** Constructs lookup. */ @@ -305,6 +306,7 @@ public class FolderLookup extends Folder this.content = content; } + @Override public String toString() { return "FolderLookup.lookup[\"" + fl.rootName + "\"]"; } @@ -335,7 +337,7 @@ public class FolderLookup extends Folder content = (AbstractLookup.Content)ois.readObject (); - setLookups (arr); + setLookups (DISPATCH, arr); readFromStream = true; org.openide.util.RequestProcessor.getDefault ().post (fl, 0, Thread.MIN_PRIORITY); @@ -359,11 +361,12 @@ public class FolderLookup extends Folder lookups.set(0, pairs); Lookup[] arr = (Lookup[])lookups.toArray (new Lookup[lookups.size ()]); - setLookups (arr); + setLookups (DISPATCH, arr); if (fl.err().isLoggable(Level.FINE)) fl.err ().fine("Changed lookups: " + lookups); // NOI18N } /** Waits before the processing of changes is finished. */ + @Override protected void beforeLookup (Template template) { if (readFromStream) { // ok diff -r 002c8f68e8df -r eb844a3d737f openide.loaders/test/unit/src/org/openide/loaders/FolderLookupTest.java --- a/openide.loaders/test/unit/src/org/openide/loaders/FolderLookupTest.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.loaders/test/unit/src/org/openide/loaders/FolderLookupTest.java Fri Jun 20 15:26:08 2008 +0200 @@ -52,13 +52,17 @@ import org.openide.filesystems.FileUtil; import org.openide.filesystems.FileUtil; import org.openide.filesystems.Repository; import org.openide.util.Lookup; -import org.openide.util.Lookup.Result; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; import org.openide.util.lookup.AbstractLookup; import org.openide.util.lookup.InstanceContent; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; -public class FolderLookupTest extends NbTestCase { +public class FolderLookupTest extends NbTestCase implements LookupListener { + private Lookup.Result res; + private boolean bad; + private String threadName = ""; public FolderLookupTest(java.lang.String testName) { super(testName); @@ -68,11 +72,45 @@ public class FolderLookupTest extends Nb System.setProperty ("org.openide.util.Lookup", GLkp.class.getName()); } + @Override protected void setUp() throws Exception { super.setUp(); clearWorkDir(); } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + if (res != null) { + res.allItems(); + FolderLookup.ProxyLkp.DISPATCH.post(new Runnable() { + public void run() { + } + }).waitFinished(); + if (threadName.contains("Folder") || threadName.length() == 0) { + fail("Wrong thread name: " + threadName); + } + } + } + + + private void addListener(Lookup l) { + bad = true; + assertNull("No result yet", res); + res = l.lookupResult(Object.class); + res.addLookupListener(this); + res.allItems(); + } + + public void resultChanged(LookupEvent ev) { + if (threadName.length() == 0) { + threadName = Thread.currentThread().getName(); + } else { + threadName = threadName + ", " + Thread.currentThread().getName(); + } + } + /** Test of the lookup method. Creates a file under Services directory * and tries to lookup it. The object should be immediatelly found. * @@ -100,6 +138,8 @@ public class FolderLookupTest extends Nb private void checkTheLookup (Lookup lookup, DataFolder folder) throws Exception { + addListener(lookup); + Class toFind = java.util.Dictionary.class; Class toCreate = java.util.Hashtable.class; @@ -145,6 +185,8 @@ public class FolderLookupTest extends Nb } private void checkTheLookupForSubfolders (Lookup lookup, DataFolder folder) throws Exception { + addListener(lookup); + Class toFind = java.awt.Component.class; Class toCreate = javax.swing.JButton.class; @@ -180,10 +222,11 @@ public class FolderLookupTest extends Nb DataFolder folder = DataFolder.findFolder (bb); - InstanceDataObject obj = InstanceDataObject.create (folder, null, ALkp.class); Lookup lookup = new org.openide.loaders.FolderLookup (folder).getLookup (); + addListener(lookup); + InstanceDataObject obj = InstanceDataObject.create (folder, null, ALkp.class); if (lookup.lookup (Integer.class) == null) { fail ("Integer not found in delegating lookup"); } @@ -337,8 +380,11 @@ public class FolderLookupTest extends Nb } catch (Exception ex) { this.ex = ex; } - // and this will deadlock - lookup.lookup (String.class); + Lookup l = lookup; + if (l != null) { + // and this will deadlock + lookup.lookup (String.class); + } } } diff -r 002c8f68e8df -r eb844a3d737f openide.util/apichanges.xml --- a/openide.util/apichanges.xml Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/apichanges.xml Fri Jun 20 15:26:08 2008 +0200 @@ -49,6 +49,47 @@ made subject to such option by the copyr Actions API + + + AbstractLookup and ProxyLookup fires changes asynchronously + + + + + +

+ All modification methods in AbstractLookup and ProxyLookup + were extended to accept an + Executor. + If not null, it is used to dispatch events to listeners sometime + "later". Also the AbstractLookup.Content + and InstanceContent constructors + have been extended to accept such Executor. +

+
+ + + + +
+ + + RequestProcessor implements Executor interface + + + + + +

+ In order to align RequestProcessor closer to standard + Java 5 concurency utilities, the class now implements + Executor + interfaces and its execute(Runnable) method. +

+
+ + +
Image methods were made deprecated in Utilities, replacement is diff -r 002c8f68e8df -r eb844a3d737f openide.util/nbproject/project.properties --- a/openide.util/nbproject/project.properties Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/nbproject/project.properties Fri Jun 20 15:26:08 2008 +0200 @@ -41,7 +41,7 @@ javac.source=1.5 javac.source=1.5 module.jar.dir=lib -spec.version.base=7.15.0 +spec.version.base=7.16.0 # For XMLSerializer, needed for XMLUtil.write to work w/ namespaces under JDK 1.4: diff -r 002c8f68e8df -r eb844a3d737f openide.util/src/org/openide/util/RequestProcessor.java --- a/openide.util/src/org/openide/util/RequestProcessor.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/src/org/openide/util/RequestProcessor.java Fri Jun 20 15:26:08 2008 +0200 @@ -48,6 +48,7 @@ import java.util.Stack; import java.util.Stack; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; @@ -113,10 +114,11 @@ import java.util.logging.Logger; * } * } * + * Since version 7.16 it implements {@link Executor} * * @author Petr Nejedly, Jaroslav Tulach */ -public final class RequestProcessor { +public final class RequestProcessor implements Executor{ /** the static instance for users that do not want to have own processor */ private static RequestProcessor DEFAULT = new RequestProcessor(); @@ -240,6 +242,15 @@ public final class RequestProcessor { return UNLIMITED; } + /** Implements contract of {@link Executor}. + * Simply delegates to {@link #post(java.lang.Runnable)}. + * @param command the runnable to execute + * @since 7.16 + */ + public void execute(Runnable command) { + post(command); + } + /** This methods asks the request processor to start given * runnable immediately. The default priority is {@link Thread#MIN_PRIORITY}. * diff -r 002c8f68e8df -r eb844a3d737f openide.util/src/org/openide/util/lookup/AbstractLookup.java --- a/openide.util/src/org/openide/util/lookup/AbstractLookup.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/src/org/openide/util/lookup/AbstractLookup.java Fri Jun 20 15:26:08 2008 +0200 @@ -63,6 +63,7 @@ import java.util.Set; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.Executor; import org.openide.util.Utilities; @@ -200,10 +201,19 @@ public class AbstractLookup extends Look * @param pair class/instance pair */ protected final void addPair(Pair pair) { - addPairImpl(pair); + addPairImpl(pair, null); } - private final void addPairImpl(Pair pair) { + /** The method to add instance to the lookup with. + * @param pair class/instance pair + * @param notifyIn the executor that will handle the notification of events + * @since 7.16 + */ + protected final void addPair(Pair pair, Executor notifyIn) { + addPairImpl(pair, notifyIn); + } + + private final void addPairImpl(Pair pair, Executor notifyIn) { HashSet toNotify = new HashSet(); AbstractLookup.Storage t = enterStorage(); @@ -233,17 +243,25 @@ public class AbstractLookup extends Look exitStorage(); } - notifyListeners(toNotify); + notifyIn(notifyIn, toNotify); } /** Remove instance. * @param pair class/instance pair */ protected final void removePair(Pair pair) { - removePairImpl(pair); + removePairImpl(pair, null); + } + /** Remove instance. + * @param pair class/instance pair + * @param notifyIn the executor that will handle the notification of events + * @since 7.16 + */ + protected final void removePair(Pair pair, Executor notifyIn) { + removePairImpl(pair, notifyIn); } - private void removePairImpl(Pair pair) { + private void removePairImpl(Pair pair, Executor notifyIn) { HashSet toNotify = new HashSet(); AbstractLookup.Storage t = enterStorage(); @@ -257,14 +275,40 @@ public class AbstractLookup extends Look exitStorage(); } - notifyListeners(toNotify); + notifyIn(notifyIn, toNotify); } /** Changes all pairs in the lookup to new values. * @param collection the collection of (Pair) objects */ protected final void setPairs(Collection collection) { - notifyCollectedListeners(setPairsAndCollectListeners(collection)); + setPairs(collection, null); + } + + /** Changes all pairs in the lookup to new values, notifies listeners + * using provided executor. + * + * @param collection the collection of (Pair) objects + * @param notifyIn the executor that will handle the notification of events + * @since 7.16 + */ + protected final void setPairs(Collection collection, Executor notifyIn) { + HashSet listeners = setPairsAndCollectListeners(collection); + notifyIn(notifyIn, listeners); + } + + private final void notifyIn(Executor notifyIn, final HashSet listeners) { + if (notifyIn == null) { + notifyListeners(listeners); + return; + } + + class Notify implements Runnable { + public void run() { + notifyListeners(listeners); + } + } + notifyIn.execute(new Notify()); } /** Getter for set of pairs. Package private contract with MetaInfServicesLookup. @@ -349,13 +393,6 @@ public class AbstractLookup extends Look } return toNotify; - } - - /** Notifies all collected listeners. Needed by MetaInfServicesLookup, - * maybe it will be an API later. - */ - final void notifyCollectedListeners(Set listeners) { - notifyListeners(listeners); } private final void writeObject(ObjectOutputStream oos) @@ -461,7 +498,7 @@ public class AbstractLookup extends Look /** Notifies listeners. * @param allAffectedResults set of R */ - private static void notifyListeners(Set allAffectedResults) { + static void notifyListeners(Set allAffectedResults) { if (allAffectedResults.isEmpty()) { return; } @@ -1078,8 +1115,28 @@ public class AbstractLookup extends Look // one of them is always null (except attach stage) /** abstract lookup we are connected to */ - private AbstractLookup al = null; - private transient ArrayList earlyPairs; + private AbstractLookup al; + private transient Object notifyIn; + + /** Default constructor. + */ + public Content() { + this(null); + } + + /** Creates a content associated with an executor to handle dispatch + * of changes. + * @param notifyIn the executor to notify changes in + * @since 7.16 + */ + public Content(Executor notifyIn) { + this.notifyIn = notifyIn; + } + + /** for testing purposes */ + final void attachExecutor(Executor notifyIn) { + this.notifyIn = notifyIn; + } /** A lookup attaches to this object. */ @@ -1087,9 +1144,9 @@ public class AbstractLookup extends Look if (this.al == null) { this.al = al; - if (earlyPairs != null) { - ArrayList ep = earlyPairs; - earlyPairs = null; + ArrayList ep = getEarlyPairs(); + if (ep != null) { + notifyIn = null; setPairs(ep); } } else { @@ -1104,15 +1161,16 @@ public class AbstractLookup extends Look */ public final void addPair(Pair pair) { AbstractLookup a = al; + Executor e = getExecutor(); - if (a != null) { - a.addPair(pair); + if (a != null || e != null) { + a.addPair(pair, e); } else { - if (earlyPairs == null) { - earlyPairs = new ArrayList(3); + if (notifyIn == null) { + notifyIn = new ArrayList(3); } - earlyPairs.add(pair); + getEarlyPairs().add(pair); } } @@ -1121,15 +1179,16 @@ public class AbstractLookup extends Look */ public final void removePair(Pair pair) { AbstractLookup a = al; + Executor e = getExecutor(); - if (a != null) { - a.removePair(pair); + if (a != null || e != null) { + a.removePair(pair, e); } else { - if (earlyPairs == null) { - earlyPairs = new ArrayList(3); + if (notifyIn == null) { + notifyIn = new ArrayList(3); } - earlyPairs.remove(pair); + getEarlyPairs().remove(pair); } } @@ -1138,12 +1197,24 @@ public class AbstractLookup extends Look */ public final void setPairs(Collection c) { AbstractLookup a = al; + Executor e = getExecutor(); + + if (a != null || e != null) { + a.setPairs(c, e); + } else { + notifyIn = new ArrayList(c); + } + } - if (a != null) { - a.setPairs(c); - } else { - earlyPairs = new ArrayList(c); - } + @SuppressWarnings("unchecked") + private ArrayList getEarlyPairs() { + Object o = notifyIn; + return o instanceof ArrayList ? (ArrayList)o : null; + } + + private Executor getExecutor() { + Object o = notifyIn; + return o instanceof Executor ? (Executor)o : null; } } // end of Content diff -r 002c8f68e8df -r eb844a3d737f openide.util/src/org/openide/util/lookup/InstanceContent.java --- a/openide.util/src/org/openide/util/lookup/InstanceContent.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/src/org/openide/util/lookup/InstanceContent.java Fri Jun 20 15:26:08 2008 +0200 @@ -40,12 +40,12 @@ */ package org.openide.util.lookup; -import org.openide.util.lookup.AbstractLookup; import org.openide.util.lookup.AbstractLookup.Pair; import java.lang.ref.WeakReference; import java.util.*; +import java.util.concurrent.Executor; /** A special content implementation that can be passed to AbstractLookup @@ -71,6 +71,14 @@ public final class InstanceContent exten public InstanceContent() { } + /** Creates a content associated with an executor to handle dispatch + * of changes. + * @param notifyIn the executor to notify changes in + * @since 7.16 + */ + public InstanceContent(Executor notifyIn) { + super(notifyIn); + } /** The method to add instance to the lookup with. * @param inst instance */ @@ -190,6 +198,7 @@ public final class InstanceContent exten return obj; } + @Override public boolean equals(Object o) { if (o instanceof SimpleItem) { return obj.equals(((SimpleItem) o).obj); @@ -198,6 +207,7 @@ public final class InstanceContent exten } } + @Override public int hashCode() { return obj.hashCode(); } @@ -292,6 +302,7 @@ public final class InstanceContent exten return converted; } + @Override public boolean equals(Object o) { if (o instanceof ConvertingItem) { return obj.equals(((ConvertingItem) o).obj); @@ -300,6 +311,7 @@ public final class InstanceContent exten } } + @Override public int hashCode() { return obj.hashCode(); } diff -r 002c8f68e8df -r eb844a3d737f openide.util/src/org/openide/util/lookup/MetaInfServicesLookup.java --- a/openide.util/src/org/openide/util/lookup/MetaInfServicesLookup.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/src/org/openide/util/lookup/MetaInfServicesLookup.java Fri Jun 20 15:26:08 2008 +0200 @@ -128,7 +128,7 @@ final class MetaInfServicesLookup extend } } - notifyCollectedListeners(listeners); + notifyListeners(listeners); } /** Finds all pairs and adds them to the collection. diff -r 002c8f68e8df -r eb844a3d737f openide.util/src/org/openide/util/lookup/ProxyLookup.java --- a/openide.util/src/org/openide/util/lookup/ProxyLookup.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/src/org/openide/util/lookup/ProxyLookup.java Fri Jun 20 15:26:08 2008 +0200 @@ -55,6 +55,7 @@ import java.util.Map; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.Executor; import javax.swing.event.EventListenerList; import org.openide.util.Lookup; import org.openide.util.LookupEvent; @@ -115,6 +116,18 @@ public class ProxyLookup extends Lookup * @since 1.19 protected */ protected final void setLookups(Lookup... lookups) { + setLookups(null, lookups); + } + + /** + * Changes the delegates immediatelly, notifies the listeners in provided + * executor, potentially later. + * + * @param lookups the new lookups to delegate to + * @param notifyIn executor to deliver the notification to listeners or null + * @since 7.16 + */ + protected final void setLookups(Executor notifyIn, Lookup... lookups) { Collection> arr; Set newL; Set current; @@ -143,7 +156,7 @@ public class ProxyLookup extends Lookup // this cannot be done from the synchronized block - ArrayList evAndListeners = new ArrayList(); + final ArrayList evAndListeners = new ArrayList(); for (Reference ref : arr) { R r = ref.get(); if (r != null) { @@ -151,13 +164,21 @@ public class ProxyLookup extends Lookup } } - { - Iterator it = evAndListeners.iterator(); - while (it.hasNext()) { - LookupEvent ev = (LookupEvent)it.next(); - LookupListener l = (LookupListener)it.next(); - l.resultChanged(ev); + class Notify implements Runnable { + public void run() { + Iterator it = evAndListeners.iterator(); + while (it.hasNext()) { + LookupEvent ev = (LookupEvent)it.next(); + LookupListener l = (LookupListener)it.next(); + l.resultChanged(ev); + } } + } + Notify n = new Notify(); + if (notifyIn == null) { + n.run(); + } else { + notifyIn.execute(n); } } diff -r 002c8f68e8df -r eb844a3d737f openide.util/test/unit/src/org/openide/util/lookup/AbstractLookupBaseHid.java --- a/openide.util/test/unit/src/org/openide/util/lookup/AbstractLookupBaseHid.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/test/unit/src/org/openide/util/lookup/AbstractLookupBaseHid.java Fri Jun 20 15:26:08 2008 +0200 @@ -47,7 +47,6 @@ import org.openide.util.*; import java.lang.ref.WeakReference; import java.util.*; -import junit.framework.*; import org.netbeans.junit.*; import java.io.Serializable; import java.lang.ref.Reference; @@ -1688,6 +1687,7 @@ public class AbstractLookupBaseHid exten protected static class LL implements LookupListener { private int count = 0; public Object source; + public Thread changesIn; public LL () { this (null); @@ -1698,6 +1698,11 @@ public class AbstractLookupBaseHid exten } public void resultChanged(LookupEvent ev) { + if (changesIn != null) { + assertEquals("Changes in the same thread", changesIn, Thread.currentThread()); + } else { + changesIn = Thread.currentThread(); + } ++count; if (source != null) { assertSame ("Source is the same", source, ev.getSource ()); diff -r 002c8f68e8df -r eb844a3d737f openide.util/test/unit/src/org/openide/util/lookup/AbstractLookupExecutorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.util/test/unit/src/org/openide/util/lookup/AbstractLookupExecutorTest.java Fri Jun 20 15:26:08 2008 +0200 @@ -0,0 +1,97 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * 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. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.openide.util.lookup; + +import org.openide.util.*; + +import java.util.concurrent.Executor; + +public class AbstractLookupExecutorTest extends AbstractLookupBaseHid +implements AbstractLookupBaseHid.Impl, Executor, LookupListener { + Lookup.Result res; + + + public AbstractLookupExecutorTest(java.lang.String testName) { + super(testName, null); + } + + // + // Impl of AbstractLookupBaseHid.Impl + // + + /** Creates the initial abstract lookup. + */ + public Lookup createInstancesLookup (InstanceContent ic) { + ic.attachExecutor(this); + Lookup l = new AbstractLookup (ic, new InheritanceTree ()); + return l; + } + + /** 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 + */ + public Lookup createLookup (Lookup lookup) { + res = lookup.lookupResult(Object.class); + res.addLookupListener(this); + return lookup; + } + + public void clearCaches () { + } + + ThreadLocal ME = new ThreadLocal(); + public void execute(Runnable command) { + assertEquals("Not yet set", null, ME.get()); + ME.set(this); + try { + command.run(); + } finally { + ME.set(null); + } + } + + public void resultChanged(LookupEvent ev) { + assertEquals("Changes delivered only from execute method", this, ME.get()); + } +} diff -r 002c8f68e8df -r eb844a3d737f openide.util/test/unit/src/org/openide/util/lookup/ProxyLookupTest.java --- a/openide.util/test/unit/src/org/openide/util/lookup/ProxyLookupTest.java Thu Jun 19 01:13:25 2008 +0200 +++ b/openide.util/test/unit/src/org/openide/util/lookup/ProxyLookupTest.java Fri Jun 20 15:26:08 2008 +0200 @@ -47,6 +47,8 @@ import java.lang.ref.Reference; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.Executor; import junit.framework.*; import org.netbeans.junit.*; import org.openide.util.Lookup.Result; @@ -125,6 +127,64 @@ implements AbstractLookupBaseHid.Impl { lookup.setLookups (new Lookup[] { del }); + if (ll.getCount () != 0) { + fail ("Calling setLookups (thesamearray) fired a change"); + } + } + + public void testNoListenersProxyListener () { + ProxyLookup lookup = new ProxyLookup (new Lookup[0]); + class E implements Executor { + Runnable r; + public void execute(Runnable command) { + assertNull("NO previous", r); + r = command; + } + public void perform() { + assertNotNull("We shall have a runnable", r); + r.run(); + r = null; + } + } + E executor = new E(); + + + final Lookup.Template template = new Lookup.Template(Object.class); + final Object[] IGNORE = { + ProxyLookup.ImmutableInternalData.EMPTY, + ProxyLookup.ImmutableInternalData.EMPTY_ARR, + Utilities.activeReferenceQueue(), + Collections.emptyMap(), + Collections.emptyList(), + Collections.emptySet() + }; + + assertSize("Pretty small", Collections.singleton(lookup), 16, IGNORE); + + Lookup.Result res = lookup.lookup (template); + + assertSize("Bigger", Collections.singleton(lookup), 216, IGNORE); + + LL ll = new LL (); + res.addLookupListener (ll); + Collection allRes = res.allInstances (); + + lookup.setLookups (executor, new Lookup[0]); + if (ll.getCount () != 0) { + fail ("Calling setLookups (emptyarray) fired a change"); + } + + InstanceContent t = new InstanceContent(); + Lookup del = new AbstractLookup (t); + t.add("Ahoj"); + lookup.setLookups (executor, new Lookup[] { del }); + assertEquals("No change yet", 0, ll.getCount()); + executor.perform(); + if (ll.getCount () != 1) { + fail ("Changing lookups did not generate an event"); + } + + lookup.setLookups (executor, new Lookup[] { del }); if (ll.getCount () != 0) { fail ("Calling setLookups (thesamearray) fired a change"); }