KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > SelectionEnabler


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.lang.reflect.Method JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18
19 import org.eclipse.core.runtime.IAdaptable;
20 import org.eclipse.core.runtime.IConfigurationElement;
21 import org.eclipse.core.runtime.Platform;
22 import org.eclipse.jface.viewers.ISelection;
23 import org.eclipse.jface.viewers.IStructuredSelection;
24 import org.eclipse.jface.viewers.StructuredSelection;
25 import org.eclipse.ui.actions.SimpleWildcardTester;
26 import org.eclipse.ui.internal.ActionExpression;
27 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
28 import org.eclipse.ui.internal.util.Util;
29 import org.eclipse.ui.model.IWorkbenchAdapter;
30 import org.osgi.framework.Bundle;
31
32 /**
33  * Determines the enablement status given a selection. This calculation is done
34  * based on the definition of the <code>enablesFor</code> attribute,
35  * <code>enablement</code> element, and the <code>selection</code> element
36  * found in the <code>IConfigurationElement</code> provided.
37  * <p>
38  * This class can be instantiated by clients. It is not intended to be extended.
39  * </p>
40  *
41  * @since 3.0
42  *
43  * Note: The dependency on org.eclipse.jface.text for ITextSelection must be
44  * severed It may be possible to do with IActionFilter generic workbench
45  * registers IActionFilter for "size" property against IStructuredSelection
46  * workbench text registers IActionFilter for "size" property against
47  * ITextSelection code here: sel.getAdapter(IActionFilter.class) As an interim
48  * solution, use reflection to access selections implementing ITextSelection
49  */

