blob: abec983309ca3d8575295c838791c590a0796dbb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2011 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
* Bug 349224 Navigator content provider "appearsBefore" creates hard reference to named id - paul.fullbright@oracle.com
*******************************************************************************/
package org.eclipse.ui.internal.navigator.extensions;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
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.core.runtime.IStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.internal.navigator.NavigatorPlugin;
import org.eclipse.ui.internal.navigator.NavigatorSafeRunnable;
import org.eclipse.ui.internal.navigator.Policy;
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.navigator.OverridePolicy;
import org.eclipse.ui.plugin.AbstractUIPlugin;
/**
* @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/* <Object, NavigatorContentDescriptor[]> */= new HashMap();
private final Map evaluationsWithOverrides/*<Object, NavigatorContentDescriptor[]>*/ = new HashMap();
EvaluationCache(VisibilityAssistant anAssistant) {
anAssistant.addListener(this);
}
protected final NavigatorContentDescriptor[] getDescriptors(Object anElement) {
return getDescriptors(anElement, true);
}
protected final void setDescriptors(Object anElement, NavigatorContentDescriptor[] theDescriptors) {
setDescriptors(anElement, theDescriptors, true);
}
protected final NavigatorContentDescriptor[] getDescriptors(Object anElement, boolean toComputeOverrides) {
if (anElement == null)
return null;
NavigatorContentDescriptor[] cachedDescriptors = null;
if (toComputeOverrides) {
SoftReference cache = (SoftReference) evaluations.get(anElement);
if (cache != null && (cachedDescriptors = (NavigatorContentDescriptor[]) cache.get()) == null)
evaluations.remove(anElement);
return cachedDescriptors;
}
SoftReference cache = (SoftReference) evaluationsWithOverrides.get(anElement);
if (cache != null && (cachedDescriptors = (NavigatorContentDescriptor[]) cache.get()) == null)
evaluationsWithOverrides.remove(anElement);
return cachedDescriptors;
}
protected final void setDescriptors(Object anElement, NavigatorContentDescriptor[] theDescriptors, boolean toComputeOverrides) {
if (anElement != null) {
if (toComputeOverrides)
evaluations.put(new EvalutationReference(anElement), new SoftReference(theDescriptors));
else
evaluationsWithOverrides.put(new EvalutationReference(anElement), new SoftReference(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 sortOnlyDescriptors = 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, ExtensionSequenceNumberComparator.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, ExtensionSequenceNumberComparator.INSTANCE);
return finalDescriptors;
}
/**
*
* @return Returns all content descriptors that are sort only
*/
public NavigatorContentDescriptor[] getSortOnlyContentDescriptors() {
NavigatorContentDescriptor[] finalDescriptors = new NavigatorContentDescriptor[sortOnlyDescriptors
.size()];
sortOnlyDescriptors.toArray(finalDescriptors);
Arrays.sort(finalDescriptors, ExtensionSequenceNumberComparator.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.
* @param considerOverrides
* @return the best content descriptor for the given element.
*/
public Set findDescriptorsForTriggerPoint(Object anElement,
VisibilityAssistant aVisibilityAssistant, boolean considerOverrides) {
return findDescriptors(anElement, cachedTriggerPointEvaluations, aVisibilityAssistant, considerOverrides, !POSSIBLE_CHILD);
}
/**
*
* 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.
* @param toComputeOverrides
* @return the best content descriptor for the given element.
*/
public Set findDescriptorsForPossibleChild(Object anElement,
VisibilityAssistant aVisibilityAssistant, boolean toComputeOverrides) {
return findDescriptors(anElement, cachedPossibleChildrenEvaluations, aVisibilityAssistant, toComputeOverrides, POSSIBLE_CHILD);
}
private static final boolean POSSIBLE_CHILD = true;
private Set findDescriptors(Object anElement,
Map cachedEvaluations, VisibilityAssistant aVisibilityAssistant, boolean considerOverrides, boolean possibleChild) {
EvaluationCache cache = getEvaluationCache(
cachedEvaluations, aVisibilityAssistant);
Set descriptors = new TreeSet(ExtensionSequenceNumberComparator.INSTANCE);
NavigatorContentDescriptor[] cachedDescriptors = null;
if ((cachedDescriptors = cache.getDescriptors(anElement)) != null) {
descriptors.addAll(Arrays.asList(cachedDescriptors));
}
if (considerOverrides) {
addDescriptorsConsideringOverrides(anElement, firstClassDescriptorsSet, aVisibilityAssistant, descriptors, possibleChild);
if (Policy.DEBUG_RESOLUTION) {
System.out.println("Find descriptors for: " + Policy.getObjectString(anElement) + //$NON-NLS-1$
": " + descriptors); //$NON-NLS-1$
}
} else {
/* Find other ContentProviders which enable for this object */
for (Iterator contentDescriptorsItr = firstClassDescriptorsSet.iterator(); contentDescriptorsItr.hasNext();) {
NavigatorContentDescriptor descriptor = (NavigatorContentDescriptor) contentDescriptorsItr.next();
if (aVisibilityAssistant.isActive(descriptor) && aVisibilityAssistant.isVisible(descriptor)
&& (possibleChild ? descriptor.isPossibleChild(anElement) : descriptor.isTriggerPoint(anElement))) {
descriptors.add(descriptor);
}
}
}
cache.setDescriptors(anElement, (NavigatorContentDescriptor[]) descriptors.toArray(new NavigatorContentDescriptor[descriptors.size()]));
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;
}
private boolean addDescriptorsConsideringOverrides(Object anElement,
Set theChildDescriptors, VisibilityAssistant aVisibilityAssistant,
Set theFoundDescriptors, boolean possibleChild) {
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)
&& (possibleChild ? descriptor.isPossibleChild(anElement) : descriptor.isTriggerPoint(anElement));
if (descriptor.hasOverridingExtensions()) {
boolean isOverridden;
Set overridingDescriptors = new TreeSet(ExtensionSequenceNumberComparator.INSTANCE);
isOverridden = addDescriptorsConsideringOverrides(anElement, descriptor.getOverriddingExtensions(),
aVisibilityAssistant, overridingDescriptors, possibleChild);
if (!isOverridden && isApplicable) {
theFoundDescriptors.add(descriptor);
} else if (isOverridden) {
theFoundDescriptors.addAll(overridingDescriptors);
}
} 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);
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println("First class descriptor: " + desc); //$NON-NLS-1$
}
} else {
overridingDescriptors.add(desc);
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println("Overriding descriptor: " + desc); //$NON-NLS-1$
}
}
allDescriptors.put(desc.getId(), desc);
if (desc.hasSaveablesProvider()) {
saveablesProviderDescriptors.add(desc);
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println("Saveables provider descriptor: " + desc); //$NON-NLS-1$
}
}
if (desc.isSortOnly()) {
sortOnlyDescriptors.add(desc);
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println("SortOnly descriptor: " + desc); //$NON-NLS-1$
}
}
}
}
}
/**
*
*/
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) {
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println(descriptor + " overrides: " + overriddenDescriptor); //$NON-NLS-1$
}
/*
* 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) {
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println(descriptor + " is first class"); //$NON-NLS-1$
}
firstClassDescriptorsMap.put(descriptor.getId(),
descriptor);
firstClassDescriptorsSet.add(descriptor);
}
} else {
String message =
"Invalid suppressedExtensionId \"" //$NON-NLS-1$
+ descriptor.getSuppressedExtensionId()
+ "\" specified from \"" //$NON-NLS-1$
+ descriptor.getId() + "\" in \"" + descriptor.getContribution() //$NON-NLS-1$
.getPluginId()
+ "\". No extension with matching id found."; //$NON-NLS-1$
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println("Error: " + message); //$NON-NLS-1$
}
NavigatorPlugin.logError(0, message, null);
}
}
}
}
private int findId(List list, String id) {
for (int i = 0, len = list.size(); i < len; i++) {
NavigatorContentDescriptor desc = (NavigatorContentDescriptor) list.get(i);
if (desc.getId().equals(id))
return i;
}
// Do not require content descriptor to exist in workspace
NavigatorPlugin.log(IStatus.WARNING, 0,
"Can't find Navigator Content Descriptor with id: " + id, null); //$NON-NLS-1$
return -1;
}
private void computeSequenceNumbers() {
NavigatorContentDescriptor[] descs = getAllContentDescriptors();
LinkedList list = new LinkedList();
for (int i = 0; i < descs.length; i++) {
list.add(descs[i]);
}
boolean changed = true;
while (changed) {
changed = false;
for (int i = 0, len = list.size(); i < len; i++) {
NavigatorContentDescriptor desc = (NavigatorContentDescriptor) list.get(i);
if (desc.getAppearsBeforeId() != null) {
int beforeInd = findId(list, desc.getAppearsBeforeId());
if (beforeInd >= 0 && beforeInd < i) {
list.add(beforeInd, desc);
list.remove(i + 1);
changed = true;
}
}
}
}
for (int i = 0, len = list.size(); i < len; i++) {
NavigatorContentDescriptor desc = (NavigatorContentDescriptor) list.get(i);
desc.setSequenceNumber(i);
if (Policy.DEBUG_EXTENSION_SETUP) {
System.out.println("Descriptors by sequence: " + desc); //$NON-NLS-1$
}
}
}
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();
computeSequenceNumbers();
computeOverrides();
}
protected boolean readElement(final IConfigurationElement anElement) {
if (TAG_NAVIGATOR_CONTENT.equals(anElement.getName())) {
SafeRunner.run(new NavigatorSafeRunnable(anElement) {
public void run() throws Exception {
addNavigatorContentDescriptor(new NavigatorContentDescriptor(anElement));
}
});
}
return super.readElement(anElement);
}
}
}