blob: 2c3542dd0413210e381593c0e4e4bcc24b0fcc9f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 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.extensions;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.WeakHashMap;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.internal.navigator.NavigatorPlugin;
import org.eclipse.ui.internal.navigator.VisibilityAssistant;
import org.eclipse.ui.internal.navigator.VisibilityAssistant.VisibilityListener;
import org.eclipse.ui.navigator.INavigatorContentDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
/**
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as
* part of a work in progress. There is a guarantee neither that this API will
* work nor that it will remain the same. Please do not use this API without
* consulting with the Platform/UI team.
* </p>
*
* @since 3.2
*/
public class NavigatorContentDescriptorManager {
private static final NavigatorContentDescriptorManager INSTANCE = new NavigatorContentDescriptorManager();
private final Map firstClassDescriptorsMap = new HashMap();
private final Map allDescriptors = new HashMap();
private class EvaluationCache implements VisibilityListener {
private final Map evaluations = new WeakHashMap();
private final Map evaluationsWithOverrides = new WeakHashMap();
EvaluationCache(VisibilityAssistant anAssistant) {
anAssistant.addListener(this);
}
protected final Set getDescriptors(Object anElement) {
return getDescriptors(anElement, true);
}
protected final void setDescriptors(Object anElement, Set theDescriptors) {
setDescriptors(anElement, theDescriptors, true);
}
protected final Set getDescriptors(Object anElement, boolean toComputeOverrides) {
if(toComputeOverrides)
return (Set) evaluations.get(anElement);
return (Set) evaluationsWithOverrides.get(anElement);
}
protected final void setDescriptors(Object anElement, Set theDescriptors, boolean toComputeOverrides) {
if(toComputeOverrides)
evaluations.put(anElement, theDescriptors);
else
evaluationsWithOverrides.put(anElement, theDescriptors);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.internal.navigator.VisibilityAssistant.VisibilityListener#onVisibilityOrActivationChange()
*/
public void onVisibilityOrActivationChange() {
evaluations.clear();
evaluationsWithOverrides.clear();
}
}
/* Map of (VisibilityAssistant, EvaluationCache)-pairs */
private final Map cachedTriggerPointEvaluations = new WeakHashMap();
/* Map of (VisibilityAssistant, EvaluationCache)-pairs */
private final Map cachedPossibleChildrenEvaluations = new WeakHashMap();
private ImageRegistry imageRegistry;
private final Set overridingDescriptors = new HashSet();
private final Set saveablesProviderDescriptors = new HashSet();
private final Set firstClassDescriptorsSet = new HashSet();
/**
* @return the singleton instance of the manager
*/
public static NavigatorContentDescriptorManager getInstance() {
return INSTANCE;
}
private NavigatorContentDescriptorManager() {
new NavigatorContentDescriptorRegistry().readRegistry();
}
/**
*
* @return Returns all content descriptor(s).
*/
public NavigatorContentDescriptor[] getAllContentDescriptors() {
NavigatorContentDescriptor[] finalDescriptors = new NavigatorContentDescriptor[allDescriptors
.size()];
finalDescriptors = (NavigatorContentDescriptor[]) allDescriptors.values().toArray(finalDescriptors);
Arrays.sort(finalDescriptors, ExtensionPriorityComparator.INSTANCE);
return finalDescriptors;
}
/**
*
* @return Returns all content descriptors that provide saveables.
*/
public NavigatorContentDescriptor[] getContentDescriptorsWithSaveables() {
NavigatorContentDescriptor[] finalDescriptors = new NavigatorContentDescriptor[saveablesProviderDescriptors
.size()];
saveablesProviderDescriptors.toArray(finalDescriptors);
Arrays.sort(finalDescriptors, ExtensionPriorityComparator.INSTANCE);
return finalDescriptors;
}
/**
*
* Returns all content descriptor(s) which enable for the given element.
*
* @param anElement
* the element to return the best content descriptor for
*
* @param aVisibilityAssistant
* The relevant viewer assistant; used to filter out unbound
* content descriptors.
* @return the best content descriptor for the given element.
*/
public Set findDescriptorsForTriggerPoint(Object anElement,
VisibilityAssistant aVisibilityAssistant) {
EvaluationCache cache = getEvaluationCache(
cachedTriggerPointEvaluations, aVisibilityAssistant);
if (cache.getDescriptors(anElement) != null) {
return cache.getDescriptors(anElement);
}
Set descriptors = new TreeSet(ExtensionPriorityComparator.INSTANCE);
/* Find other ContentProviders which enable for this object */
for (Iterator contentDescriptorsItr = firstClassDescriptorsMap.values()
.iterator(); contentDescriptorsItr.hasNext();) {
NavigatorContentDescriptor descriptor = (NavigatorContentDescriptor) contentDescriptorsItr
.next();
if (aVisibilityAssistant.isActive(descriptor)
&& aVisibilityAssistant.isVisible(descriptor)
&& descriptor.isTriggerPoint(anElement)) {
descriptors.add(descriptor);
}
}
cache.setDescriptors(anElement, descriptors);
return descriptors;
}
private EvaluationCache getEvaluationCache(Map anEvaluationMap,
VisibilityAssistant aVisibilityAssistant) {
EvaluationCache c = (EvaluationCache) anEvaluationMap
.get(aVisibilityAssistant);
if (c == null) {
anEvaluationMap.put(aVisibilityAssistant, c = new EvaluationCache(
aVisibilityAssistant));
}
return c;
}
/**
*
* Returns all content descriptor(s) which enable for the given element.
*
* @param anElement
* the element to return the best content descriptor for
*
* @param aVisibilityAssistant
* The relevant viewer assistant; used to filter out unbound
* content descriptors.
* @return the best content descriptor for the given element.
*/
public Set findDescriptorsForPossibleChild(Object anElement,
VisibilityAssistant aVisibilityAssistant) {
return findDescriptorsForPossibleChild(anElement, aVisibilityAssistant, true);
}
/**
*
* Returns all content descriptor(s) which enable for the given element.
*
* @param anElement
* the element to return the best content descriptor for
*
* @param aVisibilityAssistant
* The relevant viewer assistant; used to filter out unbound
* content descriptors.
* @return the best content descriptor for the given element.
*/
public Set findDescriptorsForPossibleChild(Object anElement,
VisibilityAssistant aVisibilityAssistant, boolean toComputeOverrides) {
EvaluationCache cache = getEvaluationCache(
cachedPossibleChildrenEvaluations, aVisibilityAssistant);
if (cache.getDescriptors(anElement, toComputeOverrides) != null) {
return cache.getDescriptors(anElement, toComputeOverrides);
}
Set descriptors = new TreeSet(ExtensionPriorityComparator.INSTANCE);
if(toComputeOverrides) {
addDescriptorsForPossibleChild(anElement, firstClassDescriptorsSet,
aVisibilityAssistant, descriptors);
} else {
NavigatorContentDescriptor descriptor;
/* Find other ContentProviders which enable for this object */
for (Iterator contentDescriptorsItr = allDescriptors.values().iterator(); contentDescriptorsItr
.hasNext();) {
descriptor = (NavigatorContentDescriptor) contentDescriptorsItr
.next();
boolean isApplicable = aVisibilityAssistant.isActive(descriptor)
&& aVisibilityAssistant.isVisible(descriptor)
&& descriptor.isPossibleChild(anElement);
if (isApplicable) {
descriptors.add(descriptor);
}
}
}
cache.setDescriptors(anElement, descriptors, toComputeOverrides);
return descriptors;
}
private boolean addDescriptorsForPossibleChild(Object anElement,
Set theChildDescriptors, VisibilityAssistant aVisibilityAssistant,
Set theFoundDescriptors) {
int initialSize = theFoundDescriptors.size();
NavigatorContentDescriptor descriptor;
/* Find other ContentProviders which enable for this object */
for (Iterator contentDescriptorsItr = theChildDescriptors.iterator(); contentDescriptorsItr
.hasNext();) {
descriptor = (NavigatorContentDescriptor) contentDescriptorsItr
.next();
boolean isApplicable = aVisibilityAssistant.isActive(descriptor)
&& aVisibilityAssistant.isVisible(descriptor)
&& descriptor.isPossibleChild(anElement);
if (descriptor.hasOverridingExtensions()) {
boolean isOverridden = addDescriptorsForPossibleChild(
anElement, descriptor.getOverriddingExtensions(),
aVisibilityAssistant, theFoundDescriptors);
if (!isOverridden && isApplicable) {
theFoundDescriptors.add(descriptor);
}
} else if (isApplicable) {
theFoundDescriptors.add(descriptor);
}
}
return initialSize < theFoundDescriptors.size();
}
/**
* Returns the navigator content descriptor with the given id.
*
* @param id
* The id of the content descriptor that should be returned
* @return The content descriptor of the given id
*/
public NavigatorContentDescriptor getContentDescriptor(String id) {
return (NavigatorContentDescriptor) allDescriptors.get(id);
}
/**
*
* @param descriptorId
* The unique id of a particular descriptor
* @return The name (value of the 'name' attribute) of the given descriptor
*/
public String getText(String descriptorId) {
INavigatorContentDescriptor descriptor = getContentDescriptor(descriptorId);
if (descriptor != null) {
return descriptor.getName();
}
return descriptorId;
}
/**
*
* @param descriptorId
* The unique id of a particular descriptor
* @return The image (corresponding to the value of the 'icon' attribute) of
* the given descriptor
*/
public Image getImage(String descriptorId) {
return retrieveAndStoreImage(descriptorId);
}
protected Image retrieveAndStoreImage(String descriptorId) {
NavigatorContentDescriptor contentDescriptor = getContentDescriptor(descriptorId);
Image image = null;
if (contentDescriptor != null) {
String icon = contentDescriptor.getIcon();
if (icon != null) {
image = getImageRegistry().get(icon);
if (image == null || image.isDisposed()) {
ImageDescriptor imageDescriptor = AbstractUIPlugin
.imageDescriptorFromPlugin(contentDescriptor
.getContribution().getPluginId(), icon);
if (imageDescriptor != null) {
image = imageDescriptor.createImage();
if (image != null) {
getImageRegistry().put(icon, image);
}
}
}
}
}
return image;
}
/**
* @param desc
*/
private void addNavigatorContentDescriptor(NavigatorContentDescriptor desc) {
if (desc == null) {
return;
}
synchronized (firstClassDescriptorsMap) {
if (firstClassDescriptorsMap.containsKey(desc.getId())) {
NavigatorPlugin
.logError(
0,
"An extension already exists with id \"" + desc.getId() + "\".", null); //$NON-NLS-1$ //$NON-NLS-2$
} else {
if (desc.getSuppressedExtensionId() == null) {
firstClassDescriptorsMap.put(desc.getId(), desc);
firstClassDescriptorsSet.add(desc);
} else {
overridingDescriptors.add(desc);
}
allDescriptors.put(desc.getId(), desc);
if (desc.hasSaveablesProvider()) {
saveablesProviderDescriptors.add(desc);
}
}
}
}
/**
*
*/
private void computeOverrides() {
if (overridingDescriptors.size() > 0) {
NavigatorContentDescriptor descriptor;
NavigatorContentDescriptor overriddenDescriptor;
for (Iterator overridingIterator = overridingDescriptors.iterator(); overridingIterator
.hasNext();) {
descriptor = (NavigatorContentDescriptor) overridingIterator
.next();
overriddenDescriptor = (NavigatorContentDescriptor) allDescriptors
.get(descriptor.getSuppressedExtensionId());
if (overriddenDescriptor != null) {
/*
* add the descriptor as an overriding extension for its
* suppressed extension
*/
overriddenDescriptor.getOverriddingExtensions().add(
descriptor);
descriptor.setOverriddenDescriptor(overriddenDescriptor);
/*
* the always policy implies this is also a top-level
* extension
*/
if (descriptor.getOverridePolicy() == OverridePolicy.InvokeAlwaysRegardlessOfSuppressedExt) {
firstClassDescriptorsMap.put(descriptor.getId(),
descriptor);
firstClassDescriptorsSet.add(descriptor);
}
} else {
NavigatorPlugin.logError(0,
"Invalid suppressedExtensionId (\"" //$NON-NLS-1$
+ descriptor.getSuppressedExtensionId()
+ "\" specified from " //$NON-NLS-1$
+ descriptor.getContribution()
.getPluginId()
+ ". No extension with matching id found.", //$NON-NLS-1$
null);
}
}
}
}
private ImageRegistry getImageRegistry() {
if (imageRegistry == null) {
imageRegistry = new ImageRegistry();
}
return imageRegistry;
}
private class NavigatorContentDescriptorRegistry extends
NavigatorContentRegistryReader {
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.internal.navigator.extensions.RegistryReader#readRegistry()
*/
public void readRegistry() {
super.readRegistry();
computeOverrides();
}
protected boolean readElement(IConfigurationElement anElement) {
if (TAG_NAVIGATOR_CONTENT.equals(anElement.getName())) {
try {
addNavigatorContentDescriptor(new NavigatorContentDescriptor(
anElement));
} catch (WorkbenchException e) {
// log an error since its not safe to open a dialog here
NavigatorPlugin.log(e.getStatus());
}
}
return super.readElement(anElement);
}
}
}