| /******************************************************************************* |
| * Copyright (c) 2005, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.navigator.actions; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.ui.actions.ActionContext; |
| import org.eclipse.ui.internal.navigator.NavigatorPlugin; |
| import org.eclipse.ui.internal.navigator.extensions.NavigatorContentRegistryReader; |
| import org.eclipse.ui.navigator.INavigatorContentService; |
| import org.eclipse.ui.navigator.Priority; |
| |
| /** |
| * Manages descriptors consumed from the 'actionProvider' elements of the |
| * <b>org.eclipse.ui.navigator.navigatorContent</b> extension point. |
| * |
| * @since 3.2 |
| * |
| */ |
| public class CommonActionDescriptorManager { |
| |
| private static final CommonActionProviderDescriptor[] NO_DESCRIPTORS = new CommonActionProviderDescriptor[0]; |
| |
| private static final CommonActionDescriptorManager INSTANCE = new CommonActionDescriptorManager(); |
| |
| private CommonActionDescriptorManager() { |
| |
| new ActionProviderRegistry().readRegistry(); |
| } |
| |
| /** |
| * @return the singleton instance of the registry |
| */ |
| public static CommonActionDescriptorManager getInstance() { |
| return INSTANCE; |
| } |
| |
| /* Provides a map of (ids, CommonActionProviderDescriptor)-pairs. */ |
| private final Map dependentDescriptors = new LinkedHashMap(); |
| |
| /* Provides a map of (ids, CommonActionProviderDescriptor)-pairs. */ |
| private final Set overridingDescriptors = new LinkedHashSet(); |
| |
| /* Provides a map of (ids, CommonActionProviderDescriptor)-pairs. */ |
| private final Map rootDescriptors = new LinkedHashMap(); |
| |
| /** |
| * |
| * @param aDescriptor |
| * A valid descriptor to begin managing. |
| */ |
| protected void addActionDescriptor( |
| CommonActionProviderDescriptor aDescriptor) { |
| |
| if (aDescriptor.getDependsOnId() == null) { |
| rootDescriptors.put(aDescriptor.getDefinedId(), aDescriptor); |
| } else { |
| dependentDescriptors.put(aDescriptor.getDefinedId(), aDescriptor); |
| } |
| |
| if (aDescriptor.getOverridesId() != null) { |
| overridingDescriptors.add(aDescriptor); |
| } |
| } |
| |
| /** |
| * Orders the set of availabe descriptors based on the order defined by the |
| * <i>dependsOn</i> attribute from the <actionProvider /> element in |
| * <b>org.eclipse.ui.navigator.navigatorContent</b> |
| * |
| */ |
| protected void computeOrdering() { |
| CommonActionProviderDescriptor dependentDescriptor; |
| CommonActionProviderDescriptor requiredDescriptor; |
| |
| CommonActionProviderDescriptor descriptor; |
| CommonActionProviderDescriptor overriddenDescriptor; |
| for (Iterator iter = overridingDescriptors.iterator(); iter.hasNext();) { |
| descriptor = (CommonActionProviderDescriptor) iter.next(); |
| if (rootDescriptors.containsKey(descriptor.getOverridesId())) { |
| overriddenDescriptor = (CommonActionProviderDescriptor) rootDescriptors |
| .get(descriptor.getOverridesId()); |
| overriddenDescriptor.addOverridingDescriptor(descriptor); |
| } else if (dependentDescriptors.containsKey(descriptor |
| .getOverridesId())) { |
| overriddenDescriptor = (CommonActionProviderDescriptor) dependentDescriptors |
| .get(descriptor.getOverridesId()); |
| overriddenDescriptor.addOverridingDescriptor(descriptor); |
| } |
| |
| } |
| |
| Collection unresolvedDependentDescriptors = new ArrayList( |
| dependentDescriptors.values()); |
| |
| for (Iterator iter = dependentDescriptors.values().iterator(); iter |
| .hasNext();) { |
| dependentDescriptor = (CommonActionProviderDescriptor) iter.next(); |
| requiredDescriptor = (CommonActionProviderDescriptor) rootDescriptors |
| .get(dependentDescriptor.getDependsOnId()); |
| if (requiredDescriptor == null) { |
| requiredDescriptor = (CommonActionProviderDescriptor) dependentDescriptors |
| .get(dependentDescriptor.getDependsOnId()); |
| } |
| if (requiredDescriptor != null) { |
| requiredDescriptor.addDependentDescriptor(dependentDescriptor); |
| unresolvedDependentDescriptors.remove(dependentDescriptor); |
| } |
| |
| } |
| |
| dependentDescriptors.clear(); |
| |
| if (!unresolvedDependentDescriptors.isEmpty()) { |
| StringBuffer errorMessage = new StringBuffer( |
| "There were unresolved dependencies for action provider extensions to a Common Navigator.\n" + //$NON-NLS-1$ |
| "Verify that the \"dependsOn\" attribute for each <actionProvider /> element is valid."); //$NON-NLS-1$ |
| |
| CommonActionProviderDescriptor[] unresolvedDescriptors = (CommonActionProviderDescriptor[]) unresolvedDependentDescriptors |
| .toArray(new CommonActionProviderDescriptor[unresolvedDependentDescriptors |
| .size()]); |
| for (int i = 0; i < unresolvedDescriptors.length; i++) { |
| errorMessage |
| .append( |
| "\nUnresolved dependency specified for actionProvider: ").append(unresolvedDescriptors[i].getDefinedId()); //$NON-NLS-1$ |
| } |
| |
| NavigatorPlugin.log(IStatus.WARNING, 0, errorMessage.toString(), |
| null); |
| |
| } |
| unresolvedDependentDescriptors.clear(); |
| |
| } |
| |
| /** |
| * |
| * @param aContentService |
| * The content service to use when filtering action providers; |
| * only action providers bound directly or indirectly will be |
| * returned. |
| * @param aContext |
| * The action context that contains a valid selection. |
| * @return An array of visible, active, and enabled CommonActionProviders. |
| * See <b>org.eclipse.ui.navigator.navigatorContent</b> for the |
| * details of what each of these adjectives implies. |
| */ |
| public CommonActionProviderDescriptor[] findRelevantActionDescriptors( |
| INavigatorContentService aContentService, ActionContext aContext) { |
| Assert.isNotNull(aContext); |
| IStructuredSelection structuredSelection = null; |
| if (aContext.getSelection() instanceof IStructuredSelection) { |
| structuredSelection = (IStructuredSelection) aContext |
| .getSelection(); |
| } else { |
| structuredSelection = StructuredSelection.EMPTY; |
| } |
| |
| Set blockedProviders = new HashSet(); |
| CommonActionProviderDescriptor actionDescriptor = null; |
| Set providers = new LinkedHashSet(); |
| for (Iterator providerItr = rootDescriptors.values().iterator(); providerItr |
| .hasNext();) { |
| actionDescriptor = (CommonActionProviderDescriptor) providerItr |
| .next(); |
| addProviderIfRelevant(aContentService, structuredSelection, |
| actionDescriptor, providers, blockedProviders); |
| } |
| if (providers.size() > 0) { |
| providers.removeAll(blockedProviders); |
| return (CommonActionProviderDescriptor[]) providers |
| .toArray(new CommonActionProviderDescriptor[providers |
| .size()]); |
| } |
| return NO_DESCRIPTORS; |
| } |
| |
| /** |
| * @param aContentService |
| * @param structuredSelection |
| * @param actionDescriptor |
| * @param providers |
| */ |
| private boolean addProviderIfRelevant( |
| INavigatorContentService aContentService, |
| IStructuredSelection structuredSelection, |
| CommonActionProviderDescriptor actionDescriptor, Set providers, Set blockedProviders) { |
| if (isVisible(aContentService, actionDescriptor) |
| && actionDescriptor.isEnabledFor(structuredSelection)) { |
| |
| if(actionDescriptor.hasOverridingDescriptors()) { |
| for (Iterator iter = actionDescriptor.overridingDescriptors(); iter.hasNext();) { |
| CommonActionProviderDescriptor descriptor = (CommonActionProviderDescriptor) iter.next(); |
| if(addProviderIfRelevant(aContentService, structuredSelection, descriptor, providers, blockedProviders)) { |
| while(iter.hasNext()) |
| blockedProviders.add(iter.next()); |
| return true; |
| } |
| |
| } |
| } |
| providers.add(actionDescriptor); |
| if (actionDescriptor.hasDependentDescriptors()) { |
| for (Iterator iter = actionDescriptor.dependentDescriptors(); iter |
| .hasNext();) { |
| addProviderIfRelevant(aContentService, structuredSelection, |
| (CommonActionProviderDescriptor) iter.next(), |
| providers, blockedProviders); |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean isVisible(INavigatorContentService aContentService, |
| CommonActionProviderDescriptor descriptor) { |
| if (descriptor.isNested()) { |
| return aContentService.isActive(descriptor.getId()) |
| && aContentService.isVisible(descriptor.getId()); |
| } |
| return aContentService.getViewerDescriptor().isVisibleActionExtension( |
| descriptor.getId()); |
| } |
| |
| private class ActionProviderRegistry extends NavigatorContentRegistryReader { |
| |
| public void readRegistry() { |
| super.readRegistry(); |
| computeOrdering(); |
| } |
| |
| protected boolean readElement(IConfigurationElement anElement) { |
| if (TAG_ACTION_PROVIDER.equals(anElement.getName())) { |
| addActionDescriptor(new CommonActionProviderDescriptor( |
| anElement)); |
| return true; |
| } else if (TAG_NAVIGATOR_CONTENT.equals(anElement.getName())) { |
| |
| IConfigurationElement[] actionProviders = anElement.getChildren(TAG_ACTION_PROVIDER); |
| |
| if (actionProviders.length > 0) { |
| |
| IConfigurationElement defaultEnablement = null; |
| IConfigurationElement[] inheritedEnablement = anElement.getChildren(TAG_ENABLEMENT); |
| if (inheritedEnablement.length == 0) { |
| inheritedEnablement = anElement.getChildren(TAG_POSSIBLE_CHILDREN); |
| } |
| |
| defaultEnablement = inheritedEnablement.length == 1 ? inheritedEnablement[0] : null; |
| |
| Priority defaultPriority = Priority.get(anElement.getAttribute(ATT_PRIORITY)); |
| |
| |
| if(defaultEnablement == null) { |
| NavigatorPlugin.logError(0, |
| "An actionProvider has been defined as the child " + //$NON-NLS-1$ |
| "of a navigatorContent extension that does not specify " + //$NON-NLS-1$ |
| "an <enablement/> or <possibleChildren /> expression. Please " + //$NON-NLS-1$ |
| "review the documenation and correct this error.", null); //$NON-NLS-1$ |
| } |
| for (int i = 0; i < actionProviders.length; i++) { |
| if(defaultEnablement == null) { |
| NavigatorPlugin.logError(0, |
| "Disabling actionProvider: " + actionProviders[i].getAttribute(ATT_ID), null); //$NON-NLS-1$ |
| } else { |
| SafeRunner.run(new AddProviderSafeRunner(actionProviders[i], defaultEnablement, defaultPriority, anElement)); |
| } |
| } |
| } |
| return true; |
| } |
| return super.readElement(anElement); |
| } |
| |
| private class AddProviderSafeRunner implements ISafeRunnable { |
| |
| private IConfigurationElement parentElement; |
| private IConfigurationElement defaultEnablement; |
| private IConfigurationElement actionProvider; |
| private Priority defaultPriority; |
| |
| protected AddProviderSafeRunner(IConfigurationElement actionProvider, |
| IConfigurationElement defaultEnablement, |
| Priority defaultPriority, |
| IConfigurationElement parentElement) { |
| this.actionProvider = actionProvider; |
| this.defaultEnablement = defaultEnablement; |
| this.defaultPriority = defaultPriority; |
| this.parentElement = parentElement; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.runtime.ISafeRunnable#run() |
| */ |
| public void run() throws Exception { |
| addActionDescriptor(new CommonActionProviderDescriptor( |
| actionProvider, defaultEnablement, defaultPriority, parentElement |
| .getAttribute(ATT_ID), true)); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) |
| */ |
| public void handleException(Throwable t) { |
| NavigatorPlugin.logError(0, "Recovering from error while parsing actionProviders.", t); //$NON-NLS-1$ |
| } |
| |
| |
| } |
| } |
| |
| |
| } |