50 public final class SelectionEnabler {
51
52     /* package */static class SelectionClass {
53         public String JavaDoc className;
54
55         public String JavaDoc nameFilter;
56
57         public boolean recursive;
58     }
59
60     public static final int ANY_NUMBER = -2;
61
62     /**
63      * The constant integer hash code value meaning the hash code has not yet
64      * been computed.
65      */

66     private static final int HASH_CODE_NOT_COMPUTED = -1;
67
68     /**
69      * A factor for computing the hash code for all schemes.
70      */

71     private static final int HASH_FACTOR = 89;
72
73     /**
74      * The seed for the hash code for all schemes.
75      */

76     private static final int HASH_INITIAL = SelectionEnabler.class.getName()
77             .hashCode();
78
79     /**
80      * Cached value of <code>org.eclipse.jface.text.ITextSelection.class</code>;
81      * <code>null</code> if not initialized or not present.
82      */

83     private static Class JavaDoc iTextSelectionClass = null;
84
85     /**
86      * Hard-wired id of the JFace text plug-in (not on pre-req chain).
87      */

88     private static final String JavaDoc JFACE_TEXT_PLUG_IN = "org.eclipse.jface.text"; //$NON-NLS-1$
89

90     public static final int MULTIPLE = -5;
91
92     public static final int NONE = -4;
93
94     public static final int NONE_OR_ONE = -3;
95
96     public static final int ONE_OR_MORE = -1;
97
98     /**
99      * Hard-wired fully qualified name of the text selection class (not on
100      * pre-req chain).
101      */

102     private static final String JavaDoc TEXT_SELECTION_CLASS = "org.eclipse.jface.text.ITextSelection"; //$NON-NLS-1$
103

104     /**
105      * Indicates whether the JFace text plug-in is even around. Without the
106      * JFace text plug-in, text selections are moot.
107      */

108     private static boolean textSelectionPossible = true;
109
110     public static final int UNKNOWN = 0;
111
112     /**
113      * Returns <code>ITextSelection.class</code> or <code>null</code> if the
114      * class is not available.
115      *
116      * @return <code>ITextSelection.class</code> or <code>null</code> if
117      * class not available
118      * @since 3.0
119      */

120     private static Class JavaDoc getTextSelectionClass() {
121         if (iTextSelectionClass != null) {
122             // tried before and succeeded
123
return iTextSelectionClass;
124         }
125         if (!textSelectionPossible) {
126             // tried before and failed
127
return null;
128         }
129
130         // JFace text plug-in is not on prereq chain of generic wb plug-in
131
// hence: ITextSelection.class won't compile
132
// and Class.forName("org.eclipse.jface.text.ITextSelection") won't find
133
// it need to be trickier...
134
Bundle bundle = Platform.getBundle(JFACE_TEXT_PLUG_IN);
135         if (bundle == null || bundle.getState() == Bundle.UNINSTALLED) {
136             // JFace text plug-in is not around, or has already
137
// been removed, assume that it will never be around
138
textSelectionPossible = false;
139             return null;
140         }
141
142         // plug-in is around
143
// it's not our job to activate the plug-in
144
if (bundle.getState() == Bundle.INSTALLED) {
145             // assume it might come alive later
146
textSelectionPossible = true;
147             return null;
148         }
149
150         try {
151             Class JavaDoc c = bundle.loadClass(TEXT_SELECTION_CLASS);
152             // remember for next time
153
iTextSelectionClass = c;
154             return iTextSelectionClass;
155         } catch (ClassNotFoundException JavaDoc e) {
156             // unable to load ITextSelection - sounds pretty serious
157
// treat as if JFace text plug-in were unavailable
158
textSelectionPossible = false;
159             return null;
160         }
161     }
162
163     /**
164      * Verifies that the given name matches the given wildcard filter. Returns
165      * true if it does.
166      *
167      * @param name
168      * @param filter
169      * @return <code>true</code> if there is a match
170      */

171     public static boolean verifyNameMatch(String JavaDoc name, String JavaDoc filter) {
172         return SimpleWildcardTester.testWildcardIgnoreCase(filter, name);
173     }
174
175     private List JavaDoc classes = new ArrayList JavaDoc();
176
177     private ActionExpression enablementExpression;
178
179     /**
180      * The hash code for this object. This value is computed lazily, and marked
181      * as invalid when one of the values on which it is based changes.
182      */

183     private transient int hashCode = HASH_CODE_NOT_COMPUTED;
184
185     private int mode = UNKNOWN;
186
187     /**
188      * Create a new instance of the receiver.
189      *
190      * @param configElement
191      */

192     public SelectionEnabler(IConfigurationElement configElement) {
193         super();
194         if (configElement == null) {
195             throw new IllegalArgumentException JavaDoc();
196         }
197         parseClasses(configElement);
198     }
199
200     public final boolean equals(final Object JavaDoc object) {
201         if (object instanceof SelectionEnabler) {
202             final SelectionEnabler that = (SelectionEnabler) object;
203             return Util.equals(this.classes, that.classes)
204                     && Util.equals(this.enablementExpression,
205                             that.enablementExpression)
206                     && Util.equals(this.mode, that.mode);
207         }
208
209         return false;
210     }
211
212     /**
213      * Computes the hash code for this object based on the id.
214      *
215      * @return The hash code for this object.
216      */

217     public final int hashCode() {
218         if (hashCode == HASH_CODE_NOT_COMPUTED) {
219             hashCode = HASH_INITIAL * HASH_FACTOR + Util.hashCode(classes);
220             hashCode = hashCode * HASH_FACTOR
221                     + Util.hashCode(enablementExpression);
222             hashCode = hashCode * HASH_FACTOR + Util.hashCode(mode);
223             if (hashCode == HASH_CODE_NOT_COMPUTED) {
224                 hashCode++;
225             }
226         }
227         return hashCode;
228     }
229
230     /**
231      * Returns true if given structured selection matches the conditions
232      * specified in the registry for this action.
233      */

234     private boolean isEnabledFor(ISelection sel) {
235         Object JavaDoc obj = sel;
236         int count = sel.isEmpty() ? 0 : 1;
237
238         if (verifySelectionCount(count) == false) {
239             return false;
240         }
241
242         // Compare selection to enablement expression.
243
if (enablementExpression != null) {
244             return enablementExpression.isEnabledFor(obj);
245         }
246
247         // Compare selection to class requirements.
248
if (classes.isEmpty()) {
249             return true;
250         }
251         if (obj instanceof IAdaptable) {
252             IAdaptable element = (IAdaptable) obj;
253             if (verifyElement(element) == false) {
254                 return false;
255             }
256         } else {
257             return false;
258         }
259
260         return true;
261     }
262
263     /**
264      * Returns true if given text selection matches the conditions specified in
265      * the registry for this action.
266      */

267     private boolean isEnabledFor(ISelection sel, int count) {
268         if (verifySelectionCount(count) == false) {
269             return false;
270         }
271
272         // Compare selection to enablement expression.
273
if (enablementExpression != null) {
274             return enablementExpression.isEnabledFor(sel);
275         }
276
277         // Compare selection to class requirements.
278
if (classes.isEmpty()) {
279             return true;
280         }
281         for (int i = 0; i < classes.size(); i++) {
282             SelectionClass sc = (SelectionClass) classes.get(i);
283             if (verifyClass(sel, sc.className)) {
284                 return true;
285             }
286         }
287         return false;
288     }
289
290     /**
291      * Returns true if given structured selection matches the conditions
292      * specified in the registry for this action.
293      */

294     private boolean isEnabledFor(IStructuredSelection ssel) {
295         int count = ssel.size();
296
297         if (verifySelectionCount(count) == false) {
298             return false;
299         }
300
301         // Compare selection to enablement expression.
302
if (enablementExpression != null) {
303             return enablementExpression.isEnabledFor(ssel);
304         }
305
306         // Compare selection to class requirements.
307
if (classes.isEmpty()) {
308             return true;
309         }
310         for (Iterator JavaDoc elements = ssel.iterator(); elements.hasNext();) {
311             Object JavaDoc obj = elements.next();
312             if (obj instanceof IAdaptable) {
313                 IAdaptable element = (IAdaptable) obj;
314                 if (verifyElement(element) == false) {
315                     return false;
316                 }
317             } else {
318                 return false;
319             }
320         }
321
322         return true;
323     }
324
325     /**
326      * Check if the receiver is enabled for the given selection.
327      *
328      * @param selection
329      * @return <code>true</code> if the given selection matches the conditions
330      * specified in <code>IConfirgurationElement</code>.
331      */

332     public boolean isEnabledForSelection(ISelection selection) {
333         // Optimize it.
334
if (mode == UNKNOWN) {
335             return false;
336         }
337
338         // Handle undefined selections.
339
if (selection == null) {
340             selection = StructuredSelection.EMPTY;
341         }
342
343         // According to the dictionary, a selection is "one that
344
// is selected", or "a collection of selected things".
345
// In reflection of this, we deal with one or a collection.
346

347         // special case: structured selections
348
if (selection instanceof IStructuredSelection) {
349             return isEnabledFor((IStructuredSelection) selection);
350         }
351
352         // special case: text selections
353
// Code should read
354
// if (selection instanceof ITextSelection) {
355
// int count = ((ITextSelection) selection).getLength();
356
// return isEnabledFor(selection, count);
357
// }
358
// use Java reflection to avoid dependence of org.eclipse.jface.text
359
// which is in an optional part of the generic workbench
360
Class JavaDoc tselClass = getTextSelectionClass();
361         if (tselClass != null && tselClass.isInstance(selection)) {
362             try {
363                 Method JavaDoc m = tselClass.getDeclaredMethod(
364                         "getLength", new Class JavaDoc[0]); //$NON-NLS-1$
365
Object JavaDoc r = m.invoke(selection, new Object JavaDoc[0]);
366                 if (r instanceof Integer JavaDoc) {
367                     return isEnabledFor(selection, ((Integer JavaDoc) r).intValue());
368                 }
369                 // should not happen - but enable if it does
370
return true;
371             } catch (NoSuchMethodException JavaDoc e) {
372                 // should not happen - fall through if it does
373
} catch (IllegalAccessException JavaDoc e) {
374                 // should not happen - fall through if it does
375
} catch (InvocationTargetException JavaDoc e) {
376                 // should not happen - fall through if it does
377
}
378         }
379
380         // all other cases
381
return isEnabledFor(selection);
382     }
383
384     /**
385      * Parses registry element to extract mode and selection elements that will
386      * be used for verification.
387      */

388     private void parseClasses(IConfigurationElement config) {
389         // Get enables for.
390
String JavaDoc enablesFor = config
391                 .getAttribute(IWorkbenchRegistryConstants.ATT_ENABLES_FOR);
392         if (enablesFor == null) {
393             enablesFor = "*"; //$NON-NLS-1$
394
}
395         if (enablesFor.equals("*")) { //$NON-NLS-1$
396
mode = ANY_NUMBER;
397         } else if (enablesFor.equals("?")) { //$NON-NLS-1$
398
mode = NONE_OR_ONE;
399         } else if (enablesFor.equals("!")) { //$NON-NLS-1$
400
mode = NONE;
401         } else if (enablesFor.equals("+")) { //$NON-NLS-1$
402
mode = ONE_OR_MORE;
403         } else if (enablesFor.equals("multiple") //$NON-NLS-1$
404
|| enablesFor.equals("2+")) { //$NON-NLS-1$
405
mode = MULTIPLE;
406         } else {
407             try {
408                 mode = Integer.parseInt(enablesFor);
409             } catch (NumberFormatException JavaDoc e) {
410                 mode = UNKNOWN;
411             }
412         }
413
414         // Get enablement block.
415
IConfigurationElement[] children = config
416                 .getChildren(IWorkbenchRegistryConstants.TAG_ENABLEMENT);
417         if (children.length > 0) {
418             enablementExpression = new ActionExpression(children[0]);
419             return;
420         }
421
422         // Get selection block.
423
children = config
424                 .getChildren(IWorkbenchRegistryConstants.TAG_SELECTION);
425         if (children.length > 0) {
426             classes = new ArrayList JavaDoc();
427             for (int i = 0; i < children.length; i++) {
428                 IConfigurationElement sel = children[i];
429                 String JavaDoc cname = sel
430                         .getAttribute(IWorkbenchRegistryConstants.ATT_CLASS);
431                 String JavaDoc name = sel
432                         .getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
433                 SelectionClass sclass = new SelectionClass();
434                 sclass.className = cname;
435                 sclass.nameFilter = name;
436                 classes.add(sclass);
437             }
438         }
439     }
440
441     /**
442      * Verifies if the element is an instance of a class with a given class
443      * name. If direct match fails, implementing interfaces will be tested, then
444      * recursively all superclasses and their interfaces.
445      */

446     private boolean verifyClass(Object JavaDoc element, String JavaDoc className) {
447         Class JavaDoc eclass = element.getClass();
448         Class JavaDoc clazz = eclass;
449         boolean match = false;
450         while (clazz != null) {
451             // test the class itself
452
if (clazz.getName().equals(className)) {
453                 match = true;
454                 break;
455             }
456             // test all the interfaces it implements
457
Class JavaDoc[] interfaces = clazz.getInterfaces();
458             for (int i = 0; i < interfaces.length; i++) {
459                 if (interfaces[i].getName().equals(className)) {
460                     match = true;
461                     break;
462                 }
463             }
464             if (match == true) {
465                 break;
466             }
467             // get the superclass
468
clazz = clazz.getSuperclass();
469         }
470         return match;
471     }
472
473     /**
474      * Verifies if the given element matches one of the selection requirements.
475      * Element must at least pass the type test, and optionally wildcard name
476      * match.
477      */

478     private boolean verifyElement(IAdaptable element) {
479         if (classes.isEmpty()) {
480             return true;
481         }
482         for (int i = 0; i < classes.size(); i++) {
483             SelectionClass sc = (SelectionClass) classes.get(i);
484             if (verifyClass(element, sc.className) == false) {
485                 continue;
486             }
487             if (sc.nameFilter == null) {
488                 return true;
489             }
490             IWorkbenchAdapter de = (IWorkbenchAdapter) Util.getAdapter(element, IWorkbenchAdapter.class);
491             if ((de != null)
492                     && verifyNameMatch(de.getLabel(element), sc.nameFilter)) {
493                 return true;
494             }
495         }
496         return false;
497     }
498
499     /**
500      * Compare selection count with requirements.
501      */

502     private boolean verifySelectionCount(int count) {
503         if (count > 0 && mode == NONE) {
504             return false;
505         }
506         if (count == 0 && mode == ONE_OR_MORE) {
507             return false;
508         }
509         if (count > 1 && mode == NONE_OR_ONE) {
510             return false;
511         }
512         if (count < 2 && mode == MULTIPLE) {
513             return false;
514         }
515         if (mode > 0 && count != mode) {
516             return false;
517         }
518         return true;
519     }
520 }
521
Popular Tags