KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > nodes > CookieSet


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 package org.openide.nodes;
20
21 import java.lang.ref.Reference JavaDoc;
22 import java.lang.ref.WeakReference JavaDoc;
23
24 import java.util.*;
25
26 import javax.swing.event.ChangeEvent JavaDoc;
27 import javax.swing.event.ChangeListener JavaDoc;
28 import javax.swing.event.EventListenerList JavaDoc;
29 import org.openide.util.Lookup;
30 import org.openide.util.lookup.AbstractLookup;
31 import org.openide.util.lookup.InstanceContent;
32
33
34 /** Support class for storing cookies and
35 * retriving them by representation class.
36 * Provides simple notifications about changes
37 * in cookies.
38 *
39 * @author Jaroslav Tulach
40 */

41 public final class CookieSet extends Object JavaDoc implements Lookup.Provider {
42     /** variable to allow effecient communication with NodeLookup, Node.Cookie or Class or Set */
43     private static ThreadLocal JavaDoc<Object JavaDoc> QUERY_MODE = new ThreadLocal JavaDoc<Object JavaDoc>();
44
45     /** list of cookies (Class, Node.Cookie) */
46     private HashMap<Class JavaDoc, R> map = new HashMap<Class JavaDoc,R>(31);
47
48     /** set of listeners */
49     private EventListenerList JavaDoc listeners = new EventListenerList JavaDoc();
50     
51     /** potential instance content */
52     private final CookieSetLkp ic;
53     /** lookup to use return from the cookie set, if initialized */
54     private Lookup lookup;
55
56     /** Default constructor. */
57     public CookieSet() {
58         this(null, null);
59     }
60     
61     private CookieSet(CookieSetLkp ic, Lookup lookup) {
62         this.ic = ic;
63         this.lookup = lookup;
64     }
65     
66     /** Factory method to create new, general purpose cookie set.
67      * The <q>general purpose</q> means that it is possible to store
68      * any object, into the cookie set and then obtain it using {@link #getLookup}
69      * and queries on the returned {@link Lookup}. The before object can
70      * be passed in if one wants to do a lazy initialization of the {@link CookieSet}
71      * content.
72      *
73      * @param before the interface to support lazy initialization
74      * @return new cookie set that can contain not only {@link Node.Cookie} but also
75      * any plain old java object
76      * @see #assign
77      * @since 7.0
78      */

79     public static CookieSet createGeneric(Before before) {
80         CookieSetLkp al = new CookieSetLkp(before);
81         return new CookieSet(al, al);
82     }
83
84     /** The lookup associated with this cookie set. Keeps track of
85      * the same things that are in the cookie set, but presents them
86      * as being inside the lookup.
87      *
88      * @return the lookup representing this cookie set
89      * @since 7.0
90      */

91     public Lookup getLookup() {
92         synchronized (QUERY_MODE) {
93             if (lookup == null) {
94                 AbstractNode an = new AbstractNode(this);
95                 lookup = an.getLookup();
96             }
97         }
98         return lookup;
99     }
100
101     
102     /** Add a new cookie to the set. If a cookie of the same
103     * <em>actual</em> (not representation!) class is already there,
104     * it is replaced.
105     * <p>Cookies inserted earlier are given preference during lookup,
106     * in case a supplied representation class matches more than one cookie
107     * in the set.
108     *
109     * @param cookie cookie to add
110     */

111     public void add(Node.Cookie cookie) {
112         addImpl((Object JavaDoc)cookie);
113         fireChangeEvent();
114     }
115     
116     private void addImpl(Object JavaDoc cookie) {
117         synchronized (this) {
118             registerCookie(cookie.getClass(), cookie);
119         }
120         if (ic != null) {
121             ic.add(cookie);
122         }
123     }
124
125     /** Remove a cookie from the set.
126     * @param cookie the cookie to remove
127     */

128     public void remove(Node.Cookie cookie) {
129         removeImpl((Object JavaDoc)cookie);
130         fireChangeEvent();
131     }
132     
133     void removeImpl(Object JavaDoc cookie) {
134         synchronized (this) {
135             unregisterCookie(cookie.getClass(), cookie);
136         }
137         if (ic != null) {
138             ic.remove(cookie);
139         }
140     }
141     
142     /** Associates a given set of instances with
143      */

144     
145
146     /** Get a cookie.
147     *
148     * @param clazz the representation class
149     * @return a cookie assignable to the representation class, or <code>null</code> if there is none
150     */

151     public <T extends Node.Cookie> T getCookie(Class JavaDoc<T> clazz) {
152         if (ic != null) {
153             ic.beforeLookupImpl(clazz);
154         }
155         
156         Node.Cookie ret = null;
157         Object JavaDoc queryMode = QUERY_MODE.get();
158
159         synchronized (this) {
160             R r = findR(clazz);
161
162             if (r == null) {
163                 if (queryMode == null || ic == null) {
164                     return null;
165                 }
166             } else {
167                 ret = r.cookie();
168
169                 if (queryMode instanceof Set) {
170                     @SuppressWarnings JavaDoc("unchecked")
171                     Set<Class JavaDoc> keys = (Set<Class JavaDoc>)queryMode;
172                     keys.addAll(map.keySet());
173                 }
174             }
175         }
176
177         if (ret instanceof CookieEntry) {
178             if (clazz == queryMode) {
179                 // we expected to be asked for this class
180
// set cookie entry as a result
181
QUERY_MODE.set(ret);
182                 ret = null;
183             } else {
184                 // unwrap the cookie
185
ret = ((CookieEntry) ret).getCookie(true);
186             }
187         } else if (ret == null) {
188             if (ic != null &&
189                 (!Node.Cookie.class.isAssignableFrom(clazz) || clazz == Node.Cookie.class)
190             ) {
191                 enhancedQueryMode(lookup, clazz);
192                 ret = null;
193             }
194         }
195
196         return clazz.cast(ret);
197     }
198     
199     static void enhancedQueryMode(Lookup lookup, Class JavaDoc<?> clazz) {
200         Object JavaDoc type = QUERY_MODE.get();
201         if (type != clazz) {
202             return;
203         }
204         Collection<? extends Lookup.Item<?>> items = lookup.lookupResult(clazz).allItems();
205         if (items.size() == 0) {
206             return;
207         }
208         AbstractLookup.Pair[] arr = new AbstractLookup.Pair[items.size()];
209         Iterator<? extends Lookup.Item> it = items.iterator();
210         for (int i = 0; i < arr.length; i++) {
211             arr[i] = new PairWrap(it.next());
212         }
213         QUERY_MODE.set(arr);
214     }
215
216     /** Add a listener to changes in the cookie set.
217     * @param l the listener to add
218     */

219     public void addChangeListener(ChangeListener JavaDoc l) {
220         listeners.add(ChangeListener JavaDoc.class, l);
221     }
222
223     /** Remove a listener to changes in the cookie set.
224     * @param l the listener to remove
225     */

226     public void removeChangeListener(ChangeListener JavaDoc l) {
227         listeners.remove(ChangeListener JavaDoc.class, l);
228     }
229     
230     
231     /** Node lookup starts its non-important query.
232      */

233     static Object JavaDoc entryQueryMode(Class JavaDoc c) {
234         Object JavaDoc prev = QUERY_MODE.get();
235         QUERY_MODE.set(c);
236
237         return prev;
238     }
239
240     /** Allows query for all know classes registered in this cookie.
241      */

242     static Object JavaDoc entryAllClassesMode() {
243         Object JavaDoc prev = QUERY_MODE.get();
244         QUERY_MODE.set(new HashSet());
245
246         return prev;
247     }
248
249     /** Exits query mode.
250      */

251     static Collection<AbstractLookup.Pair> exitQueryMode(Object JavaDoc prev) {
252         Object JavaDoc cookie = QUERY_MODE.get();
253         QUERY_MODE.set(prev);
254
255         if (cookie instanceof CookieSet.CookieEntry) {
256             return Collections.singleton((AbstractLookup.Pair)new CookieEntryPair((CookieSet.CookieEntry) cookie));
257         } else if (cookie instanceof AbstractLookup.Pair[]) {
258             return Arrays.asList((AbstractLookup.Pair[])cookie);
259         } else {
260             return null;
261         }
262     }
263
264     /** Returns list of all classes. */
265     static Set exitAllClassesMode(Object JavaDoc prev) {
266         Object JavaDoc cookie = QUERY_MODE.get();
267         QUERY_MODE.set(prev);
268
269         if (cookie instanceof HashSet) {
270             return (Set) cookie;
271         }
272
273         return null;
274     }
275
276     /** Fires change event
277     */

278     final void fireChangeEvent() {
279         Object JavaDoc[] arr = listeners.getListenerList();
280
281         if (arr.length > 0) {
282             ChangeEvent JavaDoc ev = null;
283
284             // Process the listeners last to first, notifying
285
// those that are interested in this event
286
for (int i = arr.length - 2; i >= 0; i -= 2) {
287                 if (arr[i] == ChangeListener JavaDoc.class) {
288                     if (ev == null) {
289                         ev = new ChangeEvent JavaDoc(this);
290                     }
291
292                     ((ChangeListener JavaDoc) arr[i + 1]).stateChanged(ev);
293                 }
294             }
295         }
296     }
297
298     /** Attaches cookie to given class and all its superclasses and
299     * superinterfaces.
300     *
301     * @param c class or null
302     * @param cookie cookie to attach
303     */

304     private void registerCookie(Class JavaDoc<?> c, Object JavaDoc cookie) {
305         if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) {
306             return;
307         }
308         Class JavaDoc<? extends Node.Cookie> nc = c.asSubclass(Node.Cookie.class);
309
310         R r = findR(nc);
311
312         if (r == null) {
313             r = new R();
314             map.put(c, r);
315         }
316
317         r.add((Node.Cookie)cookie);
318
319         registerCookie(c.getSuperclass(), cookie);
320
321         Class JavaDoc[] inter = c.getInterfaces();
322
323         for (int i = 0; i < inter.length; i++) {
324             registerCookie(inter[i], cookie);
325         }
326     }
327
328     /** Removes cookie from the class and all its superclasses and
329     * superinterfaces.
330     *
331     * @param c class or null
332     * @param cookie cookie to attach
333     */

