openide.util/src/org/openide/util/lookup/ProxyLookup.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 05 Feb 2008 19:43:00 +0100
branchweak_proxy_lookup_but_does_not_GC_template_and_its_class
changeset 340 c059ff4c4133
parent 326 00a401f0f875
child 341 29bbadcb8e5f
permissions -rw-r--r--
An attempt to make the result hold the data weak, but it does not work because of GC of template's class
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
     5  *
     6  * The contents of this file are subject to the terms of either the GNU
     7  * General Public License Version 2 only ("GPL") or the Common
     8  * Development and Distribution License("CDDL") (collectively, the
     9  * "License"). You may not use this file except in compliance with the
    10  * License. You can obtain a copy of the License at
    11  * http://www.netbeans.org/cddl-gplv2.html
    12  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    13  * specific language governing permissions and limitations under the
    14  * License.  When distributing the software, include this License Header
    15  * Notice in each file and include the License file at
    16  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    17  * particular file as subject to the "Classpath" exception as provided
    18  * by Sun in the GPL Version 2 section of the License file that
    19  * accompanied this code. If applicable, add the following below the
    20  * License Header, with the fields enclosed by brackets [] replaced by
    21  * your own identifying information:
    22  * "Portions Copyrighted [year] [name of copyright owner]"
    23  *
    24  * Contributor(s):
    25  *
    26  * The Original Software is NetBeans. The Initial Developer of the Original
    27  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
    28  * Microsystems, Inc. All Rights Reserved.
    29  *
    30  * If you wish your version of this file to be governed by only the CDDL
    31  * or only the GPL Version 2, indicate your decision by adding
    32  * "[Contributor] elects to include this software in this distribution
    33  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    34  * single choice of license, a recipient has the option to distribute
    35  * your version of this file under either the CDDL, the GPL Version 2 or
    36  * to extend the choice of license to its licensees as provided above.
    37  * However, if you add GPL Version 2 code and therefore, elected the GPL
    38  * Version 2 license, then the option applies only if the new code is
    39  * made subject to such option by the copyright holder.
    40  */
    41 
    42 package org.openide.util.lookup;
    43 
    44 import java.lang.ref.Reference;
    45 import java.lang.ref.WeakReference;
    46 import java.util.ArrayList;
    47 import java.util.Arrays;
    48 import java.util.Collection;
    49 import java.util.Collections;
    50 import java.util.HashMap;
    51 import java.util.HashSet;
    52 import java.util.IdentityHashMap;
    53 import java.util.Iterator;
    54 import java.util.List;
    55 import java.util.Map;
    56 import java.util.Set;
    57 import javax.swing.event.EventListenerList;
    58 import org.openide.util.Lookup;
    59 import org.openide.util.LookupEvent;
    60 import org.openide.util.LookupListener;
    61 import org.openide.util.Utilities;
    62 
    63 /** Implementation of lookup that can delegate to others.
    64  *
    65  * @author  Jaroslav Tulach
    66  * @since 1.9
    67  */
    68 public class ProxyLookup extends Lookup {
    69     /** empty array of lookups for potential use */
    70     static final Lookup[] EMPTY_ARR = new Lookup[0];
    71 
    72     /** lookups to delegate to (either Lookup or array of Lookups) */
    73     private Object lookups;
    74 
    75     /** data representing the state of the lookup */
    76     private ImmutableInternalData data;
    77 
    78     /** Create a proxy to some other lookups.
    79      * @param lookups the initial delegates
    80      */
    81     public ProxyLookup(Lookup... lookups) {
    82         this.setLookupsNoFire(lookups);
    83     }
    84 
    85     /**
    86      * Create a lookup initially proxying to no others.
    87      * Permits serializable subclasses.
    88      * @since 3.27
    89      */
    90     protected ProxyLookup() {
    91         this(EMPTY_ARR);
    92     }
    93 
    94     @Override
    95     public String toString() {
    96         return "ProxyLookup(class=" + getClass() + ")->" + Arrays.asList(getLookups(false)); // NOI18N
    97     }
    98 
    99     /** Getter for the delegates.
   100     * @return the array of lookups we delegate to
   101     * @since 1.19
   102     */
   103     protected final Lookup[] getLookups() {
   104         synchronized (ProxyLookup.this) {
   105             return getLookups(true);
   106         }
   107     }
   108 
   109     /** getter for the delegates, that can but need not do a clone.
   110      * @param clone true if clone of internal array is requested
   111      */
   112     private final Lookup[] getLookups(boolean clone) {
   113         assert Thread.holdsLock(ProxyLookup.this);
   114         Object l = this.lookups;
   115         if (l instanceof Lookup) {
   116             return new Lookup[] { (Lookup)l };
   117         } else {
   118             Lookup[] arr = (Lookup[])l;
   119             if (clone) {
   120                 arr = arr.clone();
   121             }
   122             return arr;
   123         }
   124     }
   125 
   126     private Set<Lookup> identityHashSet(Collection<Lookup> current) {
   127         Map<Lookup,Void> map = new IdentityHashMap<Lookup, Void>();
   128         for (Lookup lookup : current) {
   129             map.put(lookup, null);
   130         }
   131         return map.keySet();
   132     }
   133     
   134     /** Called from setLookups and constructor. 
   135      * @param lookups the lookups to setup
   136      */
   137     private void setLookupsNoFire(Lookup[] lookups) {
   138         if (lookups.length == 1) {
   139             this.lookups = lookups[0];
   140             assert this.lookups != null : "Cannot assign null delegate";
   141         } else {
   142             if (lookups.length == 0) {
   143                 this.lookups = EMPTY_ARR;
   144             } else {
   145                 this.lookups = lookups.clone();
   146             }
   147         }
   148     }
   149 
   150     /**
   151      * Changes the delegates.
   152      *
   153      * @param lookups the new lookups to delegate to
   154      * @since 1.19 protected
   155      */
   156     protected final void setLookups(Lookup... lookups) {
   157         Collection<Reference<R>> arr;
   158         Set<Lookup> newL;
   159         Set<Lookup> current;
   160         Lookup[] old;
   161         
   162         Map<Result,LookupListener> toRemove = new IdentityHashMap<Lookup.Result, LookupListener>();
   163         Map<Result,LookupListener> toAdd = new IdentityHashMap<Lookup.Result, LookupListener>();
   164         
   165         synchronized (ProxyLookup.this) {
   166             old = getLookups(false);
   167             current = identityHashSet(Arrays.asList(old));
   168             newL = identityHashSet(Arrays.asList(lookups));
   169 
   170             setLookupsNoFire(lookups);
   171             
   172             if (getData() == null) {
   173                 // no affected results => exit
   174                 return;
   175             }
   176 
   177             arr = getData().references();
   178 
   179             Set<Lookup> removed = identityHashSet(current);
   180             removed.removeAll(newL); // current contains just those lookups that have disappeared
   181             newL.removeAll(current); // really new lookups
   182 
   183             if (removed.isEmpty() && newL.isEmpty()) {
   184                 // no need to notify changes
   185                 return;
   186             }
   187 
   188             for (Reference<R> ref : arr) {
   189                 R<?> r = ref.get();
   190                 if (r != null) {
   191                     r.lookupChange(newL, removed, old, lookups, toAdd, toRemove);
   192                 }
   193             }
   194         }
   195         
   196         // better to do this later than in synchronized block
   197         for (Map.Entry<Result, LookupListener> e : toRemove.entrySet()) {
   198             e.getKey().removeLookupListener(e.getValue());
   199         }
   200         for (Map.Entry<Result, LookupListener> e : toAdd.entrySet()) {
   201             e.getKey().addLookupListener(e.getValue());
   202         }
   203 
   204 
   205         // this cannot be done from the synchronized block
   206         ArrayList<Object> evAndListeners = new ArrayList<Object>();
   207         for (Reference<R> ref : arr) {
   208             R<?> r = ref.get();
   209             if (r != null) {
   210                 r.collectFires(evAndListeners);
   211             }
   212         }
   213         
   214         {
   215             Iterator it = evAndListeners.iterator();
   216             while (it.hasNext()) {
   217                 LookupEvent ev = (LookupEvent)it.next();
   218                 LookupListener l = (LookupListener)it.next();
   219                 l.resultChanged(ev);
   220             }
   221         }
   222     }
   223 
   224     /** Notifies subclasses that a query is about to be processed.
   225      * Subclasses can update its state before the actual processing
   226      * begins. It is allowed to call <code>setLookups</code> method
   227      * to change/update the set of objects the proxy delegates to.
   228      *
   229      * @param template the template of the query
   230      * @since 1.31
   231      */
   232     protected void beforeLookup(Template<?> template) {
   233     }
   234 
   235     public final <T> T lookup(Class<T> clazz) {
   236         beforeLookup(new Template<T>(clazz));
   237 
   238         Lookup[] tmpLkps;
   239         synchronized (ProxyLookup.this) {
   240             tmpLkps = this.getLookups(false);
   241         }
   242 
   243         for (int i = 0; i < tmpLkps.length; i++) {
   244             T o = tmpLkps[i].lookup(clazz);
   245 
   246             if (o != null) {
   247                 return o;
   248             }
   249         }
   250 
   251         return null;
   252     }
   253 
   254     @Override
   255     public final <T> Item<T> lookupItem(Template<T> template) {
   256         beforeLookup(template);
   257 
   258         Lookup[] tmpLkps; 
   259         synchronized (ProxyLookup.this) {
   260             tmpLkps = this.getLookups(false);
   261         }
   262 
   263         for (int i = 0; i < tmpLkps.length; i++) {
   264             Item<T> o = tmpLkps[i].lookupItem(template);
   265 
   266             if (o != null) {
   267                 return o;
   268             }
   269         }
   270 
   271         return null;
   272     }
   273 
   274     @SuppressWarnings("unchecked")
   275     private static <T> R<T> convertResult(R r) {
   276         return (R<T>)r;
   277     }
   278 
   279     public final <T> Result<T> lookup(Lookup.Template<T> template) {
   280         synchronized (ProxyLookup.this) {
   281             ImmutableInternalData[] res = { data };
   282             R<T> newR = ImmutableInternalData.findResult(this, res, template);
   283             setData(res[0]);
   284             return newR;
   285         }
   286     }
   287 
   288     /** Unregisters a template from the has map.
   289      */
   290     private final void unregisterTemplate(Template<?> template) {
   291         synchronized (ProxyLookup.this) {
   292             ImmutableInternalData id = getData();
   293             if (id == null) {
   294                 return;
   295             }
   296             setData(id.removeTemplate(this, template));
   297         }
   298     }
   299 
   300     private ImmutableInternalData getData() {
   301         return data;
   302     }
   303 
   304     private void setData(ImmutableInternalData data) {
   305         assert Thread.holdsLock(ProxyLookup.this);
   306         this.data = data;
   307     }
   308 
   309     /** Result of a lookup request. Allows access to single object
   310      * that was found (not too useful) and also to all objects found
   311      * (more useful).
   312      */
   313     private static final class R<T> extends WaitableResult<T> {
   314         /** list of listeners added */
   315         private javax.swing.event.EventListenerList listeners;
   316 
   317         /** collection of Objects */
   318         private Collection[] cache;
   319 
   320         /** weak listener & result */
   321         private final WeakResult<T> weakL;
   322         private final Lookup.Template<T> template;
   323 
   324         /** Constructor.
   325          */
   326         public R(ProxyLookup proxy, Lookup.Template<T> t) {
   327             this.weakL = new WeakResult<T>(proxy, this);
   328             this.template = t;
   329         }
   330 
   331         @SuppressWarnings("unchecked")
   332         private Result<T>[] newResults(int len) {
   333             return new Result[len];
   334         }
   335 
   336         /** initializes the results
   337          */
   338         private Result<T>[] initResults() {
   339             BIG_LOOP: for (;;) {
   340                 Lookup[] myLkps;
   341                 synchronized (weakL.getLock()) {
   342                     if (weakL.getResults() != null) {
   343                         return weakL.getResults();
   344                     }
   345                     myLkps = weakL.getLookups(false);
   346                 }
   347 
   348                 Result<T>[] arr = newResults(myLkps.length);
   349 
   350                 for (int i = 0; i < arr.length; i++) {
   351                     arr[i] = myLkps[i].lookup(template);
   352                 }
   353 
   354                 synchronized (weakL.getLock()) {
   355                     Lookup[] currentLkps = weakL.getLookups(false);
   356                     if (currentLkps.length != myLkps.length) {
   357                         continue BIG_LOOP;
   358                     }
   359                     for (int i = 0; i < currentLkps.length; i++) {
   360                         if (currentLkps[i] != myLkps[i]) {
   361                             continue BIG_LOOP;
   362                         }
   363                     }
   364                     
   365                     // some other thread might compute the result mean while. 
   366                     // if not finish the computation yourself
   367                     if (weakL.getResults() != null) {
   368                         return weakL.getResults();
   369                     }
   370 
   371                     for (int i = 0; i < arr.length; i++) {
   372                         arr[i].addLookupListener(weakL);
   373                     }
   374 
   375                     weakL.setResults(arr);
   376 
   377                     return arr;
   378                 }
   379             }
   380         }
   381 
   382         /** Called when there is a change in the list of proxied lookups.
   383          * @param added set of added lookups
   384          * @param remove set of removed lookups
   385          * @param current array of current lookups
   386          */
   387         protected void lookupChange(
   388             Set<Lookup> added, Set<Lookup> removed, Lookup[] old, Lookup[] current, 
   389             Map<Result,LookupListener> toAdd, Map<Result,LookupListener> toRemove
   390         ) {
   391             synchronized (weakL.getLock()) {
   392                 if (weakL.getResults() == null) {
   393                     // not computed yet, do not need to do anything
   394                     return;
   395                 }
   396 
   397                 // map (Lookup, Lookup.Result)
   398                 Map<Lookup,Result<T>> map = new IdentityHashMap<Lookup,Result<T>>(old.length * 2);
   399 
   400                 for (int i = 0; i < old.length; i++) {
   401                     if (removed.contains(old[i])) {
   402                         // removed lookup
   403                         toRemove.put(weakL.getResults()[i], weakL);
   404                     } else {
   405                         // remember the association
   406                         map.put(old[i], weakL.getResults()[i]);
   407                     }
   408                 }
   409 
   410                 Lookup.Result<T>[] arr = newResults(current.length);
   411 
   412                 for (int i = 0; i < current.length; i++) {
   413                     if (added.contains(current[i])) {
   414                         // new lookup
   415                         arr[i] = current[i].lookup(template);
   416                         toAdd.put(arr[i], weakL);
   417                     } else {
   418                         // old lookup
   419                         arr[i] = map.get(current[i]);
   420 
   421                         if (arr[i] == null) {
   422                             // assert
   423                             throw new IllegalStateException();
   424                         }
   425                     }
   426                 }
   427 
   428                 // remember the new results
   429                 weakL.setResults(arr);
   430             }
   431         }
   432 
   433         /** Just delegates.
   434          */
   435         public void addLookupListener(LookupListener l) {
   436             synchronized (weakL.getLock()) {
   437                 if (listeners == null) {
   438                     listeners = new EventListenerList();
   439                 }
   440             }
   441 
   442             listeners.add(LookupListener.class, l);
   443         }
   444 
   445         /** Just delegates.
   446          */
   447         public void removeLookupListener(LookupListener l) {
   448             if (listeners != null) {
   449                 listeners.remove(LookupListener.class, l);
   450             }
   451         }
   452 
   453         /** Access to all instances in the result.
   454          * @return collection of all instances
   455          */
   456         @SuppressWarnings("unchecked")
   457         public java.util.Collection<T> allInstances() {
   458             return computeResult(0);
   459         }
   460 
   461         /** Classes of all results. Set of the most concreate classes
   462          * that are registered in the system.
   463          * @return set of Class objects
   464          */
   465         @SuppressWarnings("unchecked")
   466         @Override
   467         public java.util.Set<Class<? extends T>> allClasses() {
   468             return (java.util.Set<Class<? extends T>>) computeResult(1);
   469         }
   470 
   471         /** All registered items. The collection of all pairs of
   472          * ii and their classes.
   473          * @return collection of Lookup.Item
   474          */
   475         @SuppressWarnings("unchecked")
   476         @Override
   477         public java.util.Collection<? extends Item<T>> allItems() {
   478             return computeResult(2);
   479         }
   480 
   481         /** Computes results from proxied lookups.
   482          * @param indexToCache 0 = allInstances, 1 = allClasses, 2 = allItems
   483          * @return the collection or set of the objects
   484          */
   485         private java.util.Collection computeResult(int indexToCache) {
   486             // results to use
   487             Lookup.Result<T>[] arr = myBeforeLookup();
   488 
   489             // if the call to beforeLookup resulted in deletion of caches
   490             synchronized (weakL.getLock()) {
   491                 if (getCache() != null) {
   492                     Collection result = getCache()[indexToCache];
   493                     if (result != null) {
   494                         return result;
   495                     }
   496                 }
   497             }
   498 
   499             // initialize the collection to hold result
   500             Collection<Object> compute;
   501             Collection<Object> ret;
   502 
   503             if (indexToCache == 1) {
   504                 HashSet<Object> s = new HashSet<Object>();
   505                 compute = s;
   506                 ret = Collections.unmodifiableSet(s);
   507             } else {
   508                 List<Object> l = new ArrayList<Object>(arr.length * 2);
   509                 compute = l;
   510                 ret = Collections.unmodifiableList(l);
   511             }
   512 
   513             // fill the collection
   514             for (int i = 0; i < arr.length; i++) {
   515                 switch (indexToCache) {
   516                 case 0:
   517                     compute.addAll(arr[i].allInstances());
   518                     break;
   519                 case 1:
   520                     compute.addAll(arr[i].allClasses());
   521                     break;
   522                 case 2:
   523                     compute.addAll(arr[i].allItems());
   524                     break;
   525                 default:
   526                     assert false : "Wrong index: " + indexToCache;
   527                 }
   528             }
   529             
   530             
   531 
   532             synchronized (weakL.getLock()) {
   533                 if (getCache() == null) {
   534                     // initialize the cache to indicate this result is in use
   535                     setCache(new Collection[3]);
   536                 }
   537                 
   538                 if (arr == weakL.getResults()) {
   539                     // updates the results, if the results have not been
   540                     // changed during the computation of allInstances
   541                     getCache()[indexToCache] = ret;
   542                 }
   543             }
   544 
   545             return ret;
   546         }
   547 
   548         /** When the result changes, fire the event.
   549          */
   550         public void resultChanged(LookupEvent ev) {
   551             collectFires(null);
   552         }
   553         
   554         protected void collectFires(Collection<Object> evAndListeners) {
   555             // clear cached instances
   556             Collection oldItems;
   557             Collection oldInstances;
   558             synchronized (weakL.getLock()) {
   559                 if (getCache() == null) {
   560                     // nobody queried the result yet
   561                     return;
   562                 }
   563                 oldInstances = getCache()[0];
   564                 oldItems = getCache()[2];
   565                 
   566 
   567                 if (listeners == null || listeners.getListenerCount() == 0) {
   568                     // clear the cache
   569                     setCache(new Collection[3]);
   570                     return;
   571                 }
   572                 
   573                 // ignore events if they arrive as a result of call to allItems
   574                 // or allInstances, bellow...
   575                 setCache(null);
   576             }
   577 
   578             boolean modified = true;
   579 
   580             if (oldItems != null) {
   581                 Collection newItems = allItems();
   582                 if (oldItems.equals(newItems)) {
   583                     modified = false;
   584                 }
   585             } else {
   586                 if (oldInstances != null) {
   587                     Collection newInstances = allInstances();
   588                     if (oldInstances.equals(newInstances)) {
   589                         modified = false;
   590                     }
   591                 } else {
   592                     synchronized (weakL.getLock()) {
   593                         if (getCache() == null) {
   594                             // we have to initialize the cache
   595                             // to show that the result has been initialized
   596                             setCache(new Collection[3]);
   597                         }
   598                     }
   599                 }
   600             }
   601             
   602             if (modified) {
   603                 LookupEvent ev = new LookupEvent(this);
   604                 AbstractLookup.notifyListeners(listeners.getListenerList(), ev, evAndListeners);
   605             }
   606         }
   607 
   608         /** Implementation of my before lookup.
   609          * @return results to work on.
   610          */
   611         private Lookup.Result<T>[] myBeforeLookup() {
   612             weakL.proxyBefore(template);
   613 
   614             Lookup.Result<T>[] arr = initResults();
   615 
   616             // invoke update on the results
   617             for (int i = 0; i < arr.length; i++) {
   618                 if (arr[i] instanceof WaitableResult) {
   619                     WaitableResult w = (WaitableResult) arr[i];
   620                     w.beforeLookup(template);
   621                 }
   622             }
   623 
   624             return arr;
   625         }
   626 
   627         /** Used by proxy results to synchronize before lookup.
   628          */
   629         protected void beforeLookup(Lookup.Template t) {
   630             if (t.getType() == template.getType()) {
   631                 myBeforeLookup();
   632             }
   633         }
   634 
   635         private Collection[] getCache() {
   636             return cache;
   637         }
   638 
   639         private void setCache(Collection[] cache) {
   640             assert Thread.holdsLock(weakL.getLock());
   641             this.cache = cache;
   642         }
   643     }
   644     private static final class WeakResult<T> extends WaitableResult<T> implements LookupListener, Runnable {
   645         /** all results */
   646         private Lookup.Result<T>[] results;
   647 
   648         private final Reference<R> result;
   649         
   650         private final Reference<ProxyLookup> proxy;
   651         
   652         public WeakResult(ProxyLookup proxy, R r) {
   653             this.result = new RefR(r, this);
   654             this.proxy = new WeakReference<ProxyLookup>(proxy);
   655         }
   656         
   657         protected void beforeLookup(Lookup.Template t) {
   658             R r = result.get();
   659             if (r != null) {
   660                 r.beforeLookup(t);
   661             } else {
   662                 removeListeners();
   663             }
   664         }
   665         
   666         final void unregisterTemplate() {
   667             ProxyLookup p = proxy.get();
   668             R r = result.get();
   669             Lookup.Template<T> template = r == null ? null : r.template;
   670             if (p != null && template != null) {
   671                 p.unregisterTemplate(template);
   672             }
   673         }
   674         
   675         final void proxyBefore(Template template) {
   676             ProxyLookup p = proxy.get();
   677             if (p != null) {
   678                 p.beforeLookup(template);
   679             }
   680         }
   681 
   682         final Object getLock() {
   683             ProxyLookup p = proxy.get();
   684             if (p != null) {
   685                 return p;
   686             } else {
   687                 // some fallback to lock on that will not change once the ProxyLookup is GCed
   688                 return EMPTY_ARR;
   689             }
   690         }
   691         
   692         final Lookup[] getLookups(boolean clone) {
   693             ProxyLookup p = proxy.get();
   694             if (p != null) {
   695                 return p.getLookups(clone);
   696             } else {
   697                 return EMPTY_ARR;
   698             }
   699         }
   700         
   701 
   702         final void removeListeners() {
   703             Lookup.Result<T>[] arr = this.getResults();
   704             if (arr == null) {
   705                 return;
   706             }
   707 
   708             for(int i = 0; i < arr.length; i++) {
   709                 arr[i].removeLookupListener(this);
   710             }
   711         }
   712 
   713         protected void collectFires(Collection<Object> evAndListeners) {
   714             R<?> r = result.get();
   715             if (r != null) {
   716                 r.collectFires(evAndListeners);
   717             } else {
   718                 removeListeners();
   719             }
   720         }
   721 
   722         public void addLookupListener(LookupListener l) {
   723             assert false;
   724         }
   725 
   726         public void removeLookupListener(LookupListener l) {
   727             assert false;
   728         }
   729 
   730         public Collection<T> allInstances() {
   731             assert false;
   732             return null;
   733         }
   734 
   735         public void resultChanged(LookupEvent ev) {
   736             R r = result.get();
   737             if (r != null) {
   738                 r.resultChanged(ev);
   739             } else {
   740                 removeListeners();
   741             }
   742         }
   743 
   744         @Override
   745         public Collection<? extends Item<T>> allItems() {
   746             assert false;
   747             return null;
   748         }
   749 
   750         @Override
   751         public Set<Class<? extends T>> allClasses() {
   752             assert false;
   753             return null;
   754         }
   755 
   756         public void run() {
   757             removeListeners();
   758         }
   759 
   760         private Lookup.Result<T>[] getResults() {
   761             return results;
   762         }
   763 
   764         private void setResults(Lookup.Result<T>[] results) {
   765             this.results = results;
   766         }
   767     } // end of WeakResult
   768     private static final class ImmutableInternalData extends Object {
   769         /** map of templates to currently active results */
   770         private final HashMap<Template<?>,Reference<R>> results;
   771 
   772         public ImmutableInternalData(HashMap<Template<?>, Reference<ProxyLookup.R>> results) {
   773             this.results = results;
   774         }
   775 
   776         final Collection<Reference<R>> references() {
   777             return results.values();
   778         }
   779         
   780         final <T> ImmutableInternalData removeTemplate(ProxyLookup proxy, Template<T> template) {
   781             if (results.containsKey(template)) {
   782                 HashMap<Template<?>,Reference<R>> c = new HashMap<Lookup.Template<?>, Reference<ProxyLookup.R>>(results);
   783                 Reference<R> ref = c.remove(template);
   784                 if (ref != null && ref.get() != null) {
   785                     // seems like there is a reference to a result for this template
   786                     // thta is still alive
   787                     return this;
   788                 }
   789                 return new ImmutableInternalData(c);
   790             } else {
   791                 return this;
   792             }
   793         }
   794 
   795 
   796         static <T> R<T> findResult(ProxyLookup proxy, ImmutableInternalData[] oldAndNew, Template<T> template) {
   797             assert Thread.holdsLock(proxy);
   798             
   799             if (oldAndNew[0] != null) {
   800                 Reference<R> ref = oldAndNew[0].results.get(template);
   801                 R r = (ref == null) ? null : ref.get();
   802 
   803                 if (r != null) {
   804                     return convertResult(r);
   805                 }
   806             }
   807             
   808             HashMap<Template<?>, Reference<R>> res;
   809             if (oldAndNew[0] == null) {
   810                 res = new HashMap<Template<?>, Reference<R>>();
   811             } else {
   812                 res = new HashMap<Template<?>, Reference<R>>(oldAndNew[0].results);
   813             }
   814             
   815             R<T> newR = new R<T>(proxy, template);
   816             res.put(template, new java.lang.ref.SoftReference<R>(newR));
   817             oldAndNew[0] = new ImmutableInternalData(res);
   818             return newR;
   819         }
   820     }
   821     
   822     private static final class RefR extends WeakReference<R> implements Runnable {
   823         private WeakResult weakL;
   824         public RefR(R r, WeakResult weakL) {
   825             super(r, Utilities.activeReferenceQueue());
   826             this.weakL = weakL;
   827         }
   828 
   829         public void run() {
   830             weakL.unregisterTemplate();
   831         }
   832     }
   833 }