001: /*******************************************************************************
002: * Copyright (c) 2005, 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.internal.navigator.actions;
011:
012: import java.util.ArrayList;
013: import java.util.Collection;
014: import java.util.HashSet;
015: import java.util.Iterator;
016: import java.util.LinkedHashMap;
017: import java.util.LinkedHashSet;
018: import java.util.Map;
019: import java.util.Set;
020:
021: import org.eclipse.core.runtime.Assert;
022: import org.eclipse.core.runtime.IConfigurationElement;
023: import org.eclipse.core.runtime.ISafeRunnable;
024: import org.eclipse.core.runtime.IStatus;
025: import org.eclipse.core.runtime.SafeRunner;
026: import org.eclipse.jface.viewers.IStructuredSelection;
027: import org.eclipse.jface.viewers.StructuredSelection;
028: import org.eclipse.ui.actions.ActionContext;
029: import org.eclipse.ui.internal.navigator.NavigatorPlugin;
030: import org.eclipse.ui.internal.navigator.extensions.NavigatorContentRegistryReader;
031: import org.eclipse.ui.navigator.INavigatorContentService;
032: import org.eclipse.ui.navigator.Priority;
033:
034: /**
035: * Manages descriptors consumed from the 'actionProvider' elements of the
036: * <b>org.eclipse.ui.navigator.navigatorContent</b> extension point.
037: *
038: * @since 3.2
039: *
040: */
041: public class CommonActionDescriptorManager {
042:
043: private static final CommonActionProviderDescriptor[] NO_DESCRIPTORS = new CommonActionProviderDescriptor[0];
044:
045: private static final CommonActionDescriptorManager INSTANCE = new CommonActionDescriptorManager();
046:
047: private CommonActionDescriptorManager() {
048:
049: new ActionProviderRegistry().readRegistry();
050: }
051:
052: /**
053: * @return the singleton instance of the registry
054: */
055: public static CommonActionDescriptorManager getInstance() {
056: return INSTANCE;
057: }
058:
059: /* Provides a map of (ids, CommonActionProviderDescriptor)-pairs. */
060: private final Map dependentDescriptors = new LinkedHashMap();
061:
062: /* Provides a map of (ids, CommonActionProviderDescriptor)-pairs. */
063: private final Set overridingDescriptors = new LinkedHashSet();
064:
065: /* Provides a map of (ids, CommonActionProviderDescriptor)-pairs. */
066: private final Map rootDescriptors = new LinkedHashMap();
067:
068: /**
069: *
070: * @param aDescriptor
071: * A valid descriptor to begin managing.
072: */
073: protected void addActionDescriptor(
074: CommonActionProviderDescriptor aDescriptor) {
075:
076: if (aDescriptor.getDependsOnId() == null) {
077: rootDescriptors
078: .put(aDescriptor.getDefinedId(), aDescriptor);
079: } else {
080: dependentDescriptors.put(aDescriptor.getDefinedId(),
081: aDescriptor);
082: }
083:
084: if (aDescriptor.getOverridesId() != null) {
085: overridingDescriptors.add(aDescriptor);
086: }
087: }
088:
089: /**
090: * Orders the set of availabe descriptors based on the order defined by the
091: * <i>dependsOn</i> attribute from the <actionProvider /> element in
092: * <b>org.eclipse.ui.navigator.navigatorContent</b>
093: *
094: */
095: protected void computeOrdering() {
096: CommonActionProviderDescriptor dependentDescriptor;
097: CommonActionProviderDescriptor requiredDescriptor;
098:
099: CommonActionProviderDescriptor descriptor;
100: CommonActionProviderDescriptor overriddenDescriptor;
101: for (Iterator iter = overridingDescriptors.iterator(); iter
102: .hasNext();) {
103: descriptor = (CommonActionProviderDescriptor) iter.next();
104: if (rootDescriptors
105: .containsKey(descriptor.getOverridesId())) {
106: overriddenDescriptor = (CommonActionProviderDescriptor) rootDescriptors
107: .get(descriptor.getOverridesId());
108: overriddenDescriptor
109: .addOverridingDescriptor(descriptor);
110: } else if (dependentDescriptors.containsKey(descriptor
111: .getOverridesId())) {
112: overriddenDescriptor = (CommonActionProviderDescriptor) dependentDescriptors
113: .get(descriptor.getOverridesId());
114: overriddenDescriptor
115: .addOverridingDescriptor(descriptor);
116: }
117:
118: }
119:
120: Collection unresolvedDependentDescriptors = new ArrayList(
121: dependentDescriptors.values());
122:
123: for (Iterator iter = dependentDescriptors.values().iterator(); iter
124: .hasNext();) {
125: dependentDescriptor = (CommonActionProviderDescriptor) iter
126: .next();
127: requiredDescriptor = (CommonActionProviderDescriptor) rootDescriptors
128: .get(dependentDescriptor.getDependsOnId());
129: if (requiredDescriptor == null) {
130: requiredDescriptor = (CommonActionProviderDescriptor) dependentDescriptors
131: .get(dependentDescriptor.getDependsOnId());
132: }
133: if (requiredDescriptor != null) {
134: requiredDescriptor
135: .addDependentDescriptor(dependentDescriptor);
136: unresolvedDependentDescriptors
137: .remove(dependentDescriptor);
138: }
139:
140: }
141:
142: dependentDescriptors.clear();
143:
144: if (!unresolvedDependentDescriptors.isEmpty()) {
145: StringBuffer errorMessage = new StringBuffer(
146: "There were unresolved dependencies for action provider extensions to a Common Navigator.\n" + //$NON-NLS-1$
147: "Verify that the \"dependsOn\" attribute for each <actionProvider /> element is valid."); //$NON-NLS-1$
148:
149: CommonActionProviderDescriptor[] unresolvedDescriptors = (CommonActionProviderDescriptor[]) unresolvedDependentDescriptors
150: .toArray(new CommonActionProviderDescriptor[unresolvedDependentDescriptors
151: .size()]);
152: for (int i = 0; i < unresolvedDescriptors.length; i++) {
153: errorMessage
154: .append(
155: "\nUnresolved dependency specified for actionProvider: ").append(unresolvedDescriptors[i].getDefinedId()); //$NON-NLS-1$
156: }
157:
158: NavigatorPlugin.log(IStatus.WARNING, 0, errorMessage
159: .toString(), null);
160:
161: }
162: unresolvedDependentDescriptors.clear();
163:
164: }
165:
166: /**
167: *
168: * @param aContentService
169: * The content service to use when filtering action providers;
170: * only action providers bound directly or indirectly will be
171: * returned.
172: * @param aContext
173: * The action context that contains a valid selection.
174: * @return An array of visible, active, and enabled CommonActionProviders.
175: * See <b>org.eclipse.ui.navigator.navigatorContent</b> for the
176: * details of what each of these adjectives implies.
177: */
178: public CommonActionProviderDescriptor[] findRelevantActionDescriptors(
179: INavigatorContentService aContentService,
180: ActionContext aContext) {
181: Assert.isNotNull(aContext);
182: IStructuredSelection structuredSelection = null;
183: if (aContext.getSelection() instanceof IStructuredSelection) {
184: structuredSelection = (IStructuredSelection) aContext
185: .getSelection();
186: } else {
187: structuredSelection = StructuredSelection.EMPTY;
188: }
189:
190: Set blockedProviders = new HashSet();
191: CommonActionProviderDescriptor actionDescriptor = null;
192: Set providers = new LinkedHashSet();
193: for (Iterator providerItr = rootDescriptors.values().iterator(); providerItr
194: .hasNext();) {
195: actionDescriptor = (CommonActionProviderDescriptor) providerItr
196: .next();
197: addProviderIfRelevant(aContentService, structuredSelection,
198: actionDescriptor, providers, blockedProviders);
199: }
200: if (providers.size() > 0) {
201: providers.removeAll(blockedProviders);
202: return (CommonActionProviderDescriptor[]) providers
203: .toArray(new CommonActionProviderDescriptor[providers
204: .size()]);
205: }
206: return NO_DESCRIPTORS;
207: }
208:
209: /**
210: * @param aContentService
211: * @param structuredSelection
212: * @param actionDescriptor
213: * @param providers
214: */
215: private boolean addProviderIfRelevant(
216: INavigatorContentService aContentService,
217: IStructuredSelection structuredSelection,
218: CommonActionProviderDescriptor actionDescriptor,
219: Set providers, Set blockedProviders) {
220: if (isVisible(aContentService, actionDescriptor)
221: && actionDescriptor.isEnabledFor(structuredSelection)) {
222:
223: if (actionDescriptor.hasOverridingDescriptors()) {
224: for (Iterator iter = actionDescriptor
225: .overridingDescriptors(); iter.hasNext();) {
226: CommonActionProviderDescriptor descriptor = (CommonActionProviderDescriptor) iter
227: .next();
228: if (addProviderIfRelevant(aContentService,
229: structuredSelection, descriptor, providers,
230: blockedProviders)) {
231: while (iter.hasNext())
232: blockedProviders.add(iter.next());
233: return true;
234: }
235:
236: }
237: }
238: providers.add(actionDescriptor);
239: if (actionDescriptor.hasDependentDescriptors()) {
240: for (Iterator iter = actionDescriptor
241: .dependentDescriptors(); iter.hasNext();) {
242: addProviderIfRelevant(aContentService,
243: structuredSelection,
244: (CommonActionProviderDescriptor) iter
245: .next(), providers,
246: blockedProviders);
247: }
248: }
249: return true;
250: }
251: return false;
252: }
253:
254: private boolean isVisible(INavigatorContentService aContentService,
255: CommonActionProviderDescriptor descriptor) {
256: if (descriptor.isNested()) {
257: return aContentService.isActive(descriptor.getId())
258: && aContentService.isVisible(descriptor.getId());
259: }
260: return aContentService.getViewerDescriptor()
261: .isVisibleActionExtension(descriptor.getId());
262: }
263:
264: private class ActionProviderRegistry extends
265: NavigatorContentRegistryReader {
266:
267: public void readRegistry() {
268: super .readRegistry();
269: computeOrdering();
270: }
271:
272: protected boolean readElement(IConfigurationElement anElement) {
273: if (TAG_ACTION_PROVIDER.equals(anElement.getName())) {
274: addActionDescriptor(new CommonActionProviderDescriptor(
275: anElement));
276: return true;
277: } else if (TAG_NAVIGATOR_CONTENT
278: .equals(anElement.getName())) {
279:
280: IConfigurationElement[] actionProviders = anElement
281: .getChildren(TAG_ACTION_PROVIDER);
282:
283: if (actionProviders.length > 0) {
284:
285: IConfigurationElement defaultEnablement = null;
286: IConfigurationElement[] inheritedEnablement = anElement
287: .getChildren(TAG_ENABLEMENT);
288: if (inheritedEnablement.length == 0) {
289: inheritedEnablement = anElement
290: .getChildren(TAG_POSSIBLE_CHILDREN);
291: }
292:
293: defaultEnablement = inheritedEnablement.length == 1 ? inheritedEnablement[0]
294: : null;
295:
296: Priority defaultPriority = Priority.get(anElement
297: .getAttribute(ATT_PRIORITY));
298:
299: if (defaultEnablement == null) {
300: NavigatorPlugin
301: .logError(
302: 0,
303: "An actionProvider has been defined as the child " + //$NON-NLS-1$
304: "of a navigatorContent extension that does not specify "
305: + //$NON-NLS-1$
306: "an <enablement/> or <possibleChildren /> expression. Please "
307: + //$NON-NLS-1$
308: "review the documenation and correct this error.",
309: null); //$NON-NLS-1$
310: }
311: for (int i = 0; i < actionProviders.length; i++) {
312: if (defaultEnablement == null) {
313: NavigatorPlugin
314: .logError(
315: 0,
316: "Disabling actionProvider: " + actionProviders[i].getAttribute(ATT_ID), null); //$NON-NLS-1$
317: } else {
318: SafeRunner.run(new AddProviderSafeRunner(
319: actionProviders[i],
320: defaultEnablement, defaultPriority,
321: anElement));
322: }
323: }
324: }
325: return true;
326: }
327: return super .readElement(anElement);
328: }
329:
330: private class AddProviderSafeRunner implements ISafeRunnable {
331:
332: private IConfigurationElement parentElement;
333: private IConfigurationElement defaultEnablement;
334: private IConfigurationElement actionProvider;
335: private Priority defaultPriority;
336:
337: protected AddProviderSafeRunner(
338: IConfigurationElement actionProvider,
339: IConfigurationElement defaultEnablement,
340: Priority defaultPriority,
341: IConfigurationElement parentElement) {
342: this .actionProvider = actionProvider;
343: this .defaultEnablement = defaultEnablement;
344: this .defaultPriority = defaultPriority;
345: this .parentElement = parentElement;
346: }
347:
348: /* (non-Javadoc)
349: * @see org.eclipse.core.runtime.ISafeRunnable#run()
350: */
351: public void run() throws Exception {
352: addActionDescriptor(new CommonActionProviderDescriptor(
353: actionProvider, defaultEnablement,
354: defaultPriority, parentElement
355: .getAttribute(ATT_ID), true));
356: }
357:
358: /* (non-Javadoc)
359: * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable)
360: */
361: public void handleException(Throwable t) {
362: NavigatorPlugin
363: .logError(
364: 0,
365: "Recovering from error while parsing actionProviders.", t); //$NON-NLS-1$
366: }
367:
368: }
369: }
370:
371: }
|