334     private void unregisterCookie(Class JavaDoc<?> c, Object JavaDoc cookie) {
335         if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) {
336             return;
337         }
338         Class JavaDoc<? extends Node.Cookie> nc = c.asSubclass(Node.Cookie.class);
339
340
341         // if different cookie is attached to class c stop removing
342
R r = findR(nc);
343
344         if (r != null) {
345             // remove the cookie
346
r.remove((Node.Cookie)cookie);
347         }
348
349         unregisterCookie(c.getSuperclass(), cookie);
350
351         Class JavaDoc[] inter = c.getInterfaces();
352
353         for (int i = 0; i < inter.length; i++) {
354             unregisterCookie(inter[i], cookie);
355         }
356     }
357
358     /** Registers a Factory for given cookie class */
359     public void add(Class JavaDoc<? extends Node.Cookie> cookieClass, Factory factory) {
360         if (factory == null) {
361             throw new IllegalArgumentException JavaDoc();
362         }
363
364         synchronized (this) {
365             registerCookie(cookieClass, new CookieEntry(factory, cookieClass));
366         }
367         if (ic != null) {
368             ic.add(new FactAndClass(cookieClass, factory), C.INSTANCE);
369         }
370         fireChangeEvent();
371     }
372
373     /** Registers a Factory for given cookie classes */
374     public void add(Class JavaDoc<? extends Node.Cookie>[] cookieClass, Factory factory) {
375         if (factory == null) {
376             throw new IllegalArgumentException JavaDoc();
377         }
378
379         synchronized (this) {
380             for (int i = 0; i < cookieClass.length; i++) {
381                 registerCookie(cookieClass[i], new CookieEntry(factory, cookieClass[i]));
382             }
383         }
384
385         if (ic != null) {
386             for (Class JavaDoc<? extends Node.Cookie> c : cookieClass) {
387                 ic.add(new FactAndClass(c, factory), C.INSTANCE);
388             }
389         }
390         fireChangeEvent();
391     }
392
393     /**
394      * Unregisters a Factory for given cookie class
395      * @since 2.6
396      */

