KickJava   Java API By Example, From Geeks To Geeks.

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


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.Collection JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.LinkedList JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.ListIterator JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Set JavaDoc;
33
34 import javax.swing.Action JavaDoc;
35 import javax.swing.JComponent JavaDoc;
36 import javax.swing.JSeparator JavaDoc;
37
38 import org.openide.ErrorManager;
39 import org.openide.util.WeakListeners;
40
41 import org.netbeans.api.enode.*;
42 import org.netbeans.api.registry.*;
43
44 /**
45  * Object that helps the ExtensibleNode to keep track of
46  * the actions. The actions are read from the layer using Registry API.
47  * @author David Strupl
48  */

49 public class ExtensibleActionsImpl extends ExtensibleActions {
50
51     private static ErrorManager log = ErrorManager.getDefault().getInstance(ExtensibleActionsImpl.class.getName());
52     private static boolean LOGGABLE = log.isLoggable(ErrorManager.INFORMATIONAL);
53     
54     /**
55      * Our paths.
56      */

57     private String JavaDoc[] paths;
58     
59     /**
60      * We hold a reference to the listener for preventing
61      * the garbage collection.
62      */

63     private Listener listener;
64     
65     /**
66      * Cache for the actions
67      */

68     private Action JavaDoc[] actions;
69     
70     /**
71      * Prevent the listeners to be attached more than once.
72      */

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

81     private Set JavaDoc listenersAttachedTo = new HashSet JavaDoc();
82     
83     /**
84      *
85      */

86     public ExtensibleActionsImpl(String JavaDoc[] paths) {
87         this.paths = paths;
88     }
89     
90     /** Reads actions from a Regisry context. Also adds a listener
91      * to the context.
92      * @param name of the context.
93      * @return array of actions
94      */

95     public Action JavaDoc[] getActions() {
96         if (LOGGABLE) log.log("getActions() called on " + this);
97         if (actions != null) {
98             if (LOGGABLE) log.log("returning cached actions: ");
99             return actions;
100         }
101         
102         // array for gathering the result
103
List JavaDoc arr = new ArrayList JavaDoc ();
104         // for each object from arr the location will contain String location on the SFS
105
List JavaDoc/*<String>*/ location = new ArrayList JavaDoc();
106         
107         String JavaDoc cpaths[] = getCookiePaths();
108         try {
109             for (int i = 0; i < paths.length; i++) {
110                 String JavaDoc path = ExtensibleNode.E_NODE_ACTIONS + paths[i];
111                 scanContext(path, arr, location);
112             }
113             for (int i = 0; i < cpaths.length; i++) {
114                 String JavaDoc path = ExtensibleNode.E_NODE_ACTIONS + cpaths[i];
115                 scanContext(path, arr, location);
116             }
117             if (LOGGABLE) log("arr after scanContext", arr);
118             List JavaDoc result = scanSubFolderContext(arr, location);
119             if (LOGGABLE) log("result after scanSubFolderContext", result);
120             Map JavaDoc m = new HashMap JavaDoc(); // for removing duplicate menu entries
121
for (Iterator JavaDoc it = result.iterator(); it.hasNext();) {
122                 Object JavaDoc obj = it.next();
123                 if (obj instanceof SubMenuAction) {
124                     if (m.containsKey(obj)) {
125                         // remove the duplicate
126
it.remove();
127                         if (LOGGABLE) log.log("removed " + obj);
128                         // move all the elements from the removed action to its brother
129
SubMenuAction removed = (SubMenuAction)obj;
130                         SubMenuAction sma = (SubMenuAction)m.get(obj);
131                         if (removed != sma) { // they are equal but not the same object:
132
// we have to pass the content of the other
133
// one to sma.
134
if (LOGGABLE) log.log("adding all cache entries to " + sma);
135                             sma.addAllCacheItems(removed.getCacheItries());
136                         }
137                     } else {
138                         m.put(obj, obj);
139                     }
140                 }
141             }
142             if (LOGGABLE) log("result after removing duplicates", result);
143             List JavaDoc rest = new ArrayList JavaDoc(arr);
144             if (LOGGABLE) log("rest", rest);
145             // add actions that were not configured in subfolders
146
result.addAll(rest);
147             arr = result;
148             
149         } catch (Exception JavaDoc ce) {
150             log.notify(ErrorManager.INFORMATIONAL, ce); // NOI18N
151
}
152         // replace separators with nulls
153
for (int i = 0; i < arr.size(); i++) {
154             Object JavaDoc element = arr.get(i);
155             if (element instanceof JSeparator JavaDoc) {
156                 arr.set(i, null);
157             }
158         }
159         if (LOGGABLE) log("arr before returning", arr);
160         listenersAttached = true; // after successfull iteration
161
actions = (Action JavaDoc[])arr.toArray(new Action JavaDoc[arr.size()]);
162         return actions;
163     }
164
165     /**
166      * Helper method dumping the given colletion to the log.
167      */

168     private static void log(String JavaDoc name, Collection JavaDoc c) {
169         log.log("Dumping " + name);
170         for (Iterator JavaDoc it = c.iterator(); it.hasNext();) {
171             Object JavaDoc obj = it.next();
172             if (obj != null) {
173                 log.log(obj.toString());
174             } else {
175                 log.log("null");
176             }
177         }
178         log.log("End of " + name);
179     }
180     
181     /**
182      *
183      */

184     private String JavaDoc subFoldersLocation() {
185         return ExtensibleNode.E_NODE_SUBMENUS;
186     }
187     
188     /**
189      * Adds objects from the context with path to the list arr.
190      */

191     private void scanContext(String JavaDoc path, List JavaDoc arr, List JavaDoc location) {
192         if (LOGGABLE) log.log("scanContext(" + path + ",arr) called on " + this);
193         boolean exists = true;
194         Context con = Context.getDefault().getSubcontext(path);
195         if (con == null) {
196             exists = false;
197             con = ExtensibleLookupImpl.findExistingContext(path);
198         }
199         if (!listenersAttached) {
200             ContextListener l1 = getContextListener(con);
201             con.addContextListener(l1);
202             listenersAttachedTo.add(con);
203         }
204
205         if (! exists) {
206             if (LOGGABLE) log.log("scanContext(" + path + ",arr) returning - context does not exist.");
207             return;
208         }
209         
210         List JavaDoc names = con.getOrderedNames();
211         Iterator JavaDoc it = names.iterator();
212         while (it.hasNext()) {
213             String JavaDoc objName = (String JavaDoc)it.next();
214             Object JavaDoc obj = con.getObject(objName, null);
215             String JavaDoc absName = con.getAbsoluteContextName()+"/"+objName;
216             if (absName.startsWith("/")) {
217                 absName = absName.substring(1);
218             }
219             if (LOGGABLE) log.log("scanContext(...) checking " + obj + " with absName " + absName);
220             if (obj instanceof Action JavaDoc) {
221                 arr.add(obj);
222                 location.add(absName);
223                 if (LOGGABLE) log.log("scanContext(...) adding " + obj + " with name " + absName);
224                 continue;
225             }
226             // special handling of separators since they are represented
227
// as nulls in the actions array
228
if (obj instanceof JSeparator JavaDoc) {
229                 arr.add(obj);
230                 location.add(absName);
231                 if (LOGGABLE) log.log("scanContext(...) adding " + obj + " with name " + absName);
232                 continue;
233             }
234             // general JComponents are wrapped in special actions
235
if (obj instanceof JComponent JavaDoc) {
236                 Action JavaDoc a = new ComponentAction((JComponent JavaDoc)obj);
237                 arr.add(a);
238                 location.add(absName);
239                 if (LOGGABLE) log.log("scanContext(...) adding " + a + " with name " + absName);
240                 continue;
241             }
242         }
243     }
244
245     /**
246      * Returns list of actions configured via the SubMenu
247      * folder.
248      */

249     private List JavaDoc/*<Action>*/ scanSubFolderContext(List JavaDoc/*<Object>*/objects, List JavaDoc location) {
250         String JavaDoc path = subFoldersLocation();
251         if (LOGGABLE) log.log("scanSubFolderContext(...) with " + path + ") called on " + this);
252         List JavaDoc/*<ActionCacheEntryPair>*/ linkedList = new LinkedList JavaDoc();
253         boolean exists = true;
254         Context con = Context.getDefault().getSubcontext(path);
255         if (con == null) {
256             exists = false;
257             con = ExtensibleLookupImpl.findExistingContext(path);
258         }
259         if (!listenersAttached) {
260             ContextListener l1 = getContextListener(con);
261             con.addContextListener(l1);
262             listenersAttachedTo.add(con);
263         }
264
265         if (! exists) {
266             if (LOGGABLE) log.log("scanSubFolderContext(...) returning empty list because " + path + " does not exist.");
267             return new ArrayList JavaDoc();
268         }
269         
270         Iterator JavaDoc locationIt = location.iterator();
271         for (Iterator JavaDoc it = objects.iterator(); it.hasNext(); ) {
272             Object JavaDoc obj = it.next();
273             String JavaDoc objLocation = (String JavaDoc)locationIt.next();
274             if (LOGGABLE) log.log("scanSubFolderContext(...) checking " + obj + " with location " + objLocation);
275             if (objLocation == null) {
276                 throw new IllegalStateException JavaDoc("Did not find location for " + obj);
277             }
278             SubMenuCache.CacheEntry entry = SubMenuCache.getInstance().getCacheEntry(objLocation);
279             if (entry == null) {
280                 if (LOGGABLE) log.log(obj + " with location " + objLocation + " was not found in cache");
281                 continue;
282             }
283             ActionCacheEntryPair pair = new ActionCacheEntryPair();
284             if (entry.getParent().getParent() == null) {
285                 pair.entry = entry;
286                 if (obj instanceof Action JavaDoc) {
287                     pair.action = (Action JavaDoc)obj;
288                     // successfully added to result --> remove from the original list
289
it.remove();
290                     locationIt.remove();
291                 }
292                 // general JComponents are wrapped in special actions
293
if ((obj instanceof JComponent JavaDoc) && (! (obj instanceof JSeparator JavaDoc))) {
294                     pair.action = new ComponentAction((JComponent JavaDoc)obj);
295                     // successfully added to result --> remove from the original list
296
it.remove();
297                     locationIt.remove();
298                 }
299                 // special handling of separators since they are represented
300
// as nulls in the actions array
301
if (obj instanceof JSeparator JavaDoc) {
302                     // we remove the separator from the original collection
303
// because it is now represented as pair in the linkedList
304
it.remove();
305                     locationIt.remove();
306                 }
307             } else {
308                 SubMenuCache.CacheEntry origEntry = entry;
309                 // it is in some subfolder
310
while (entry.getParent().getParent() != null) {
311                     entry = entry.getParent();
312                 }
313                 pair.entry = entry;
314                 if (entry instanceof SubMenuCache.MenuEntry) {
315                     SubMenuAction sma = new SubMenuAction((SubMenuCache.MenuEntry)entry);
316                     pair.action = sma;
317                     sma.addItemFromCache(origEntry);
318                     // successfully added to result --> remove from the original list
319
it.remove();
320                     locationIt.remove();
321                 } else {
322                     log.log(entry + " is not MenuEntry! adding null instead of " + obj);
323                 }
324             }
325             insertPair(linkedList, pair);
326         }
327         ArrayList JavaDoc arr = new ArrayList JavaDoc();
328         for (Iterator JavaDoc it = linkedList.iterator(); it.hasNext();) {
329             ActionCacheEntryPair acep = (ActionCacheEntryPair)it.next();
330             arr.add(acep.action);
331         }
332         return arr;
333     }
334
335     /**
336      *
337      */

338     private static void insertPair(List JavaDoc/*<ActionCacheEntryPair>*/ list, ActionCacheEntryPair pair) {
339         ActionCacheEntryPair existing = null;
340         ListIterator JavaDoc it = list.listIterator();
341         while (it.hasNext()) {
342             existing = (ActionCacheEntryPair)it.next();
343             if (! pair.entry.getParent().equals(existing.entry.getParent())) {
344                 throw new IllegalStateException JavaDoc(pair.entry + " and " + existing.entry + " don't have the same parent");
345             }
346             int pairIndex = pair.entry.getIndex();
347             int existingIndex = existing.entry.getIndex();
348             if (pairIndex < existingIndex) {
349                 if (it.hasPrevious()) {
350                     ActionCacheEntryPair previous = (ActionCacheEntryPair)it.previous();
351                 }
352                 it.add(pair);
353                 return;
354             }
355         }
356         // if this will be the last element in the list:
357
it.add(pair);
358     }
359     
360     /**
361      * Computes additional paths for actions configured
362      * via adding an action to a cookie instance.
363      */

364     private String JavaDoc[] getCookiePaths() {
365         if (LOGGABLE) log.log("getCookiePaths() called on " + this);
366         ArrayList JavaDoc names = new ArrayList JavaDoc();
367         for (int i = 0; i < paths.length; i++) {
368             String JavaDoc path = ExtensibleNode.E_NODE_LOOKUP + paths[i];
369             if (LOGGABLE) log.log("getCookiePaths() checking " + path);
370             try {
371                 boolean exists = true;
372                 Context con = Context.getDefault().getSubcontext(path);
373                 if (con == null) {
374                     con = ExtensibleLookupImpl.findExistingContext(path);
375                     exists = false;
376                 }
377                 if (!listenersAttached) {
378                     ContextListener l1 = getContextListener(con);
379                     con.addContextListener(l1);
380                     listenersAttachedTo.add(con);
381                 }
382                 if (exists) {
383                     Collection JavaDoc objects = con.getBindingNames();
384                     if (LOGGABLE) {
385                         log.log("getCookiePaths() adding names:");
386                         for (Iterator JavaDoc it = objects.iterator(); it.hasNext();) {
387                             Object JavaDoc tmp = it.next();
388                             if (tmp != null) {
389                                 log.log( tmp.toString());
390                             }
391                         }
392                     }
393                     names.addAll(objects);
394                 }
395             } catch (Exception JavaDoc ce) {
396                 log.notify(ErrorManager.INFORMATIONAL, ce); // NOI18N
397
}
398         }
399         if (LOGGABLE) log.log("getCookiePaths() returning " + names.size() + " names.");
400         return (String JavaDoc[])names.toArray(new String JavaDoc[names.size()]);
401     }
402
403     public String JavaDoc toString() {
404         String JavaDoc res = "ExtensibleActionsImpl[";
405         if (paths != null) {
406             res += "path=";
407             for (int i = 0; i < paths.length; i++) {
408                 res += paths[i] + ",";
409             }
410         }
411         if (actions != null) {
412             res += "actions=";
413             for (int i = 0; i < actions.length; i++) {
414                 res += actions[i] + ",";
415             }
416         }
417         return res + "]";
418     }
419     
420     /**
421      * Lazy initialization of the listener variable. This method
422      * will return a weak listener.
423      * The weak listener references the object hold
424      * by the <code> listener </code> variable.
425      */

426     private ContextListener getContextListener(Object JavaDoc source) {
427         if (listener == null) {
428             listener = new Listener();
429         }
430         if (LOGGABLE) log.log(this + " adding context listener to " + source);
431         return (ContextListener)WeakListeners.create(ContextListener.class, listener, source);
432     }
433     
434     /**
435      * Whatever happens in the selected context this listener only clears
436      * the actions reference. This cause the list of actions to
437      * be computed next time someone asks for them.
438      */

439     private class Listener implements ContextListener {
440         public void attributeChanged(AttributeEvent evt) {
441             if (LOGGABLE) log.log("attributeChanged("+evt+") called on listener from " + ExtensibleActionsImpl.this);
442             actions = null;
443         }
444         
445         public void bindingChanged(BindingEvent evt) {
446             if (LOGGABLE) log.log("bindingChanged("+evt+") called on listener from " + ExtensibleActionsImpl.this);
447             actions = null;
448         }
449         
450         public void subcontextChanged(SubcontextEvent evt) {
451             if (LOGGABLE) log.log("subcontextChanged("+evt+") called on listener from " + ExtensibleActionsImpl.this);
452             actions = null;
453         }
454     }
455     
456     private static class ActionCacheEntryPair {
457         public Action JavaDoc action;
458         public SubMenuCache.CacheEntry entry;
459     }
460 }
461
Popular Tags