KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > enode > ExtensibleLookupImpl


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 Nokia. Portions Copyright 2003-2004 Nokia.
17  * All Rights Reserved.
18  */

19
20 package org.netbeans.modules.enode;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.LinkedList JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Set JavaDoc;
28
29 import org.openide.ErrorManager;
30 import org.openide.util.Lookup;
31 import org.openide.util.Lookup.Template;
32 import org.openide.util.WeakListeners;
33 import org.openide.util.lookup.AbstractLookup;
34 import org.openide.util.lookup.InstanceContent;
35 import org.openide.util.lookup.ProxyLookup;
36
37 import org.netbeans.api.enode.ExtensibleNode;
38 import org.netbeans.spi.enode.LookupContentFactory;
39 import org.netbeans.api.registry.*;
40
41 /**
42  * Special lookup capable of reading its content from the system
43  * file system.
44  * @author David Strupl
45  */

46 public class ExtensibleLookupImpl extends ProxyLookup {
47
48     private static ErrorManager log = ErrorManager.getDefault().getInstance(SubMenuCache.class.getName());
49     private static boolean LOGGABLE = log.isLoggable(ErrorManager.INFORMATIONAL);
50
51     /** enode we (lookup) belong to */
52     private ExtensibleNode enode;
53     
54     /** Content of the lookup containing all the intances (not lookups)
55       * from the layer. */

56     private InstanceContent content;
57     
58     /** Candidates for including in this lookup (found by Registry). This
59      * cache is null at the beginning, than filled with candidates and
60      * finally the candidates are removed as they are examined.
61      */

62     private List JavaDoc candidates;
63     
64     /** lock for candidates modifications */
65     private Object JavaDoc lock = new Object JavaDoc();
66     
67     /** To avoid recursion */
68     private boolean settingLookups = false;
69     
70     /**
71      * We hold a reference to the listener for preventing
72      * the garbage collection.
73      */

74     private Listener listener;
75     /**
76      * Prevent the listeners to be attached more than once.
77      */

78     private boolean listenersAttached = false;
79     
80     /**
81      * To prevent garbage collection of context where we attached
82      * listeners. We just add items to the set and never do anything
83      * with them. But that is the reason why it is here - to hold
84      * strong references to the Context objects.
85      */

86     private Set JavaDoc listenersAttachedTo = new HashSet JavaDoc();
87     
88     /**
89      * setExtensibleNode has to be called shortly after using this
90      * constructor otherwise this class will not work properly
91      */

92     public ExtensibleLookupImpl() {
93         super(new Lookup[0]);
94     }
95
96     /** Sets the enode variable and finishes the initialization.
97      * This method has to be called shortly after the constructor -
98      * before the enode variable is needed.
99      */

100     public final void setExtensibleNode(ExtensibleNode en) {
101         this.enode = en;
102         Lookup instances = new AbstractLookup(content = new InstanceContent());
103         setLookups(new Lookup[] { instances });
104     }
105     
106     /** Notifies subclasses that a query is about to be processed.
107      * @param template the template
108      */

109     protected void beforeLookup (Template template) {
110         super.beforeLookup(template);
111         
112         if (settingLookups) {
113             if (LOGGABLE) {
114                 log.log(this + " beforeLookup exiting because settingLookups == true"); // NOI18N
115
Thread.dumpStack();
116             }
117             return;
118         }
119         
120         if (LOGGABLE) log.log("beforeLookup " + template.getType().getName());
121         Iterator JavaDoc c = getCandidates();
122         
123         while (c.hasNext()) {
124             Object JavaDoc o = c.next();
125             if (LOGGABLE) log.log("candidate " + o);
126             if (o instanceof LookupContentFactory) {
127                 LookupContentFactory lcf = (LookupContentFactory)o;
128                 if (lcf instanceof FactoryWrapper) {
129                     // in this case we avoid initialization of the objects
130
// that we know not to be returned anyway
131
FactoryWrapper impl = (FactoryWrapper)lcf;
132                     if (! impl.matches(template)) {
133                         if (LOGGABLE) log.log("continue");
134                         continue;
135                     }
136                 }
137                 // remove object o (lcf, impl) from the list of candidates
138
synchronized (lock) {
139                     // we can do this here even if there are more threads in this
140
// method since we are iterating through a copy (getCandidates returned a copy
141
// of candidates)
142
if (candidates != null) {
143                         candidates.remove(o);
144                     }
145                 }
146
147                 Object JavaDoc resObject = lcf.create(enode);
148                 Lookup resLookup = lcf.createLookup(enode);
149                 if (resLookup != null) {
150                     if (LOGGABLE) log.log("adding lookup " + resLookup);
151                     addLookup(resLookup);
152                 }
153                 if (resObject != null) {
154                     if (LOGGABLE) log.log("adding " + resObject);
155                     content.add(resObject);
156                 }
157             } else {
158                 if (! (o instanceof Context)) { // Context is directory -- not interested in warning
159
ErrorManager.getDefault().log(ErrorManager.WARNING,
160                         "For " + enode + // NOI18N
161
" ExtensibleNodeLookup found an object that is not LookupContentFactory : " + o); // NOI18N
162
}
163             }
164         }
165     }
166
167     /**
168      * Adds additional lookup to this proxy lookup.
169      */

170     private void addLookup(Lookup l) {
171         Lookup []old = getLookups();
172         for (int i = 0; i < old.length; i++) {
173             if (old[i] == l) {
174                 // it is already there - do nothing
175
return;
176             }
177         }
178         Lookup []newL = new Lookup[old.length + 1];
179         System.arraycopy(old, 0, newL, 0, old.length);
180         newL[old.length] = l;
181         try {
182             settingLookups = true;
183             setLookups(newL);
184         } finally {
185             settingLookups = false;
186         }
187     }
188     
189     /**
190      * Check the cache of candidates and return an iterator.
191      * !!! The iterator can change between successive calls to this
192      * method due to removing candidates !!!
193      * This method returns an iterator from a copy of the actual candidates list
194      */

195     private Iterator JavaDoc getCandidates() {
196         List JavaDoc c = null;
197         if (candidates == null) {
198             // we are called for the first time
199
c = new LinkedList JavaDoc();
200             String JavaDoc[] whereToSearch = enode.getPaths();
201             for (int i = 0; i < whereToSearch.length; i++) {
202                 computeCandidates(whereToSearch[i], c);
203             }
204             listenersAttached = true;
205         }
206         
207         synchronized (lock) {
208             if (candidates == null) {
209                 candidates = c;
210             }
211             // return a copy to make sure two threads have distinct copies
212
List JavaDoc result = new ArrayList JavaDoc(candidates);
213             return result.iterator();
214         }
215     }
216     
217     /**
218      * Consults Registry to get the list of applicable objects.
219      */

220     private void computeCandidates(String JavaDoc name, List JavaDoc result) {
221         String JavaDoc path = ExtensibleNode.E_NODE_LOOKUP + name;
222         try {
223             boolean exists = true;
224             Context con = Context.getDefault().getSubcontext(path);
225             if (con == null) {
226                 con = findExistingContext(path);
227                 exists = false;
228             }
229             if (!listenersAttached) {
230                 ContextListener l1 = getContextListener(con);
231                 con.addContextListener(l1);
232                 listenersAttachedTo.add(con);
233             }
234             if (exists) {
235                 List JavaDoc objects = con.getOrderedObjects();
236                 result.addAll(objects);
237             }
238         } catch (Exception JavaDoc ce) {
239             ErrorManager.getDefault().getInstance("org.netbeans.modules.enode").notify(ErrorManager.INFORMATIONAL, ce); // NOI18N
240
}
241     }
242     
243     /**
244      * Lazy initialization of the listener variable. This method
245      * will return a weak listener.
246      * The weak listener references the object hold
247      * by the <code> listener </code> variable.
248      */

249     private ContextListener getContextListener(Object JavaDoc source) {
250         if (listener == null) {
251             listener = new Listener();
252         }
253         return (ContextListener)WeakListeners.create(ContextListener.class, listener, source);
254     }
255     
256     /**
257      * Change content of the lookup after the listener detected a change
258      * on the system file system.
259      */

260     private void changeContent() {
261         synchronized (lock) {
262             candidates = null;
263         }
264         Lookup instances = new AbstractLookup(content = new InstanceContent());
265         setLookups(new Lookup[] { instances });
266     }
267     
268     /**
269      * Whatever happens in the selected context this listener only calls
270      * changeContent.
271      */

272     private class Listener implements ContextListener {
273         public void attributeChanged(AttributeEvent evt) {
274             changeContent();
275         }
276         
277         public void bindingChanged(BindingEvent evt) {
278             changeContent();
279         }
280         
281         public void subcontextChanged(SubcontextEvent evt) {
282             changeContent();
283         }
284     }
285     
286     /**
287      * Tries to find an existing context that is created from given path.
288      * If context with given path does not exist this method tries to walk
289      * up to the parent until an existing one is found.
290      */

291     static Context findExistingContext(String JavaDoc path) {
292         String JavaDoc result = path;
293         Context con = Context.getDefault().getSubcontext(result);
294         while (con == null) {
295             int slash = result.lastIndexOf('/');
296             if (slash < 0) {
297                 if (LOGGABLE) log.log("Cound not find proper context for " + path); // NOI18N
298
return Context.getDefault();
299             }
300             result = result.substring(0, slash);
301             con = Context.getDefault().getSubcontext(result);
302         }
303         return con;
304     }
305 }
306
Popular Tags