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