KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.lookup;
20
21 import org.openide.util.Lookup;
22 import org.openide.util.LookupEvent;
23 import org.openide.util.LookupListener;
24
25 import java.io.IOException JavaDoc;
26 import java.io.ObjectOutputStream JavaDoc;
27 import java.io.Serializable JavaDoc;
28
29 import java.lang.ref.*;
30 import java.lang.ref.ReferenceQueue JavaDoc;
31
32 import java.util.*;
33 import org.openide.util.Utilities;
34
35
36 /** Implementation of the lookup from OpenAPIs that is based on the
37  * introduction of Item. This class should provide the default way
38  * of how to store (Class, Object) pairs in the lookups. It offers
39  * protected methods for subclasses to register the pairs.
40  * <p>Serializable since 3.27.
41  * @author Jaroslav Tulach
42  * @since 1.9
43  */

44 public class AbstractLookup extends Lookup implements Serializable JavaDoc {
45     static final long serialVersionUID = 5L;
46
47     /** lock for initialization of the maps of lookups */
48     private static Object JavaDoc treeLock = new Object JavaDoc();
49
50     /** the tree that registers all items (or Integer as a treshold size) */
51     private Object JavaDoc tree;
52
53     /** count of items in to lookup */
54     private int count;
55
56     /** Constructor to create this lookup and associate it with given
57      * Content. The content than allows the creator to invoke protected
58      * methods which are not accessible for any other user of the lookup.
59      *
60      * @param content the content to assciate with
61      *
62      * @since 1.25
63      */

64     public AbstractLookup(Content content) {
65         content.attach(this);
66     }
67
68     /** Constructor for testing purposes that allows specification of storage
69      * as mechanism as well.
70      */

71     AbstractLookup(Content content, Storage<?> storage) {
72         this(content);
73         this.tree = storage;
74         initialize();
75     }
76
77     /** Constructor for testing purposes that allows specification of storage
78      * as mechanism as well.
79      * @param trashhold number of Pair to "remain small"
80      */

81     AbstractLookup(Content content, Integer JavaDoc trashhold) {
82         this(content);
83         this.tree = trashhold;
84     }
85
86     /** Default constructor for subclasses that do not need to provide a content
87      */

88     protected AbstractLookup() {
89     }
90
91     public String JavaDoc toString() {
92         if (tree instanceof Storage) {
93             return "AbstractLookup" + lookup(new Lookup.Template<Object JavaDoc>(Object JavaDoc.class)).allItems(); // NOI18N
94
} else {
95             return super.toString();
96         }
97     }
98
99     /** Entres the storage management system.
100      */

101     @SuppressWarnings JavaDoc("unchecked")
102     private <T> AbstractLookup.Storage<T> enterStorage() {
103         for (;;) {
104             synchronized (treeLock) {
105                 if (tree instanceof AbstractLookup.Storage) {
106                     if (tree instanceof DelegatingStorage) {
107                         // somebody is using the lookup right now
108
DelegatingStorage del = (DelegatingStorage) tree;
109
110                         // check whether there is not access from the same
111
// thread (can throw exception)
112
del.checkForTreeModification();
113
114                         try {
115                             treeLock.wait();
116                         } catch (InterruptedException JavaDoc ex) {
117                             // ignore and go on
118
}
119
120                         continue;
121                     } else {
122                         // ok, tree is initialized and nobody is using it yet
123
tree = new DelegatingStorage((Storage<T>) tree);
124
125                         return (Storage<T>) tree;
126                     }
127                 }
128
129                 // first time initialization of the tree
130
if (tree instanceof Integer JavaDoc) {
131                     tree = new ArrayStorage((Integer JavaDoc) tree);
132                 } else {
133                     tree = new ArrayStorage();
134                 }
135             }
136
137             // the tree has not yet been initilized, initialize and go on again
138
initialize();
139         }
140     }
141
142     /** Exists tree ownership.
143      */

144     private AbstractLookup.Storage exitStorage() {
145         synchronized (treeLock) {
146             AbstractLookup.Storage stor = ((DelegatingStorage) tree).exitDelegate();
147             tree = stor;
148             treeLock.notifyAll();
149
150             return stor;
151         }
152     }
153
154     /** Method for subclasses to initialize them selves.
155      */

156     protected void initialize() {
157     }
158
159     /** Notifies subclasses that a query is about to be processed.
160      * @param template the template
161      */

162     protected void beforeLookup(Template<?> template) {
163     }
164
165     /** The method to add instance to the lookup with.
166      * @param pair class/instance pair
167      */

168     protected final void addPair(Pair<?> pair) {
169         addPairImpl(pair);
170     }
171
172     private final <Transaction> void addPairImpl(Pair<?> pair) {
173         HashSet<R> toNotify = new HashSet<R>();
174
175         AbstractLookup.Storage<Transaction> t = enterStorage();
176         Transaction transaction = null;
177
178         try {
179             transaction = t.beginTransaction(-2);
180
181             if (t.add(pair, transaction)) {
182                 try {
183                     pair.setIndex(t, count++);
184                 } catch (IllegalStateException JavaDoc ex) {
185                     // remove the pair
186
t.remove(pair, transaction);
187
188                     // rethrow the exception
189
throw ex;
190                 }
191
192                 // if the pair is newly added and was not there before
193
t.endTransaction(transaction, toNotify);
194             } else {
195                 // just finish the process by calling endTransaction
196
t.endTransaction(transaction, new HashSet<R>());
197             }
198         } finally {
199             exitStorage();
200         }
201
202         notifyListeners(toNotify);
203     }
204
205     /** Remove instance.
206      * @param pair class/instance pair
207      */

208     protected final void removePair(Pair<?> pair) {
209         removePairImpl(pair);
210     }
211
212     private <Transaction> void removePairImpl(Pair<?> pair) {
213         HashSet<R> toNotify = new HashSet<R>();
214
215         AbstractLookup.Storage<Transaction> t = enterStorage();
216         Transaction transaction = null;
217
218         try {
219             transaction = t.beginTransaction(-1);
220             t.remove(pair, transaction);
221             t.endTransaction(transaction, toNotify);
222         } finally {
223             exitStorage();
224         }
225
226         notifyListeners(toNotify);
227     }
228
229     /** Changes all pairs in the lookup to new values.
230      * @param collection the collection of (Pair) objects
231      */

232     protected final void setPairs(Collection<? extends Pair> collection) {
233         notifyCollectedListeners(setPairsAndCollectListeners(collection));
234     }
235     
236     /** Getter for set of pairs. Package private contract with MetaInfServicesLookup.
237      * @return a LinkedHashSet that can be modified
238      */

239     final LinkedHashSet<Pair<?>> getPairsAsLHS() {
240         AbstractLookup.Storage<?> t = enterStorage();
241
242         try {
243             Enumeration<Pair<Object JavaDoc>> en = t.lookup(Object JavaDoc.class);
244             LinkedHashSet<Pair<?>> arr = new LinkedHashSet<Pair<?>>();
245             while (en.hasMoreElements()) {
246                 Pair<Object JavaDoc> item = en.nextElement();
247                 arr.add(item);
248             }
249             return arr;
250         } finally {
251             exitStorage();
252         }
253     }
254
255     /** Collects listeners without notification. Needed in MetaInfServicesLookup
256      * right now, but maybe will become an API later.
257      */

258     final <Transaction> HashSet<R> setPairsAndCollectListeners(Collection<? extends Pair> collection) {
259         HashSet<R> toNotify = new HashSet<R>(27);
260
261         AbstractLookup.Storage<Transaction> t = enterStorage();
262         Transaction transaction = null;
263
264         try {
265             // map between the Items and their indexes (Integer)
266
HashMap<Item<?>,Info> shouldBeThere = new HashMap<Item<?>,Info>(collection.size() * 2);
267
268             count = 0;
269
270             Iterator it = collection.iterator();
271             transaction = t.beginTransaction(collection.size());
272
273             while (it.hasNext()) {
274                 Pair item = (Pair) it.next();
275
276                 if (t.add(item, transaction)) {
277                     // the item has not been there yet
278
//t.endTransaction(transaction, toNotify);
279
}
280
281                 // remeber the item, because it should not be removed
282
shouldBeThere.put(item, new Info(count++, transaction));
283
284                 // arr.clear ();
285
}
286
287             // Object transaction = t.beginTransaction ();
288
// deletes all objects that should not be there and
289
t.retainAll(shouldBeThere, transaction);
290
291             // collect listeners
292
t.endTransaction(transaction, toNotify);
293
294             /*
295             // check consistency
296             Enumeration en = t.lookup (java.lang.Object.class);
297             boolean[] max = new boolean[count];
298             int mistake = -1;
299             while (en.hasMoreElements ()) {
300                 Pair item = (Pair)en.nextElement ();
301
302                 if (max[item.index]) {
303                     mistake = item.index;
304                 }
305                 max[item.index] = true;
306             }
307
308             if (mistake != -1) {
309                 System.err.println ("Mistake at: " + mistake);
310                 tree.print (System.err, true);
311             }
312             */

313         } finally {
314             exitStorage();
315         }
316
317         return toNotify;
318     }
319
320     /** Notifies all collected listeners. Needed by MetaInfServicesLookup,
321      * maybe it will be an API later.
322      */

323     final void notifyCollectedListeners(Set<R> listeners) {
324         notifyListeners(listeners);
325     }
326
327     private final void writeObject(ObjectOutputStream JavaDoc oos)
328     throws IOException JavaDoc {
329         AbstractLookup.Storage s = enterStorage();
330
331         try {
332             // #36830: Serializing only InheritanceTree no ArrayStorage
333
s.beginTransaction(Integer.MAX_VALUE);
334
335             // #32040: don't write half-made changes
336
oos.defaultWriteObject();
337         } finally {
338             exitStorage();
339         }
340     }
341
342     public final <T> T lookup(Class JavaDoc<T> clazz) {
343         Lookup.Item<T> item = lookupItem(new Lookup.Template<T>(clazz));
344         return (item == null) ? null : item.getInstance();
345     }
346
347     public final <T> Lookup.Item<T> lookupItem(Lookup.Template<T> template) {
348         AbstractLookup.this.beforeLookup(template);
349
350         ArrayList<Pair<T>> list = null;
351         AbstractLookup.Storage<?> t = enterStorage();
352
353         try {
354             Enumeration<Pair<T>> en;
355
356             try {
357                 en = t.lookup(template.getType());
358
359                 return findSmallest(en, template, false);
360             } catch (AbstractLookup.ISE ex) {
361                 // not possible to enumerate the exception, ok, copy it
362
// to create new
363
list = new ArrayList<Pair<T>>();
364                 en = t.lookup(null); // this should get all the items without any checks
365

366                 // the checks will be done out side of the storage
367
while (en.hasMoreElements()) {
368                     list.add(en.nextElement());
369                 }
370             }
371         } finally {
372             exitStorage();
373         }
374
375         return findSmallest(Collections.enumeration(list), template, true);
376     }
377
378     private static <T> Pair<T> findSmallest(Enumeration<Pair<T>> en, Lookup.Template<T> template, boolean deepCheck) {
379         int smallest = InheritanceTree.unsorted(en) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
380         Pair<T> res = null;
381
382         while (en.hasMoreElements()) {
383             Pair<T> item = en.nextElement();
384
385             if (matches(template, item, deepCheck)) {
386                 if (smallest == Integer.MIN_VALUE) {
387                     // ok, sorted enumeration the first that matches is fine
388
return item;
389                 } else {
390                     // check for the smallest item
391
if (smallest > item.getIndex()) {
392                         smallest = item.getIndex();
393                         res = item;
394                     }
395                 }
396             }
397         }
398
399         return res;
400     }
401
402     public final <T> Lookup.Result<T> lookup(Lookup.Template<T> template) {
403         for (;;) {
404             AbstractLookup.ISE toRun = null;
405
406             AbstractLookup.Storage<?> t = enterStorage();
407
408             try {
409                 R<T> r = new R<T>();
410                 ReferenceToResult<T> newRef = new ReferenceToResult<T>(r, this, template);
411                 newRef.next = t.registerReferenceToResult(newRef);
412
413                 return r;
414             } catch (AbstractLookup.ISE ex) {
415                 toRun = ex;
416             } finally {
417                 exitStorage();
418             }
419
420             toRun.recover(this);
421
422             // and try again
423
}
424     }
425
426     /** Notifies listeners.
427      * @param allAffectedResults set of R
428      */

429     private static void notifyListeners(Set<R> allAffectedResults) {
430         if (allAffectedResults.isEmpty()) {
431             return;
432         }
433
434         ArrayList<Object JavaDoc> evAndListeners = new ArrayList<Object JavaDoc>();
435         {
436             for (R<?> result : allAffectedResults) {
437                 result.collectFires(evAndListeners);
438             }
439         }
440
441         {
442             Iterator it = evAndListeners.iterator();
443             while (it.hasNext()) {
444                 LookupEvent ev = (LookupEvent)it.next();
445                 LookupListener l = (LookupListener)it.next();
446                 l.resultChanged(ev);
447             }
448         }
449     }
450
451     /**
452      * Call resultChanged on all listeners.
453      * @param listeners array of listeners in the format used by
454      * javax.swing.EventListenerList. It means that there are Class
455      * objects on even positions and the listeners on odd positions
456      * @param ev the event to fire
457      */

458     static void notifyListeners(Object JavaDoc[] listeners, LookupEvent ev, Collection<Object JavaDoc> evAndListeners) {
459         for (int i = listeners.length - 1; i >= 0; i--) {
460             if (! (listeners[i] instanceof LookupListener)) {
461                 continue;
462             }
463             LookupListener ll = (LookupListener)listeners[i];
464
465             try {
466                 if (evAndListeners != null) {
467                     if (ll instanceof WaitableResult) {
468                         WaitableResult<?> wr = (WaitableResult<?>)ll;
469                         wr.collectFires(evAndListeners);
470                     } else {
471                         evAndListeners.add(ev);
472                         evAndListeners.add(ll);
473                     }
474                 } else {
475                     ll.resultChanged(ev);
476                 }
477             } catch (RuntimeException JavaDoc e) {
478                 // Such as e.g. occurred in #32040. Do not halt other things.
479
e.printStackTrace();
480             }
481         }
482     }
483
484     /** A method that defines matching between Item and Template.
485      * @param t template providing the criteria
486      * @param item the item to match
487      * @param deepCheck true if type of the pair should be tested, false if it is already has been tested
488      * @return true if item matches the template requirements, false if not
489      */

490     static boolean matches(Template<?> t, Pair<?> item, boolean deepCheck) {
491         String JavaDoc id = t.getId();
492
493         if ((id != null) && !item.getId().equals(id)) {
494             return false;
495         }
496
497         Object JavaDoc instance = t.getInstance();
498
499         if ((instance != null) && !item.creatorOf(instance)) {
500             return false;
501         }
502
503         if (deepCheck) {
504             return item.instanceOf(t.getType());
505         } else {
506             return true;
507         }
508     }
509
510     /**
511      * Compares the array elements for equality.
512      * @return true if all elements in the arrays are equal
513      * (by calling equals(Object x) method)
514      */

515     private static boolean compareArrays(Object JavaDoc[] a, Object JavaDoc[] b) {
516         // handle null values
517
if (a == null) {
518             return (b == null);
519         } else {
520             if (b == null) {
521                 return false;
522             }
523         }
524
525         if (a.length != b.length) {
526             return false;
527         }
528
529         for (int i = 0; i < a.length; i++) {
530             // handle null values for individual elements
531
if (a[i] == null) {
532                 if (b[i] != null) {
533                     return false;
534                 }
535
536                 // both are null --> ok, take next
537
continue;
538             } else {
539                 if (b[i] == null) {
540                     return false;
541                 }
542             }
543
544             // perform the comparison
545
if (!a[i].equals(b[i])) {
546                 return false;
547             }
548         }
549
550         return true;
551     }
552
553     /** Method to be called when a result is cleared to signal that the list
554      * of all result should be checked for clearing.
555      * @param template the template the result was for
556      * @return true if the hash map with all items has been cleared
557      */

558     <T> boolean cleanUpResult(Lookup.Template<T> template) {
559         AbstractLookup.Storage<?> t = enterStorage();
560
561         try {
562             return t.cleanUpResult(template) == null;
563         } finally {
564             exitStorage();
565         }
566     }
567
568     /** Generic support for listeners, so it can be used in other results
569      * as well.
570      * @param add true to add it, false to modify
571      * @param l listener to modify
572      * @param ref the value of the reference to listener or listener list
573      * @return new value to the reference to listener or list
574      */

575     @SuppressWarnings JavaDoc("unchecked")
576     static Object JavaDoc modifyListenerList(boolean add, LookupListener l, Object JavaDoc ref) {
577         if (add) {
578             if (ref == null) {
579                 return l;
580             }
581
582             if (ref instanceof LookupListener) {
583                 ArrayList arr = new ArrayList();
584                 arr.add(ref);
585                 ref = arr;
586             }
587
588             ((ArrayList) ref).add(l);
589
590             return ref;
591         } else {
592             // remove
593
if (ref == null) {
594                 return null;
595             }
596
597             if (ref == l) {
598                 return null;
599             }
600
601             ArrayList arr = (ArrayList) ref;
602             arr.remove(l);
603
604             if (arr.size() == 1) {
605                 return arr.iterator().next();
606             } else {
607                 return arr;
608             }
609         }
610     }
611
612     private static ReferenceQueue JavaDoc<Object JavaDoc> activeQueue() {
613         return Utilities.activeReferenceQueue();
614     }
615
616     /** Storage to keep the internal structure of Pairs and to answer
617      * different queries.
618      */

619     interface Storage<Transaction> {
620         /** Initializes a modification operation by creating an object
621          * that will be passsed to all add, remove, retainAll methods
622          * and should collect enough information about the change to
623          * notify listeners about the transaction later
624          *
625          * @param ensure the amount of items that will appear in the storage
626          * after the modifications (-1 == remove one, -2 == add one, >= 0
627          * the amount of objects at the end
628          * @return a token to identify the transaction
629          */

630         public Transaction beginTransaction(int ensure);
631
632         /** Collects all affected results R that were modified in the
633          * given transaction.
634          *
635          * @param modified place to add results R to
636          * @param transaction the transaction indentification
637          */

638         public void endTransaction(Transaction transaction, Set<R> modifiedResults);
639
640         /** Adds an item into the storage.
641         * @param item to add
642         * @param transaction transaction token
643         * @return true if the Item has been added for the first time or false if some other
644         * item equal to this one already existed in the lookup
645         */

646         public boolean add(AbstractLookup.Pair<?> item, Transaction transaction);
647
648         /** Removes an item.
649         */

650         public void remove(AbstractLookup.Pair item, Transaction transaction);
651
652         /** Removes all items that are not present in the provided collection.
653         * @param retain collection of Pairs to keep them in
654         * @param transaction the transaction context
655         */

656         public void retainAll(Map retain, Transaction transaction);
657
658         /** Queries for instances of given class.
659         * @param clazz the class to check
660         * @return enumeration of Item
661         * @see #unsorted
662         */

663         public <T> Enumeration<Pair<T>> lookup(Class JavaDoc<T> clazz);
664
665         /** Registers another reference to a result with the storage. This method
666          * has also a special meaning.
667          *
668          * @param newRef the new reference to remember
669          * @return the previous reference that was kept (null if newRef is the first one)
670          * the applications is expected to link from newRef to this returned
671          * value to form a linked list
672          */

673         public ReferenceToResult<?> registerReferenceToResult(ReferenceToResult<?> newRef);
674
675         /** Given the provided template, Do cleanup the results.
676          * @param templ template of a result(s) that should be checked
677          * @return null if all references for this template were cleared or one of them
678          */

679         public ReferenceToResult<?> cleanUpResult(Lookup.Template<?> templ);
680     }
681
682     /** Extension to the default lookup item that offers additional information
683      * for the data structures use in AbstractLookup
684      */

685     public static abstract class Pair<T> extends Lookup.Item<T> implements Serializable JavaDoc {
686         private static final long serialVersionUID = 1L;
687
688         /** possition of this item in the lookup, manipulated in addPair, removePair, setPairs methods */
689         private int index = -1;
690
691         /** For use by subclasses. */
692         protected Pair() {
693         }
694
695         final int getIndex() {
696             return index;
697         }
698
699         final void setIndex(AbstractLookup.Storage<?> tree, int x) {
700             if (tree == null) {
701                 this.index = x;
702
703                 return;
704             }
705
706             if (this.index == -1) {
707                 this.index = x;
708             } else {
709                 throw new IllegalStateException JavaDoc("You cannot use " + this + " in more than one AbstractLookup"); // NOI18N
710
}
711         }
712
713         /** Tests whether this item can produce object
714         * of class c.
715         */

716         protected abstract boolean instanceOf(Class JavaDoc<?> c);
717
718         /** Method that can test whether an instance of a class has been created
719          * by this item.
720          *
721          * @param obj the instance
722          * @return if the item has already create an instance and it is the same
723          * as obj.
724          */

725         protected abstract boolean creatorOf(Object JavaDoc obj);
726     }
727
728     /** Result based on one instance returned.
729      */

730     static final class R<T> extends WaitableResult<T> {
731         /** reference our result is attached to (do not modify) */
732         public ReferenceToResult<T> reference;
733
734         /** listeners on the results or pointer to one listener */
735         private Object JavaDoc listeners;
736
737         public R() {
738         }
739
740         /** Checks whether we have simple behaviour of complex.
741          */

742         private boolean isSimple() {
743             Storage s = (Storage) reference.lookup.tree;
744
745             return DelegatingStorage.isSimple(s);
746         }
747
748         //
749
// Handling cache management for both cases, no caches
750
// for simple (but mark that we needed them, so refresh can
751
// be done in cloneList) and complex when all 3 types
752
// of result are cached
753
//
754
private Object JavaDoc getFromCache(int indx) {
755             if (isSimple()) {
756                 return null;
757             }
758
759             Object JavaDoc maybeArray = reference.caches;
760
761             if (maybeArray instanceof Object JavaDoc[]) {
762                 return ((Object JavaDoc[]) maybeArray)[indx];
763             }
764
765             return null;
766         }
767
768         @SuppressWarnings JavaDoc("unchecked")
769         private Set<Class JavaDoc<? extends T>> getClassesCache() {
770             return (Set<Class JavaDoc<? extends T>>) getFromCache(0);
771         }
772
773         private void setClassesCache(Set s) {
774             if (isSimple()) {
775                 // mark it as being used
776
reference.caches = reference;
777
778                 return;
779             }
780
781             if (!(reference.caches instanceof Object JavaDoc[])) {
782                 reference.caches = new Object JavaDoc[3];
783             }
784
785             ((Object JavaDoc[]) reference.caches)[0] = s;
786         }
787
788         @SuppressWarnings JavaDoc("unchecked")
789         private Collection<T> getInstancesCache() {
790             return (Collection<T>) getFromCache(1);
791         }
792
793         private void setInstancesCache(Collection c) {
794             if (isSimple()) {
795                 // mark it as being used
796
reference.caches = reference;
797
798                 return;
799             }
800
801             if (!(reference.caches instanceof Object JavaDoc[])) {
802                 reference.caches = new Object JavaDoc[3];
803             }
804
805             ((Object JavaDoc[]) reference.caches)[1] = c;
806         }
807
808         @SuppressWarnings JavaDoc("unchecked")
809         private Pair<T>[] getItemsCache() {
810             return (Pair<T>[]) getFromCache(2);
811         }
812
813         private void setItemsCache(Collection<?> c) {
814             if (isSimple()) {
815                 // mark it as being used
816
reference.caches = reference;
817
818                 return;
819             }
820
821             if (!(reference.caches instanceof Object JavaDoc[])) {
822                 reference.caches = new Object JavaDoc[3];
823             }
824
825             ((Object JavaDoc[]) reference.caches)[2] = c.toArray(new Pair[0]);
826         }
827
828         private void clearCaches() {
829             if (reference.caches instanceof Object JavaDoc[]) {
830                 reference.caches = new Object JavaDoc[3];
831             }
832         }
833
834         /** Ok, register listeners to all classes and super classes.
835          */

836         public synchronized void addLookupListener(LookupListener l) {
837             listeners = modifyListenerList(true, l, listeners);
838         }
839
840         /** Ok, register listeners to all classes and super classes.
841          */

842         public synchronized void removeLookupListener(LookupListener l) {
843             listeners = modifyListenerList(false, l, listeners);
844         }
845
846         /** Delete all cached values, the template changed.
847          */

848         protected void collectFires(Collection<Object JavaDoc> evAndListeners) {
849             Object JavaDoc[] previousItems = getItemsCache();
850             clearCaches();
851             
852             if (previousItems != null) {
853                 Object JavaDoc[] newArray = allItemsWithoutBeforeLookup().toArray();
854
855                 if (compareArrays(previousItems, newArray)) {
856                     // do not fire any change if nothing has been changed
857
return;
858                 }
859             }
860
861             LookupListener[] arr;
862
863             synchronized (this) {
864                 if (listeners == null) {
865                     return;
866                 }
867
868                 if (listeners instanceof LookupListener) {
869                     arr = new LookupListener[] { (LookupListener) listeners };
870                 } else {
871                     ArrayList<?> l = (ArrayList<?>) listeners;
872                     arr = l.toArray(new LookupListener[l.size()]);
873                 }
874             }
875
876             final LookupListener[] ll = arr;
877             final LookupEvent ev = new LookupEvent(this);
878             notifyListeners(ll, ev, evAndListeners);
879         }
880
881         public Collection<T> allInstances() {
882             reference.lookup.beforeLookup(reference.template);
883
884             Collection<T> s = getInstancesCache();
885
886             if (s != null) {
887                 return s;
888             }
889
890             Collection<Pair<T>> items = allItemsWithoutBeforeLookup();
891             ArrayList<T> list = new ArrayList<T>(items.size());
892
893             Iterator<Pair<T>> it = items.iterator();
894
895             while (it.hasNext()) {
896                 Pair<T> item = it.next();
897                 T obj = item.getInstance();
898
899                 if (reference.template.getType().isInstance(obj)) {
900                     list.add(obj);
901                 }
902             }
903             
904             s = Collections.unmodifiableList(list);
905             setInstancesCache(s);
906
907             return s;
908         }
909
910         /** Set of all classes.
911          *
912          */

913         public Set<Class JavaDoc<? extends T>> allClasses() {
914             reference.lookup.beforeLookup(reference.template);
915
916             Set<Class JavaDoc<? extends T>> s = getClassesCache();
917
918             if (s != null) {
919                 return s;
920             }
921
922             s = new HashSet<Class JavaDoc<? extends T>>();
923
924             for (Pair<T> item : allItemsWithoutBeforeLookup()) {
925                 Class JavaDoc<? extends T> clazz = item.getType();
926
927                 if (clazz != null) {
928                     s.add(clazz);
929                 }
930             }
931
932             s = Collections.unmodifiableSet(s);
933             setClassesCache(s);
934
935             return s;
936         }
937
938         /** Items are stored directly in the allItems.
939          */

940         public Collection<? extends Item<T>> allItems() {
941             reference.lookup.beforeLookup(reference.template);
942
943             return allItemsWithoutBeforeLookup();
944         }
945
946         /** Implements the search for allItems, but without asking for before lookup */
947         private Collection<Pair<T>> allItemsWithoutBeforeLookup() {
948             Pair<T>[] c = getItemsCache();
949
950             if (c != null) {
951                 return Collections.unmodifiableList(Arrays.asList(c));
952             }
953
954             ArrayList<Pair<Object JavaDoc>> saferCheck = null;
955             AbstractLookup.Storage<?> t = reference.lookup.enterStorage();
956
957             try {
958                 try {
959                     return Collections.unmodifiableCollection(initItems(t));
960                 } catch (AbstractLookup.ISE ex) {
961                     // do less effective evaluation of items outside of the
962
// locked storage
963
saferCheck = new ArrayList<Pair<Object JavaDoc>>();
964
965                     Enumeration<Pair<Object JavaDoc>> en = t.lookup(null); // get all Pairs
966

967                     while (en.hasMoreElements()) {
968                         Pair<Object JavaDoc> i = en.nextElement();
969                         saferCheck.add(i);
970                     }
971                 }
972             } finally {
973                 reference.lookup.exitStorage();
974             }
975             return extractPairs(saferCheck);
976         }
977
978         @SuppressWarnings JavaDoc("unchecked")
979         private Collection<Pair<T>> extractPairs(final ArrayList<Pair<Object JavaDoc>> saferCheck) {
980             TreeSet<Pair<T>> items = new TreeSet<Pair<T>>(ALPairComparator.DEFAULT);
981             for (Pair<Object JavaDoc> i : saferCheck) {
982                 if (matches(reference.template, i, false)) {
983                     items.add((Pair<T>)i);
984                 }
985             }
986             return Collections.unmodifiableCollection(items);
987         }
988
989         /** Initializes items.
990          */

991         private Collection<Pair<T>> initItems(Storage<?> t) {
992             // manipulation with the tree must be synchronized
993
Enumeration<Pair<T>> en = t.lookup(reference.template.getType());
994
995             // InheritanceTree is comparator for AbstractLookup.Pairs
996
TreeSet<Pair<T>> items = new TreeSet<Pair<T>>(ALPairComparator.DEFAULT);
997
998             while (en.hasMoreElements()) {
999                 Pair<T> i = en.nextElement();
1000
1001                if (matches(reference.template, i, false)) {
1002                    items.add(i);
1003                }
1004            }
1005
1006            // create a correctly sorted copy using the tree as the comparator
1007
setItemsCache(items);
1008
1009            return items;
1010        }
1011
1012        /** Used by proxy results to synchronize before lookup.
1013         */

1014        protected void beforeLookup(Lookup.Template t) {
1015            if (t.getType() == reference.template.getType()) {
1016                reference.lookup.beforeLookup(t);
1017            }
1018        }
1019
1020        /* Do not need to implement it, the default way is ok.
1021        public boolean equals(java.lang.Object obj) {
1022            return obj == this;
1023        }
1024        */

1025        public String JavaDoc toString() {
1026            return super.toString() + " for " + reference.template;
1027        }
1028    }
1029     // end of R
1030

1031    /** A class that can be used by the creator of the AbstractLookup to
1032     * control its content. It can be passed to AbstractLookup constructor
1033     * and used to add and remove pairs.
1034     *
1035     * @since 1.25
1036     */

1037    public static class Content extends Object JavaDoc implements Serializable JavaDoc {
1038        private static final long serialVersionUID = 1L;
1039
1040        // one of them is always null (except attach stage)
1041

1042        /** abstract lookup we are connected to */
1043        private AbstractLookup al = null;
1044        private transient ArrayList<Pair> earlyPairs;
1045
1046        /** A lookup attaches to this object.
1047         */

1048        final synchronized void attach(AbstractLookup al) {
1049            if (this.al == null) {
1050                this.al = al;
1051
1052                if (earlyPairs != null) {
1053                    // we must just add no override!
1054
for (Pair<?> p : earlyPairs) {
1055                        addPair(p);
1056                    }
1057                }
1058
1059                earlyPairs = null;
1060            } else {
1061                throw new IllegalStateException JavaDoc(
1062                    "Trying to use content for " + al + " but it is already used for " + this.al
1063                ); // NOI18N
1064
}
1065        }
1066
1067        /** The method to add instance to the lookup with.
1068         * @param pair class/instance pair
1069         */

1070        public final void addPair(Pair<?> pair) {
1071            AbstractLookup a = al;
1072
1073            if (a != null) {
1074                a.addPair(pair);
1075            } else {
1076                if (earlyPairs == null) {
1077                    earlyPairs = new ArrayList<Pair>(3);
1078                }
1079
1080                earlyPairs.add(pair);
1081            }
1082        }
1083
1084        /** Remove instance.
1085         * @param pair class/instance pair
1086         */

1087        public final void removePair(Pair<?> pair) {
1088            AbstractLookup a = al;
1089
1090            if (a != null) {
1091                a.removePair(pair);
1092            } else {
1093                if (earlyPairs == null) {
1094                    earlyPairs = new ArrayList<Pair>(3);
1095                }
1096
1097                earlyPairs.remove(pair);
1098            }
1099        }
1100
1101        /** Changes all pairs in the lookup to new values.
1102         * @param c the collection of (Pair) objects
1103         */

1104        public final void setPairs(Collection<? extends Pair> c) {
1105            AbstractLookup a = al;
1106
1107            if (a != null) {
1108                a.setPairs(c);
1109            } else {
1110                earlyPairs = new ArrayList<Pair>(c);
1111            }
1112        }
1113    }
1114     // end of Content
1115

1116    /** Just a holder for index & modified values.
1117     */

1118    final static class Info extends Object JavaDoc {
1119        public int index;
1120        public Object JavaDoc transaction;
1121
1122        public Info(int i, Object JavaDoc t) {
1123            index = i;
1124            transaction = t;
1125        }
1126    }
1127
1128    /** Reference to a result R
1129     */

1130    static final class ReferenceToResult<T> extends WeakReference<R<T>> implements Runnable JavaDoc {
1131        /** next refernece in chain, modified only from AbstractLookup or this */
1132        private ReferenceToResult<?> next;
1133
1134        /** the template for the result */
1135        public final Template<T> template;
1136
1137        /** the lookup we are attached to */
1138        public final AbstractLookup lookup;
1139
1140        /** caches for results */
1141        public Object JavaDoc caches;
1142
1143        /** Creates a weak refernece to a new result R in context of lookup
1144         * for given template
1145         */

1146        private ReferenceToResult(R<T> result, AbstractLookup lookup, Template<T> template) {
1147            super(result, activeQueue());
1148            this.template = template;
1149            this.lookup = lookup;
1150            getResult().reference = this;
1151        }
1152
1153        /** Returns the result or null
1154         */

1155        R<T> getResult() {
1156            return get();
1157        }
1158
1159        /** Cleans the reference. Implements Runnable interface, do not call
1160         * directly.
1161         */

1162        public void run() {
1163            lookup.cleanUpResult(this.template);
1164        }
1165
1166        /** Clones the reference list to given Storage.
1167         * @param storage storage to clone to
1168         */

1169        public void cloneList(AbstractLookup.Storage<?> storage) {
1170            ReferenceIterator it = new ReferenceIterator(this);
1171
1172            while (it.next()) {
1173                ReferenceToResult<?> current = it.current();
1174                ReferenceToResult<?> newRef = current.cloneRef();
1175                newRef.next = storage.registerReferenceToResult(newRef);
1176                newRef.caches = current.caches;
1177
1178                if (current.caches == current) {
1179                    current.getResult().initItems(storage);
1180                }
1181            }
1182        }
1183
1184        private ReferenceToResult<T> cloneRef() {
1185            return new ReferenceToResult<T>(getResult(), lookup, template);
1186        }
1187    }
1188     // end of ReferenceToResult
1189

1190    /** Supporting class to iterate over linked list of ReferenceToResult
1191     * Use:
1192     * <PRE>
1193     * ReferenceIterator it = new ReferenceIterator (this.ref);
1194     * while (it.next ()) {
1195     * it.current (): // do some work
1196     * }
1197     * this.ref = it.first (); // remember the first one
1198     */

1199    static final class ReferenceIterator extends Object JavaDoc {
1200        private ReferenceToResult<?> first;
1201        private ReferenceToResult<?> current;
1202
1203        /** hard reference to current result, so it is not GCed meanwhile */
1204        private R<?> currentResult;
1205
1206        /** Initializes the iterator with first reference.
1207         */

1208        public ReferenceIterator(ReferenceToResult<?> first) {
1209            this.first = first;
1210        }
1211
1212        /** Moves the current to next possition */
1213        public boolean next() {
1214            ReferenceToResult<?> prev;
1215            ReferenceToResult<?> ref;
1216
1217            if (current == null) {
1218                ref = first;
1219                prev = null;
1220            } else {
1221                prev = current;
1222                ref = current.next;
1223            }
1224
1225            while (ref != null) {
1226                R<?> result = ref.get();
1227
1228                if (result == null) {
1229                    if (prev == null) {
1230                        // move the head
1231
first = ref.next;
1232                    } else {
1233                        // skip over this reference
1234
prev.next = ref.next;
1235                    }
1236
1237                    prev = ref;
1238                    ref = ref.next;
1239                } else {
1240                    // we have found next item
1241
currentResult = result;
1242                    current = ref;
1243
1244                    return true;
1245                }
1246            }
1247
1248            currentResult = null;
1249            current = null;
1250
1251            return false;
1252        }
1253
1254        /** Access to current reference.
1255         */

1256        public ReferenceToResult<?> current() {
1257            return current;
1258        }
1259
1260        /** Access to reference that is supposed to be the first one.
1261         */

1262        public ReferenceToResult<?> first() {
1263            return first;
1264        }
1265    }
1266
1267    /** Signals that a lookup is being modified from a lookup query.
1268     *
1269     * @author Jaroslav Tulach
1270     */

1271    static final class ISE extends IllegalStateException JavaDoc {
1272        /** list of jobs to execute. */
1273        private java.util.List JavaDoc<Job> jobs;
1274
1275        /** @param msg message
1276         */

1277        public ISE(String JavaDoc msg) {
1278            super(msg);
1279        }
1280
1281        /** Registers a job to be executed partially out and partially in
1282         * the lock over storage.
1283         */

1284        public void registerJob(Job job) {
1285            if (jobs == null) {
1286                jobs = new java.util.ArrayList JavaDoc<Job>();
1287            }
1288
1289            jobs.add(job);
1290        }
1291
1292        /** Executes the jobs outside, and then inside a locked session.
1293         */

1294        public void recover(AbstractLookup lookup) {
1295            if (jobs == null) {
1296                // no recovery plan, throw itself
1297
throw this;
1298            }
1299
1300            for (Job j : jobs) {
1301                j.before();
1302            }
1303
1304            AbstractLookup.Storage s = lookup.enterStorage();
1305
1306            try {
1307                for (Job j : jobs) {
1308                    j.inside();
1309                }
1310            } finally {
1311                lookup.exitStorage();
1312            }
1313        }
1314
1315        /** A job to be executed partially outside and partially inside
1316         * the storage lock.
1317         */

1318        static interface Job {
1319            public void before();
1320
1321            public void inside();
1322        }
1323    }
1324     // end of ISE
1325
}
1326
Popular Tags