397     public void remove(Class JavaDoc<? extends Node.Cookie> cookieClass, Factory factory) {
398         if (factory == null) {
399             throw new IllegalArgumentException JavaDoc();
400         }
401
402         synchronized (this) {
403             R r = findR(cookieClass);
404
405             if (r != null) {
406                 Node.Cookie c = r.cookie();
407
408                 if (c instanceof CookieEntry) {
409                     CookieEntry ce = (CookieEntry) c;
410
411                     if (ce.factory == factory) {
412                         unregisterCookie(cookieClass, c);
413                     }
414                 }
415             }
416         }
417         if (ic != null) {
418             ic.remove(new FactAndClass(cookieClass, factory), C.INSTANCE);
419         }
420
421         fireChangeEvent();
422     }
423
424     /**
425      * Unregisters a Factory for given cookie classes
426      * @since 2.6
427      */

428     public void remove(Class JavaDoc<? extends Node.Cookie>[] cookieClass, Factory factory) {
429         if (factory == null) {
430             throw new IllegalArgumentException JavaDoc();
431         }
432
433         synchronized (this) {
434             for (int i = 0; i < cookieClass.length; i++) {
435                 R r = findR(cookieClass[i]);
436
437                 if (r != null) {
438                     Node.Cookie c = r.cookie();
439
440                     if (c instanceof CookieEntry) {
441                         CookieEntry ce = (CookieEntry) c;
442
443                         if (ce.factory == factory) {
444                             unregisterCookie(cookieClass[i], c);
445                         }
446                     }
447                 }
448             }
449         }
450         
451         if (ic != null) {
452             for (Class JavaDoc<? extends Node.Cookie> c : cookieClass) {
453                 ic.remove(new FactAndClass(c, factory), C.INSTANCE);
454             }
455         }
456
457         fireChangeEvent();
458     }
459     
460     /** Removes all instances of clazz from the set and replaces them
461      * with newly provided instance(s).
462      *
463      * @param clazz the root clazz for cookies to remove
464      * @param instances the one or more instances to put into the lookup
465      *
466      * @since 7.0
467      */

