KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > util > lookup > ProxyLookup


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.util.lookup;
21
22 import java.lang.ref.Reference JavaDoc;
23 import java.lang.ref.WeakReference JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Collections JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Set JavaDoc;
33 import javax.swing.event.EventListenerList JavaDoc;
34 import org.openide.util.Lookup;
35 import org.openide.util.LookupEvent;
36 import org.openide.util.LookupListener;
37
38 /** Implementation of lookup that can delegate to others.
39  *
40  * @author Jaroslav Tulach
41  * @since 1.9
42  */

43 public class ProxyLookup extends Lookup {
44     /** empty array of lookups for potential use */
45     private static final Lookup[] EMPTY_ARR = new Lookup[0];
46
47     /** lookups to delegate to (either Lookup or array of Lookups) */
48     private Object JavaDoc lookups;
49
50     /** map of templates to currently active results */
51     private HashMap JavaDoc<Template<?>,Reference JavaDoc<R>> results;
52
53     /** Create a proxy to some other lookups.
54      * @param lookups the initial delegates
55      */

56     public ProxyLookup(Lookup... lookups) {
57         this.setLookupsNoFire(lookups);
58     }
59
60     /**
61      * Create a lookup initially proxying to no others.
62      * Permits serializable subclasses.
63      * @since 3.27
64      */

65     protected ProxyLookup() {
66         this(EMPTY_ARR);
67     }
68
69     public String JavaDoc toString() {
70         return "ProxyLookup(class=" + getClass() + ")->" + Arrays.asList(getLookups(false)); // NOI18N
71
}
72
73     /** Getter for the delegates.
74     * @return the array of lookups we delegate to
75     * @since 1.19
76     */

77     protected final Lookup[] getLookups() {
78         return getLookups(true);
79     }
80
81     /** getter for the delegates, that can but need not do a clone.
82      * @param clone true if clone of internal array is requested
83      */

84     private final Lookup[] getLookups(boolean clone) {
85         Object JavaDoc l = this.lookups;
86         if (l instanceof Lookup) {
87             return new Lookup[] { (Lookup)l };
88         } else {
89             Lookup[] arr = (Lookup[])l;
90             if (clone) {
91                 arr = arr.clone();
92             }
93             return arr;
94         }
95     }
96     
97     /** Called from setLookups and constructor.
98      * @param lookups the lookups to setup
99      */

100     private void setLookupsNoFire(Lookup[] lookups) {
101         if (lookups.length == 1) {
102             this.lookups = lookups[0];
103             assert this.lookups != null : "Cannot assign null delegate";
104         } else {
105             if (lookups.length == 0) {
106                 this.lookups = EMPTY_ARR;
107             } else {
108                 this.lookups = lookups.clone();
109             }
110         }
111     }
112
113     /**
114      * Changes the delegates.
115      *
116      * @param lookups the new lookups to delegate to
117      * @since 1.19 protected
118      */

119     protected final void setLookups(Lookup... lookups) {
120         Collection JavaDoc<Reference JavaDoc<R>> arr;
121         HashSet JavaDoc<Lookup> newL;
122         HashSet JavaDoc<Lookup> current;
123         Lookup[] old;
124
125         synchronized (this) {
126             old = getLookups(false);
127             current = new HashSet JavaDoc<Lookup>(Arrays.asList(old));
128             newL = new HashSet JavaDoc<Lookup>(Arrays.asList(lookups));
129
130             setLookupsNoFire(lookups);
131             
132             if ((results == null) || results.isEmpty()) {
133                 // no affected results => exit
134
return;
135             }
136
137             arr = new ArrayList JavaDoc<Reference JavaDoc<R>>(results.values());
138
139             HashSet JavaDoc<Lookup> removed = new HashSet JavaDoc<Lookup>(current);
140             removed.removeAll(newL); // current contains just those lookups that have disappeared
141
newL.removeAll(current); // really new lookups
142

143             if (removed.isEmpty() && newL.isEmpty()) {
144                 // no need to notify changes
145
return;
146             }
147
148             for (Reference JavaDoc<R> ref : arr) {
149                 R r = ref.get();
150                 if (r != null) {
151                     r.lookupChange(newL, removed, old, lookups);
152                 }
153             }
154         }
155
156         // this cannot be done from the synchronized block
157
ArrayList JavaDoc<Object JavaDoc> evAndListeners = new ArrayList JavaDoc<Object JavaDoc>();
158         for (Reference JavaDoc<R> ref : arr) {
159             R<?> r = ref.get();
160             if (r != null) {
161                 r.collectFires(evAndListeners);
162             }
163         }
164         
165         {
166             Iterator JavaDoc it = evAndListeners.iterator();
167             while (it.hasNext()) {
168                 LookupEvent ev = (LookupEvent)it.next();
169                 LookupListener l = (LookupListener)it.next();
170                 l.resultChanged(ev);
171             }
172         }
173     }
174
175     /** Notifies subclasses that a query is about to be processed.
176      * Subclasses can update its state before the actual processing
177      * begins. It is allowed to call <code>setLookups</code> method
178      * to change/update the set of objects the proxy delegates to.
179      *
180      * @param template the template of the query
181      * @since 1.31
182      */

183     protected void beforeLookup(Template<?> template) {
184     }
185
186     public final <T> T lookup(Class JavaDoc<T> clazz) {
187         beforeLookup(new Template<T>(clazz));
188
189         Lookup[] lookups = this.getLookups(false);
190
191         for (int i = 0; i < lookups.length; i++) {
192             T o = lookups[i].lookup(clazz);
193
194             if (o != null) {
195                 return o;
196             }
197         }
198
199         return null;
200     }
201
202     public final <T> Item<T> lookupItem(Template<T> template) {
203         beforeLookup(template);
204
205         Lookup[] lookups = this.getLookups(false);
206
207         for (int i = 0; i < lookups.length; i++) {
208             Item<T> o = lookups[i].lookupItem(template);
209
210             if (o != null) {
211                 return o;
212             }
213         }
214
215         return null;
216     }
217
218     @SuppressWarnings JavaDoc("unchecked")
219     private static <T> R<T> convertResult(R r) {
220         return (R<T>)r;
221     }
222
223     public final synchronized <T> Result<T> lookup(Lookup.Template<T> template) {
224         if (results != null) {
225             Reference JavaDoc<R> ref = results.get(template);
226             R r = (ref == null) ? null : ref.get();
227
228             if (r != null) {
229                 return convertResult(r);
230             }
231         } else {
232             results = new HashMap JavaDoc<Template<?>,Reference JavaDoc<R>>();
233         }
234
235         R<T> newR = new R<T>(template);
236         results.put(template, new java.lang.ref.SoftReference JavaDoc<R>(newR));
237
238         return newR;
239     }
240
241     /** Unregisters a template from the has map.
242      */

243     private final synchronized void unregisterTemplate(Template<?> template) {
244         if (results == null) {
245             return;
246         }
247
248         Reference JavaDoc<R> ref = results.remove(template);
249
250         if ((ref != null) && (ref.get() != null)) {
251             // seems like there is a reference to a result for this template
252
// thta is still alive
253
results.put(template, ref);
254         }
255     }
256
257     /** Result of a lookup request. Allows access to single object
258      * that was found (not too useful) and also to all objects found
259      * (more useful).
260      */

261     private final class R<T> extends WaitableResult<T> {
262         /** list of listeners added */
263         private javax.swing.event.EventListenerList JavaDoc listeners;
264
265         /** template for this result */
266         private Lookup.Template<T> template;
267
268         /** collection of Objects */
269         private Collection JavaDoc[] cache;
270
271         /** weak listener & result */
272         private WeakResult<T> weakL;
273
274         /** Constructor.
275          */

276         public R(Lookup.Template<T> t) {
277             template = t;
278             weakL = new WeakResult<T>(this);
279         }
280
281         /** When garbage collected, remove the template from the has map.
282          */

283         protected void finalize() {
284             unregisterTemplate(template);
285         }
286
287         @SuppressWarnings JavaDoc("unchecked")
288         private Result<T>[] newResults(int len) {
289             return new Result[len];
290         }
291
292         /** initializes the results
293          */

294         private Result<T>[] initResults() {
295             synchronized (this) {
296                 if (weakL.results != null) {
297                     return weakL.results;
298                 }
299             }
300
301             Lookup[] myLkps = getLookups(false);
302             Result<T>[] arr = newResults(myLkps.length);
303
304             for (int i = 0; i < arr.length; i++) {
305                 arr[i] = myLkps[i].lookup(template);
306             }
307
308             synchronized (this) {
309                 // some other thread might compute the result mean while.
310
// if not finish the computation yourself
311
if (weakL.results != null) {
312                     return weakL.results;
313                 }
314
315                 for (int i = 0; i < arr.length; i++) {
316                     arr[i].addLookupListener(weakL);
317                 }
318
319                 weakL.results = arr;
320
321                 return arr;
322             }
323         }
324
325         /** Called when there is a change in the list of proxied lookups.
326          * @param added set of added lookups
327          * @param remove set of removed lookups
328          * @param current array of current lookups
329          */

330         protected void lookupChange(Set JavaDoc added, Set JavaDoc removed, Lookup[] old, Lookup[] current) {
331             synchronized (this) {
332                 if (weakL.results == null) {
333                     // not computed yet, do not need to do anything
334
return;
335                 }
336
337                 // map (Lookup, Lookup.Result)
338
HashMap JavaDoc<Lookup,Result<T>> map = new HashMap JavaDoc<Lookup,Result<T>>(old.length * 2);
339
340                 for (int i = 0; i < old.length; i++) {
341                     if (removed.contains(old[i])) {
342                         // removed lookup
343
weakL.results[i].removeLookupListener(weakL);
344                     } else {
345                         // remember the association
346
map.put(old[i], weakL.results[i]);
347                     }
348                 }
349
350                 Lookup.Result<T>[] arr = newResults(current.length);
351
352                 for (int i = 0; i < current.length; i++) {
353                     if (added.contains(current[i])) {
354                         // new lookup
355
arr[i] = current[i].lookup(template);
356                         arr[i].addLookupListener(weakL);
357                     } else {
358                         // old lookup
359
arr[i] = map.get(current[i]);
360
361                         if (arr[i] == null) {
362                             // assert
363
throw new IllegalStateException JavaDoc();
364                         }
365                     }
366                 }
367
368                 // remember the new results
369
weakL.results = arr;
370             }
371         }
372
373         /** Just delegates.
374          */

375         public void addLookupListener(LookupListener l) {
376             if (listeners == null) {
377                 synchronized (this) {
378                     if (listeners == null) {
379                         listeners = new EventListenerList JavaDoc();
380                     }
381                 }
382             }
383
384             listeners.add(LookupListener.class, l);
385         }
386
387         /** Just delegates.
388          */

389         public void removeLookupListener(LookupListener l) {
390             if (listeners != null) {
391                 listeners.remove(LookupListener.class, l);
392             }
393         }
394
395         /** Access to all instances in the result.
396          * @return collection of all instances
397          */

398         @SuppressWarnings JavaDoc("unchecked")
399         public java.util.Collection JavaDoc<T> allInstances() {
400             return computeResult(0);
401         }
402
403         /** Classes of all results. Set of the most concreate classes
404          * that are registered in the system.
405          * @return set of Class objects
406          */

407         @SuppressWarnings JavaDoc("unchecked")
408         public java.util.Set JavaDoc<Class JavaDoc<? extends T>> allClasses() {
409             return (java.util.Set JavaDoc<Class JavaDoc<? extends T>>) computeResult(1);
410         }
411
412         /** All registered items. The collection of all pairs of
413          * ii and their classes.
414          * @return collection of Lookup.Item
415          */

416         @SuppressWarnings JavaDoc("unchecked")
417         public java.util.Collection JavaDoc<? extends Item<T>> allItems() {
418             return computeResult(2);
419         }
420
421         /** Computes results from proxied lookups.
422          * @param indexToCache 0 = allInstances, 1 = allClasses, 2 = allItems
423          * @return the collection or set of the objects
424          */

425         private java.util.Collection JavaDoc computeResult(int indexToCache) {
426             // results to use
427
Lookup.Result<T>[] arr = myBeforeLookup();
428
429             // if the call to beforeLookup resulted in deletion of caches
430
synchronized (this) {
431                 if (cache != null) {
432                     Collection JavaDoc result = cache[indexToCache];
433                     if (result != null) {
434                         return result;
435                     }
436                 }
437             }
438
439             // initialize the collection to hold result
440
Collection JavaDoc<Object JavaDoc> compute;
441             Collection JavaDoc<Object JavaDoc> ret;
442
443             if (indexToCache == 1) {
444                 HashSet JavaDoc<Object JavaDoc> s = new HashSet JavaDoc<Object JavaDoc>();
445                 compute = s;
446                 ret = Collections.unmodifiableSet(s);
447             } else {
448                 List JavaDoc<Object JavaDoc> l = new ArrayList JavaDoc<Object JavaDoc>(arr.length * 2);
449                 compute = l;
450                 ret = Collections.unmodifiableList(l);
451             }
452
453             // fill the collection
454
for (int i = 0; i < arr.length; i++) {
455                 switch (indexToCache) {
456                 case 0:
457                     compute.addAll(arr[i].allInstances());
458                     break;
459                 case 1:
460                     compute.addAll(arr[i].allClasses());
461                     break;
462                 case 2:
463                     compute.addAll(arr[i].allItems());
464                     break;
465                 default:
466                     assert false : "Wrong index: " + indexToCache;
467                 }
468             }
469             
470             
471
472             synchronized (this) {
473                 if (cache == null) {
474                     // initialize the cache to indicate this result is in use
475
cache = new Collection JavaDoc[3];
476                 }
477                 
478                 if (arr == weakL.results) {
479                     // updates the results, if the results have not been
480
// changed during the computation of allInstances
481
cache[indexToCache] = ret;
482                 }
483             }
484
485             return ret;
486         }
487
488         /** When the result changes, fire the event.
489          */

490         public void resultChanged(LookupEvent ev) {
491             collectFires(null);
492         }
493         
494         protected void collectFires(Collection JavaDoc<Object JavaDoc> evAndListeners) {
495             // clear cached instances
496
Collection JavaDoc oldItems;
497             Collection JavaDoc oldInstances;
498             synchronized (this) {
499                 if (cache == null) {
500                     // nobody queried the result yet
501
return;
502                 }
503                 oldInstances = cache[0];
504                 oldItems = cache[2];
505                 
506
507                 if (listeners == null || listeners.getListenerCount() == 0) {
508                     // clear the cache
509
cache = new Collection JavaDoc[3];
510                     return;
511                 }
512                 
513                 // ignore events if they arrive as a result of call to allItems
514
// or allInstances, bellow...
515
cache = null;
516             }
517
518             boolean modified = true;
519
520             if (oldItems != null) {
521                 Collection JavaDoc newItems = allItems();
522                 if (oldItems.equals(newItems)) {
523                     modified = false;
524                 }
525             } else {
526                 if (oldInstances != null) {
527                     Collection JavaDoc newInstances = allInstances();
528                     if (oldInstances.equals(newInstances)) {
529                         modified = false;
530                     }
531                 } else {
532                     synchronized (this) {
533                         if (cache == null) {
534                             // we have to initialize the cache
535
// to show that the result has been initialized
536
cache = new Collection JavaDoc[3];
537                         }
538                     }
539                 }
540             }
541             
542             assert cache != null;
543
544             if (modified) {
545                 LookupEvent ev = new LookupEvent(this);
546                 AbstractLookup.notifyListeners(listeners.getListenerList(), ev, evAndListeners);
547             }
548         }
549
550         /** Implementation of my before lookup.
551          * @return results to work on.
552          */

553         private Lookup.Result<T>[] myBeforeLookup() {
554             ProxyLookup.this.beforeLookup(template);
555
556             Lookup.Result<T>[] arr = initResults();
557
558             // invoke update on the results
559
for (int i = 0; i < arr.length; i++) {
560                 if (arr[i] instanceof WaitableResult) {
561                     WaitableResult w = (WaitableResult) arr[i];
562                     w.beforeLookup(template);
563                 }
564             }
565
566             return arr;
567         }
568
569         /** Used by proxy results to synchronize before lookup.
570          */

571         protected void beforeLookup(Lookup.Template t) {
572             if (t.getType() == template.getType()) {
573                 myBeforeLookup();
574             }
575         }
576     }
577     private static final class WeakResult<T> extends WaitableResult<T> implements LookupListener {
578         /** all results */
579         private Lookup.Result<T>[] results;
580
581         private Reference JavaDoc<R> result;
582         
583         public WeakResult(R r) {
584             this.result = new WeakReference JavaDoc<R>(r);
585         }
586         
587         protected void beforeLookup(Lookup.Template t) {
588             R r = result.get();
589             if (r != null) {
590                 r.beforeLookup(t);
591             } else {
592                 removeListeners();
593             }
594         }
595
596         private void removeListeners() {
597             Lookup.Result<T>[] arr = this.results;
598             if (arr == null) {
599                 return;
600             }
601
602             for(int i = 0; i < arr.length; i++) {
603                 arr[i].removeLookupListener(this);
604             }
605         }
606
607         protected void collectFires(Collection JavaDoc<Object JavaDoc> evAndListeners) {
608             R<?> r = result.get();
609             if (r != null) {
610                 r.collectFires(evAndListeners);
611             } else {
612                 removeListeners();
613             }
614         }
615
616         public void addLookupListener(LookupListener l) {
617             assert false;
618         }
619
620         public void removeLookupListener(LookupListener l) {
621             assert false;
622         }
623
624         public Collection JavaDoc<T> allInstances() {
625             assert false;
626             return null;
627         }
628
629         public void resultChanged(LookupEvent ev) {
630             R r = result.get();
631             if (r != null) {
632                 r.resultChanged(ev);
633             } else {
634                 removeListeners();
635             }
636         }
637
638         public Collection JavaDoc<? extends Item<T>> allItems() {
639             assert false;
640             return null;
641         }
642
643         public Set JavaDoc<Class JavaDoc<? extends T>> allClasses() {
644             assert false;
645             return null;
646         }
647     } // end of WeakResult
648
}
649
Popular Tags