001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.actions;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.Iterator;
015: import java.util.List;
016:
017: import org.eclipse.core.resources.IResource;
018: import org.eclipse.core.resources.mapping.ResourceMapping;
019: import org.eclipse.core.resources.mapping.ResourceMappingContext;
020: import org.eclipse.core.resources.mapping.ResourceTraversal;
021: import org.eclipse.core.runtime.CoreException;
022: import org.eclipse.core.runtime.IAdaptable;
023: import org.eclipse.core.runtime.IAdapterManager;
024: import org.eclipse.core.runtime.NullProgressMonitor;
025: import org.eclipse.core.runtime.Platform;
026: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
027:
028: /**
029: * The abstract superclass for resource-based actions that listen to selection
030: * change events. This implementation tracks the current selection (see
031: * <code>getStructuredSelection</code>) and provides a convenient place to
032: * monitor selection changes that could affect the availability of the action.
033: * <p>
034: * Subclasses must implement the following <code>IAction</code> method:
035: * <ul>
036: * <li><code>run</code> - to do the action's work</li>
037: * </ul>
038: * </p>
039: * <p>
040: * Subclasses may extend the <code>updateSelection</code> method to update the
041: * action determine its availability based on the current selection.
042: * </p>
043: * <p>
044: * The object instantiating the subclass is responsible for registering the
045: * instance with a selection provider. Alternatively, the object can notify the
046: * subclass instance directly of a selection change using the methods:
047: * <ul>
048: * <li><code>selectionChanged(IStructuredSelection)</code> - passing the
049: * selection</li>
050: * <li><code>selectionChanged(ISelectionChangedEvent)</code> - passing the
051: * selection change event</li>
052: * </ul>
053: * </p>
054: */
055: public abstract class SelectionListenerAction extends
056: BaseSelectionListenerAction {
057: /**
058: * Empty list that is immutable.
059: */
060: private static final List EMPTY_LIST = Arrays.asList(new Object[0]);
061:
062: /**
063: * Indicates whether the selection has changes since <code>resources</code>
064: * and <code>nonResources</code> were computed.
065: */
066: private boolean selectionDirty = true;
067:
068: /**
069: * The list of resource elements in the current selection (element type:
070: * <code>IResource</code>); meaningful only when
071: * <code>selectionDirty == false</code>.
072: */
073: private List resources;
074:
075: /**
076: * The list of non-resource elements in the current selection (element type:
077: * <code>Object</code>); meaningful only when
078: * <code>selectionDirty == false</code>.
079: */
080: private List nonResources;
081:
082: /**
083: * Creates a new action with the given text.
084: *
085: * @param text
086: * the string used as the text for the action, or
087: * <code>null</code> if there is no text
088: */
089: protected SelectionListenerAction(String text) {
090: super (text);
091: }
092:
093: /**
094: * The <code>SelectionListenerAction</code> implementation of this
095: * <code>BaseSelectionListenerAction</code> method clears the cached
096: * resources and non-resources.
097: */
098: protected void clearCache() {
099: selectionDirty = true;
100: // clear out the lists in case computeResources does not get called
101: // immediately
102: resources = null;
103: nonResources = null;
104: }
105:
106: /**
107: * Extracts <code>IResource</code>s from the current selection and adds
108: * them to the resources list, and the rest into the non-resources list.
109: */
110: private final void computeResources() {
111: resources = null;
112: nonResources = null;
113:
114: for (Iterator e = getStructuredSelection().iterator(); e
115: .hasNext();) {
116: Object next = e.next();
117: if (next instanceof IResource) {
118: if (resources == null) {
119: // assume selection contains mostly resources most times
120: resources = new ArrayList(getStructuredSelection()
121: .size());
122: }
123: resources.add(next);
124: continue;
125: } else if (next instanceof IAdaptable) {
126: Object resource = ((IAdaptable) next)
127: .getAdapter(IResource.class);
128: if (resource != null) {
129: if (resources == null) {
130: // assume selection contains mostly resources most times
131: resources = new ArrayList(
132: getStructuredSelection().size());
133: }
134: resources.add(resource);
135: continue;
136: }
137: } else {
138:
139: boolean resourcesFoundForThisSelection = false;
140:
141: IAdapterManager adapterManager = Platform
142: .getAdapterManager();
143: ResourceMapping mapping = (ResourceMapping) adapterManager
144: .getAdapter(next, ResourceMapping.class);
145:
146: if (mapping != null) {
147:
148: ResourceTraversal[] traversals = null;
149: try {
150: traversals = mapping.getTraversals(
151: ResourceMappingContext.LOCAL_CONTEXT,
152: new NullProgressMonitor());
153: } catch (CoreException exception) {
154: IDEWorkbenchPlugin.log(exception
155: .getLocalizedMessage(), exception
156: .getStatus());
157: }
158:
159: if (traversals != null) {
160:
161: for (int i = 0; i < traversals.length; i++) {
162:
163: IResource[] traversalResources = traversals[i]
164: .getResources();
165:
166: if (traversalResources != null) {
167:
168: resourcesFoundForThisSelection = true;
169:
170: if (resources == null) {
171: resources = new ArrayList(
172: getStructuredSelection()
173: .size());
174: }
175:
176: for (int j = 0; j < traversalResources.length; j++) {
177: resources
178: .add(traversalResources[j]);
179: }// for
180:
181: }// if
182:
183: }// for
184:
185: }// if
186:
187: }// if
188:
189: if (resourcesFoundForThisSelection) {
190: continue;
191: }
192: }
193:
194: if (nonResources == null) {
195: // assume selection contains mostly resources most times
196: nonResources = new ArrayList(1);
197: }
198: nonResources.add(next);
199: }
200: }
201:
202: /**
203: * Returns the elements in the current selection that are not
204: * <code>IResource</code>s.
205: *
206: * @return list of elements (element type: <code>Object</code>)
207: */
208: protected List getSelectedNonResources() {
209: // recompute if selection has changed.
210: if (selectionDirty) {
211: computeResources();
212: selectionDirty = false;
213: }
214:
215: if (nonResources == null) {
216: return EMPTY_LIST;
217: }
218:
219: return nonResources;
220: }
221:
222: /**
223: * Returns the elements in the current selection that are
224: * <code>IResource</code>s.
225: *
226: * @return list of resource elements (element type: <code>IResource</code>)
227: */
228: protected List getSelectedResources() {
229: // recompute if selection has changed.
230: if (selectionDirty) {
231: computeResources();
232: selectionDirty = false;
233: }
234:
235: if (resources == null) {
236: return EMPTY_LIST;
237: }
238: return resources;
239: }
240:
241: /**
242: * Returns whether the type of the given resource is among those in the
243: * given resource type mask.
244: *
245: * @param resource
246: * the resource
247: * @param resourceMask
248: * a bitwise OR of resource types: <code>IResource</code>.{<code>FILE</code>,
249: * <code>FOLDER</code>, <code>PROJECT</code>,
250: * <code>ROOT</code>}
251: * @return <code>true</code> if the resource type matches, and
252: * <code>false</code> otherwise
253: * @see IResource
254: */
255: protected boolean resourceIsType(IResource resource,
256: int resourceMask) {
257: return (resource.getType() & resourceMask) != 0;
258: }
259:
260: /**
261: * Returns whether the current selection consists entirely of resources
262: * whose types are among those in the given resource type mask.
263: *
264: * @param resourceMask
265: * a bitwise OR of resource types: <code>IResource</code>.{<code>FILE</code>,
266: * <code>FOLDER</code>, <code>PROJECT</code>,
267: * <code>ROOT</code>}
268: * @return <code>true</code> if all resources in the current selection are
269: * of the specified types or if the current selection is empty, and
270: * <code>false</code> if some elements are resources of a
271: * different type or not resources
272: * @see IResource
273: */
274: protected boolean selectionIsOfType(int resourceMask) {
275: if (getSelectedNonResources().size() > 0) {
276: return false;
277: }
278:
279: for (Iterator e = getSelectedResources().iterator(); e
280: .hasNext();) {
281: IResource next = (IResource) e.next();
282: if (!resourceIsType(next, resourceMask)) {
283: return false;
284: }
285: }
286: return true;
287: }
288:
289: }
|