468     public <T> void assign(Class JavaDoc<? extends T> clazz, T... instances) {
469         if (Node.Cookie.class.isAssignableFrom(clazz)) {
470             Class JavaDoc<? extends Node.Cookie> cookieClazz = clazz.asSubclass(Node.Cookie.class);
471             for(;;) {
472                 Node.Cookie cookie = getCookie(cookieClazz);
473                 if (cookie != null) {
474                     removeImpl(cookie);
475                 } else {
476                     break;
477                 }
478             }
479             for (T t : instances) {
480                 addImpl(t);
481             }
482         
483             fireChangeEvent();
484         } else if (ic != null) {
485             synchronized (this) {
486                 for (T t : instances) {
487                     registerCookie(t.getClass(), t);
488                 }
489             }
490             ic.replaceInstances(clazz, instances, this);
491         }
492     }
493     
494     /** Assignes a trigger that gets called everytime given class is about
495      * to be queried. Can be used only for cookie set created with
496      * {@link CookieSet#create(true)} and for classes that are not
497      * subclasses of Node.Cookie.
498      *
499      * @param clazz the trigger class (not subclass of Node.Cookie)
500      * @param run runnable to run when the task is queried
501      *
502     public void beforeLookup(Class<?> clazz, Runnable run) {
503         if (Node.Cookie.class.isAssignableFrom(clazz) || clazz == Object.class) {
504             throw new IllegalArgumentException("Too generic class: " + clazz); // NOI18N
505         }
506         if (ic == null) {
507             throw new IllegalStateException("Can be used only on CookieSet.create(true)"); // NOI18N
508         }
509         ic.registerBeforeLookup(clazz, run);
510     }
511     */

