KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > util > actions > CookieAction


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.actions;
20
21 import org.openide.nodes.Node;
22 import org.openide.nodes.NodeAdapter;
23 import org.openide.nodes.NodeListener;
24 import org.openide.util.Lookup;
25 import org.openide.util.Mutex;
26
27 import java.beans.PropertyChangeEvent JavaDoc;
28
29 import java.lang.ref.*;
30
31 import java.util.*;
32
33
34 /** An action
35 * dependent on the cookies of the selected nodes.
36 *
37 * @author Petr Hamernik, Jaroslav Tulach, Dafe Simonek, Jesse Glick
38 */

39 public abstract class CookieAction extends NodeAction {
40     /** name of property with cookies for this action */
41     private static final String JavaDoc PROP_COOKIES = "cookies"; // NOI18N
42

43     /** Action will be enabled if there are one or more selected nodes
44     * and there is exactly one node which supports the given cookies. */

45     public static final int MODE_ONE = 0x01;
46
47     /** Action will be enabled if there are several selected nodes
48     * and some of them (at least one, but not all)
49     * support the given cookies. */

50     public static final int MODE_SOME = 0x02;
51
52     /** Action will be enabled if there are one or more selected nodes
53     * and all of them support the given cookies. */

54     public static final int MODE_ALL = 0x04;
55
56     /** Action will be enabled if there is exactly one selected node
57     * and it supports the given cookies. */

58     public static final int MODE_EXACTLY_ONE = 0x08;
59
60     /** Action will be enabled if there are one or more selected nodes
61     * and any of them (one, all, or some) support the given cookies. */

62     public static final int MODE_ANY = 0x07;
63
64     // [PENDING] 0x06 should suffice, yes? --jglick
65
private static final long serialVersionUID = 6031319415908298424L;
66     private CookiesChangeListener listener = new CookiesChangeListener(this);
67
68     /** Get the mode of the action: how strict it should be about
69     * cookie support.
70     * @return the mode of the action. Possible values are disjunctions of the <code>MODE_XXX</code>
71     * constants. */

72     protected abstract int mode();
73
74     /** Get the cookies that this action requires. The cookies are disjunctive, i.e. a node
75     * must support AT LEAST ONE of the cookies specified by this method.
76     *
77     * @return a list of cookies
78     */

79     protected abstract Class JavaDoc<?>[] cookieClasses(); // might not extend Node.Cookie; seems to work with Lookup
80

81     /** Getter for cookies.
82     * @return the set of cookies for this
83     */

84     private Class JavaDoc<?>[] getCookies() {
85         Class JavaDoc[] ret = (Class JavaDoc[]) getProperty(PROP_COOKIES);
86
87         if (ret != null) {
88             return ret;
89         }
90
91         ret = cookieClasses();
92         putProperty(PROP_COOKIES, ret);
93
94         return ret;
95     }
96
97     /** Test for enablement based on the cookies of selected nodes.
98     * Generally subclasses should not override this except for strange
99     * purposes, and then only calling the super method and adding a check.
100     * Just use {@link #cookieClasses} and {@link #mode} to specify
101     * the enablement logic.
102     * @param activatedNodes the set of activated nodes
103     * @return <code>true</code> to enable
104     */

105     protected boolean enable(Node[] activatedNodes) {
106         if (activatedNodes.length == 0) {
107             return false;
108         }
109
110         // sets new nodes to cookie change listener
111
listener.setNodes(activatedNodes);
112
113         // perform enable / disable logic
114
return doEnable(activatedNodes);
115     }
116
117     /** Implements <code>ContextAwareAction</code> interface method. */
118     public javax.swing.Action JavaDoc createContextAwareInstance(Lookup actionContext) {
119         return new CookieDelegateAction(this, actionContext);
120     }
121
122     /** Helper, actually performs enable / disable logic */
123     boolean doEnable(Node[] activatedNodes) {
124         int supported = resolveSupported(activatedNodes);
125
126         if (supported == 0) {
127             return false;
128         }
129
130         int mode = mode();
131
132         return
133         // [PENDING] shouldn't MODE_ONE also say: && supported == 1? --jglick
134
((mode & MODE_ONE) != 0) || (((mode & MODE_ALL) != 0) && (supported == activatedNodes.length)) ||
135         (((mode & MODE_EXACTLY_ONE) != 0) && (activatedNodes.length == 1)) ||
136         (((mode & MODE_SOME) != 0) && (supported < activatedNodes.length));
137     }
138
139     /**
140     * Implementation of the above method.
141     *
142     * @param activatedNodes gives array of actually activated nodes.
143     * @return number of supported classes
144     */

145     private int resolveSupported(Node[] activatedNodes) {
146         int ret = 0;
147
148         Class JavaDoc<?>[] cookies = getCookies();
149
150         for (Node n : activatedNodes) {
151             for (Class JavaDoc<?> cookie : cookies) {
152                 // test for supported cookies
153
@SuppressWarnings JavaDoc("unchecked")
154                 Lookup.Template<?> templ = new Lookup.Template(cookie);
155                 if (n.getLookup().lookupItem(templ) != null) {
156                     ret++;
157
158                     break;
159                 }
160             }
161         }
162
163         return ret;
164     }
165
166     /** Tracks changes of cookie classes in currently selected nodes
167     */

168     private static final class CookiesChangeListener extends NodeAdapter {
169         /** our weak listener */
170         private org.openide.nodes.NodeListener listener;
171
172         /** The nodes we are currently listening */
173         private List<Reference<Node>> nodes;
174
175         /** the associated action */
176         private Reference<CookieAction> action;
177
178         /** Constructor - asociates with given cookie action
179         */

180         public CookiesChangeListener(CookieAction a) {
181             listener = org.openide.nodes.NodeOp.weakNodeListener(this, null);
182             action = new WeakReference<CookieAction>(a);
183         }
184
185         /** Sets the nodes to work on */
186         void setNodes(Node[] newNodes) {
187             // detach old nodes
188
List<Reference<Node>> nodes2 = this.nodes;
189
190             if (nodes2 != null) {
191                 detachListeners(nodes2);
192             }
193
194             nodes = null;
195
196             // attach to new nodes
197
if (newNodes != null) {
198                 nodes = new ArrayList<Reference<Node>>(newNodes.length);
199
200                 for (int i = 0; i < newNodes.length; i++)
201                     nodes.add(new WeakReference<Node>(newNodes[i]));
202
203                 attachListeners(nodes);
204             }
205         }
206
207         /** Removes itself as a listener from given nodes */
208         void detachListeners(List<Reference<Node>> nodes) {
209             Iterator<Reference<Node>> it = nodes.iterator();
210
211             while (it.hasNext()) {
212                 Node node = it.next().get();
213
214                 if (node != null) {
215                     node.removeNodeListener(listener);
216                 }
217             }
218         }
219
220         /** Attach itself as a listener to the given nodes */
221         void attachListeners(List<Reference<Node>> nodes) {
222             Iterator<Reference<Node>> it = nodes.iterator();
223
224             while (it.hasNext()) {
225                 Node node = it.next().get();
226
227                 if (node != null) {
228                     node.addNodeListener(listener);
229                 }
230             }
231         }
232
233         /** Reacts to the cookie classes change -
234         * calls enable on asociated action */

235         public void propertyChange(PropertyChangeEvent JavaDoc ev) {
236             // filter only cookie classes changes
237
if (!Node.PROP_COOKIE.equals(ev.getPropertyName())) {
238                 return;
239             }
240
241             // find asociated action
242
final CookieAction a = action.get();
243
244             if (a == null) {
245                 return;
246             }
247
248             List<Reference<Node>> _nodes = this.nodes;
249
250             if (_nodes != null) {
251                 ArrayList<Node> nonNullNodes = new ArrayList<Node>(_nodes.size());
252                 Iterator<Reference<Node>> it = _nodes.iterator();
253
254                 while (it.hasNext()) {
255                     Node node = it.next().get();
256
257                     if (node != null) {
258                         nonNullNodes.add(node);
259                     } else {
260                         // If there is really a selection, it should not have been collected.
261
return;
262                     }
263                 }
264
265                 final Node[] nodes2 = new Node[nonNullNodes.size()];
266                 nonNullNodes.toArray(nodes2);
267
268                 Mutex.EVENT.writeAccess(
269                     new Runnable JavaDoc() {
270                         public void run() {
271                             a.setEnabled(a.enable(nodes2));
272                         }
273                     }
274                 );
275             }
276         }
277
278         protected void finalize() {
279             detachListeners(nodes);
280         }
281     }
282      // end of CookiesChangeListener
283

284     /** A delegate action that is usually associated with a specific lookup and
285      * extract the nodes it operates on from it. Otherwise it delegates to the
286      * regular NodeAction.
287      */

288     final static class CookieDelegateAction extends org.openide.util.actions.NodeAction.DelegateAction
289     implements org.openide.nodes.NodeListener, Runnable JavaDoc {
290         /** our weak listener */
291         private org.openide.nodes.NodeListener listener;
292
293         /** The nodes we are currently listening */
294         private List<Reference<Node>> nodes;
295
296         public CookieDelegateAction(CookieAction a, Lookup actionContext) {
297             super(a, actionContext);
298             listener = org.openide.nodes.NodeOp.weakNodeListener(this, null);
299             setNodes(nodes());
300         }
301
302         public void resultChanged(org.openide.util.LookupEvent ev) {
303             setNodes(nodes());
304             superResultChanged(ev);
305         }
306
307         private void superResultChanged(org.openide.util.LookupEvent ev) {
308             super.resultChanged(ev);
309         }
310
311         public void childrenAdded(org.openide.nodes.NodeMemberEvent ev) {
312         }
313
314         public void childrenRemoved(org.openide.nodes.NodeMemberEvent ev) {
315         }
316
317         public void childrenReordered(org.openide.nodes.NodeReorderEvent ev) {
318         }
319
320         public void nodeDestroyed(org.openide.nodes.NodeEvent ev) {
321         }
322
323         public void propertyChange(PropertyChangeEvent JavaDoc ev) {
324             // filter only cookie classes changes
325
if (!Node.PROP_COOKIE.equals(ev.getPropertyName())) {
326                 return;
327             }
328
329             // find asociated action
330
Mutex.EVENT.readAccess(this);
331         }
332
333         public void run() {
334             superResultChanged(null);
335         }
336
337         /** Attach itself as a listener own nodes */
338         private void setNodes(org.openide.nodes.Node[] newNodes) {
339             // detach listeners from old nodes
340
detachListeners(nodes);
341
342             // attach to new nodes
343
if (newNodes != null) {
344                 nodes = new ArrayList<Reference<Node>>(newNodes.length);
345
346                 for (int i = 0; i < newNodes.length; i++)
347                     nodes.add(new WeakReference<Node>(newNodes[i]));
348             }
349
350             // attach listeners to new nodes
351
attachListeners(nodes);
352         }
353
354         /** Removes itself as a listener from given nodes */
355         private void detachListeners(List<Reference<Node>> nodes) {
356             if (nodes != null) {
357                 Iterator<Reference<Node>> it = nodes.iterator();
358
359                 while (it.hasNext()) {
360                     Node node = it.next().get();
361
362                     if (node != null) {
363                         node.removeNodeListener(listener);
364                     }
365                 }
366             }
367         }
368
369         /** Attach itself as a listener to the given nodes */
370         private void attachListeners(List<Reference<Node>> nodes) {
371             if (nodes != null) {
372                 Iterator<Reference<Node>> it = nodes.iterator();
373
374                 while (it.hasNext()) {
375                     Node node = it.next().get();
376
377                     if (node != null) {
378                         node.addNodeListener(listener);
379                     }
380                 }
381             }
382         }
383
384         protected void finalize() {
385             detachListeners(nodes);
386         }
387     }
388      // end of CookieDelegateAction
389
}
390
Popular Tags