512         
513     /** Finds a result in a map.
514      */

515     private R findR(Class JavaDoc<? extends Node.Cookie> c) {
516         return map.get(c);
517     }
518
519     /** Finds base class for a cookie.
520      * @param c cookie
521      * @return base class
522      */

523     private static Class JavaDoc<? extends Node.Cookie> baseForCookie(Node.Cookie c) {
524         if (c instanceof CookieEntry) {
525             return ((CookieEntry) c).klass;
526         }
527
528         return c.getClass();
529     }
530
531     /** Factory for creating cookies of given Class */
532     public interface Factory {
533         /** Creates a Node.Cookie of given class. The method
534          * may be called more than once.
535          */

536         <T extends Node.Cookie> T createCookie(Class JavaDoc<T> klass);
537     }
538     
539     /** Allows to update content of the cookie set just before
540      * a query for a given class is made.
541      */

542     public interface Before {
543         public void beforeLookup(Class JavaDoc<?> clazz);
544     }
545
546     /** Entry for one Cookie */
547     private static class CookieEntry implements Node.Cookie {
548         /** Factory for the cookie */
549         final Factory factory;
550
551         /** Class of the cookie */
552         private final Class JavaDoc<? extends Node.Cookie> klass;
553
554         private Reference JavaDoc<Node.Cookie> cookie;
555
556         /** Constructs new FactoryEntry */
557         public CookieEntry(Factory factory, Class JavaDoc<? extends Node.Cookie> klass) {
558             this.factory = factory;
559             this.klass = klass;
560         }
561
562         /** Getter for the cookie.
563          * Synchronized because we don't want to run factory.createCookie
564          * simultaneously from two threads.
565          */

566         public synchronized Node.Cookie getCookie(boolean create) {
567             Node.Cookie ret;
568
569             if (create) {
570                 if ((cookie == null) || ((ret = cookie.get()) == null)) {
571                     ret = factory.createCookie(klass);
572
573                     if (ret == null) {
574                         return null;
575                     }
576
577                     cookie = new WeakReference JavaDoc<Node.Cookie>(ret);
578                 }
579             } else {
580                 ret = (cookie == null) ? null : cookie.get();
581             }
582
583             return ret;
584         }
585     } // end of CookieEntry
586

587     /** Implementation of the result.
588      */

589     private static final class R extends Object JavaDoc {
590         /** list of registered cookies */
591         public List JavaDoc<Node.Cookie> cookies;
592
593         /** base class of the first cookie registered here */
594         public Class JavaDoc base;
595
596         R() {
597         }
598
599         /** Adds a cookie.
600          * @return true if adding should continue on superclasses should continue
601          */

602         public void add(Node.Cookie cookie) {
603             if (cookies == null) {
604                 cookies = new ArrayList<Node.Cookie>(1);
605                 cookies.add(cookie);
606                 base = baseForCookie(cookie);
607
608                 return;
609             }
610
611             Class JavaDoc<?> newBase = baseForCookie(cookie);
612
613             if ((base == null) || newBase.isAssignableFrom(base)) {
614                 cookies.set(0, cookie);
615                 base = newBase;
616             } else {
617                 cookies.add(cookie);
618             }
619         }
620
621         /** Removes a cookie.
622          * @return true if empty
623          */

624         public boolean remove(Node.Cookie cookie) {
625             if (cookies == null) {
626                 return true;
627             }
628
629             if (cookies.remove(cookie) && (cookies.size() == 0)) {
630                 base = null;
631                 cookies = null;
632
633                 return true;
634             }
635
636             base = baseForCookie(cookies.get(0));
637
638             return false;
639         }
640
641         /** @return the cookie for this result or null
642          */

643         public Node.Cookie cookie() {
644             return ((cookies == null) || cookies.isEmpty()) ? null : cookies.get(0);
645         }
646     }
647     
648     /** Pair that wraps another Lookup.Item
649      */

650     private static final class PairWrap extends AbstractLookup.Pair {
651         private Lookup.Item<?> item;
652         private boolean created;
653         
654         public PairWrap(Lookup.Item<?> item) {
655             this.item = item;
656         }
657
658         protected boolean instanceOf(Class JavaDoc c) {
659             Class JavaDoc<?> k = c;
660             return k.isAssignableFrom(getType());
661         }
662
663         protected boolean creatorOf(Object JavaDoc obj) {
664             return created && getInstance() == obj;
665         }
666
667         public Object JavaDoc getInstance() {
668             created = true;
669             return item.getInstance();
670         }
671
672         public Class JavaDoc<? extends Object JavaDoc> getType() {
673             return item.getType();
674         }
675
676         public String JavaDoc getId() {
677             return item.getId();
678         }
679
680         public String JavaDoc getDisplayName() {
681             return item.getDisplayName();
682         }
683
684         public int hashCode() {
685             return 777 + item.hashCode();
686         }
687
688         public boolean equals(Object JavaDoc object) {
689             if (object instanceof PairWrap) {
690                 PairWrap p = (PairWrap)object;
691                 return item.equals(p.item);
692             }
693             return false;
694         }
695     } // end of PairWrap
696

697     /** Pair that represents an entry.
698      */

699     private static final class CookieEntryPair extends AbstractLookup.Pair {
700         private CookieEntry entry;
701
702         public CookieEntryPair(CookieEntry e) {
703             this.entry = e;
704         }
705
706         protected boolean creatorOf(Object JavaDoc obj) {
707             return obj == entry.getCookie(false);
708         }
709
710         public String JavaDoc getDisplayName() {
711             return getId();
712         }
713
714         public String JavaDoc getId() {
715             return entry.klass.getName();
716         }
717
718         public Object JavaDoc getInstance() {
719             return entry.getCookie(true);
720         }
721
722         public Class JavaDoc getType() {
723             return entry.klass;
724         }
725
726         protected boolean instanceOf(Class JavaDoc c) {
727             Class JavaDoc<?> k = c;
728             return k.isAssignableFrom(entry.klass);
729         }
730
731         public int hashCode() {
732             return entry.hashCode() + 5;
733         }
734
735         public boolean equals(Object JavaDoc obj) {
736             if (obj instanceof CookieEntryPair) {
737                 return ((CookieEntryPair) obj).entry == entry;
738             }
739
740             return false;
741         }
742     } // end of CookieEntryPair
743

744     private static final class FactAndClass {
745         final Class JavaDoc<? extends Node.Cookie> clazz;
746         final Factory factory;
747         
748         public FactAndClass(Class JavaDoc<? extends Node.Cookie> clazz, Factory factory) {
749             this.clazz = clazz;
750             this.factory = factory;
751         }
752         
753         public int hashCode() {
754             return clazz.hashCode() + factory.hashCode();
755         }
756         
757         public boolean equals(Object JavaDoc o) {
758             if (o instanceof FactAndClass) {
759                 FactAndClass f = (FactAndClass)o;
760                 return f.clazz.equals(clazz) && f.factory == factory;
761             }
762             return false;
763         }
764     }
765     
766     private static class C implements InstanceContent.Convertor<FactAndClass, Node.Cookie> {
767         static final C INSTANCE = new C();
768         
769
770         public Node.Cookie convert(CookieSet.FactAndClass obj) {
771             return obj.factory.createCookie(obj.clazz);
772         }
773
774         public Class JavaDoc<? extends Node.Cookie> type(CookieSet.FactAndClass obj) {
775             return obj.clazz;
776         }
777
778         public String JavaDoc id(CookieSet.FactAndClass obj) {
779             return obj.clazz.getName();
780         }
781
782         public String JavaDoc displayName(CookieSet.FactAndClass obj) {
783             return obj.clazz.getName();
784         }
785     }
786 }
787
Popular Tags