blob: 45c0b3e55921bc0044c4407d1c5da59dbdbe98e9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2007 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;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptor;
import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension;
import org.eclipse.ui.navigator.INavigatorContentDescriptor;
import org.eclipse.ui.navigator.INavigatorPipelineService;
import org.eclipse.ui.navigator.IPipelinedTreeContentProvider;
import org.eclipse.ui.navigator.PipelinedShapeModification;
import org.eclipse.ui.navigator.PipelinedViewerUpdate;
/**
* @since 3.2
*
*/
public class NavigatorPipelineService implements INavigatorPipelineService {
private NavigatorContentService contentService;
/**
* Create a pipeline assistnat for the given content service.
*
* @param aContentService
* The content service that will drive this pipeline assistant.
*/
public NavigatorPipelineService(NavigatorContentService aContentService) {
contentService = aContentService;
}
/**
* Intercept attempts to add elements directly to the viewer.
*
* <p>
* For content extensions that reshape the structure of children in a
* viewer, their overridden extensions may sometimes use optimized refreshes
* to add elements to the tree. These attempts must be intercepted and
* mapped to the correct set of model elements in the overridding extension.
* Clients may add, remove, or modify elements in the given set of added
* children. Clients should return a set for downstream extensions to
* massage further.
* </p>
* <p>
* <b>Clients should not call any of the add, remove, refresh, or update
* methods on the viewer from this method or any code invoked by the
* implementation of this method.</b>
* </p>
*
* @param anAddModification
* The shape modification which contains the current suggested
* parent and children. Clients may modify this parameter
* directly and return it as the new shape modification.
* @return The new shape modification to use. Clients should <b>never</b>
* return <b>null</b> from this method.
*/
public PipelinedShapeModification interceptAdd(
PipelinedShapeModification anAddModification) {
ContributorTrackingSet trackedSet =(ContributorTrackingSet) anAddModification.getChildren();
Set contentDescriptors = contentService.findDescriptorsByTriggerPoint(anAddModification.getParent());
for (Iterator descriptorsItr = contentDescriptors.iterator(); descriptorsItr.hasNext();) {
INavigatorContentDescriptor descriptor = (INavigatorContentDescriptor) descriptorsItr.next();
pipelineInterceptAdd(anAddModification, trackedSet, descriptor);
}
// for consistency, we register the contribution from our best known match
registerContribution(anAddModification.getParent(), anAddModification.getChildren().toArray());
return anAddModification;
}
/**
* @param parent The object to which data was contributed
* @param contributions Data contributed to the viewer
*/
private void registerContribution(Object parent, Object[] contributions) {
// returns an array sorted by priority
Set possibleContributors = contentService.findDescriptorsByTriggerPoint(parent);
Set possibleMatches = null;
for (int i = 0; i < contributions.length; i++) {
// returns an array sorted by reverse priority
possibleMatches = contentService.findDescriptorsWithPossibleChild(contributions[i]);
NavigatorContentDescriptor[] descriptors = (NavigatorContentDescriptor[]) possibleMatches.toArray(new NavigatorContentDescriptor[possibleMatches.size()]);
for (int indx = possibleMatches.size()-1; indx > -1; indx--) {
// terminates once the highest priority match is found for this child
if(possibleContributors.contains(descriptors[indx])) {
contentService.rememberContribution(descriptors[indx], contributions[i]);
break;
}
}
}
}
private void pipelineInterceptAdd(PipelinedShapeModification anAddModification, ContributorTrackingSet trackedSet, INavigatorContentDescriptor descriptor) {
if(descriptor.hasOverridingExtensions()) {
Set overridingDescriptors = descriptor.getOverriddingExtensions();
for (Iterator overridingDescriptorsItr = overridingDescriptors.iterator(); overridingDescriptorsItr
.hasNext();) {
INavigatorContentDescriptor overridingDescriptor = (INavigatorContentDescriptor) overridingDescriptorsItr.next();
if(contentService.isVisible(overridingDescriptor.getId()) && contentService.isActive(overridingDescriptor.getId())) {
trackedSet.setContributor((NavigatorContentDescriptor) overridingDescriptor);
NavigatorContentExtension extension = contentService.getExtension(overridingDescriptor);
((IPipelinedTreeContentProvider) extension.internalGetContentProvider()).interceptAdd(anAddModification);
trackedSet.setContributor(null);
pipelineInterceptAdd(anAddModification, trackedSet, overridingDescriptor);
}
}
}
}
/**
* Intercept attempts to remove elements directly from the viewer.
*
* <p>
* For content extensions that reshape the structure of children in a
* viewer, their overridden extensions may sometimes use optimized refreshes
* to remove elements to the tree. These attempts must be intercepted and
* mapped to the correct set of model elements in the overridding extension.
* Clients may add, remove, or modify elements in the given set of removed
* children. Clients should return a set for downstream extensions to
* massage further.
* </p>
* <p>
* <b>Clients should not call any of the add, remove, refresh, or update
* methods on the viewer from this method or any code invoked by the
* implementation of this method.</b>
* </p>
*
* @param aRemoveModification
* The shape modification which contains the current suggested
* parent and children. Clients may modify this parameter
* directly and return it as the new shape modification.
* @return The new shape modification to use. Clients should <b>never</b>
* return <b>null</b> from this method.
*/
public PipelinedShapeModification interceptRemove(
PipelinedShapeModification aRemoveModification) {
ContributorTrackingSet trackedSet =(ContributorTrackingSet) aRemoveModification.getChildren();
Set interestedExtensions = new LinkedHashSet();
for (Iterator iter = trackedSet.iterator(); iter.hasNext();) {
Object element = (Object) iter.next();
if(element instanceof TreePath) {
interestedExtensions.addAll(contentService.findOverrideableContentExtensionsForPossibleChild(((TreePath)element).getLastSegment()));
} else {
interestedExtensions = contentService.findOverrideableContentExtensionsForPossibleChild(element);
}
}
for (Iterator overridingExtensionsIter = interestedExtensions.iterator(); overridingExtensionsIter.hasNext();)
pipelineInterceptRemove(aRemoveModification, trackedSet, (NavigatorContentExtension) overridingExtensionsIter.next());
return aRemoveModification;
}
private void pipelineInterceptRemove(PipelinedShapeModification aRemoveModification, ContributorTrackingSet trackedSet, NavigatorContentExtension overrideableExtension) {
try {
NavigatorContentExtension overridingExtension = null;
Set overridingExtensions = new LinkedHashSet();
for (Iterator iter = trackedSet.iterator(); iter.hasNext();) {
Object element = (Object) iter.next();
if(element instanceof TreePath) {
overridingExtensions.addAll(Arrays.asList(overrideableExtension.getOverridingExtensionsForPossibleChild(((TreePath)element).getLastSegment())));
} else {
overridingExtensions.addAll(Arrays.asList(overrideableExtension.getOverridingExtensionsForPossibleChild(element)));
}
}
for (Iterator extensionsItr = overridingExtensions.iterator(); extensionsItr.hasNext();) {
overridingExtension = (NavigatorContentExtension) extensionsItr.next();
trackedSet.setContributor((NavigatorContentDescriptor) overridingExtension.getDescriptor());
if (overridingExtension.getContentProvider() instanceof IPipelinedTreeContentProvider) {
((IPipelinedTreeContentProvider) overridingExtension.getContentProvider()).interceptRemove(aRemoveModification);
}
trackedSet.setContributor(null);
if(overridingExtension.getDescriptor().hasOverridingExtensions())
pipelineInterceptRemove(aRemoveModification, trackedSet, overridingExtension);
}
} catch (Throwable e) {
String msg = e.getMessage() != null ? e.getMessage() : e.toString();
NavigatorPlugin.logError(0, msg, e);
}
}
/**
* Intercept calls to viewer <code>refresh()</code> methods.
*
* <p>
* Clients may modify the given update to add or remove the elements to be
* refreshed. Clients may return the same instance that was passed in for
* the next downstream extension.
* </p>
*
* <p>
* <b>Clients should not call any of the add, remove, refresh, or update
* methods on the viewer from this method or any code invoked by the
* implementation of this method.</b>
* </p>
*
* @param aRefreshSynchronization
* The (current) refresh update to execute against the viewer.
* @return The (potentially reshaped) refresh to execute against the viewer.
*/
public boolean interceptRefresh(
PipelinedViewerUpdate aRefreshSynchronization) {
boolean pipelined = false;
Object refreshable = null;
Set overrideableExtensions = new LinkedHashSet();
for (Iterator iter = aRefreshSynchronization.getRefreshTargets().iterator(); iter.hasNext();) {
refreshable = iter.next();
overrideableExtensions.addAll(contentService.findOverrideableContentExtensionsForPossibleChild(refreshable));
}
for (Iterator overrideableExtensionItr = overrideableExtensions.iterator(); overrideableExtensionItr.hasNext();) {
pipelined |= pipelineInterceptRefresh((NavigatorContentExtension) overrideableExtensionItr.next(), aRefreshSynchronization, refreshable);
}
return pipelined;
}
private boolean pipelineInterceptRefresh(NavigatorContentExtension overrideableExtension,
PipelinedViewerUpdate aRefreshSynchronization, Object refreshable) {
boolean intercepted = false;
NavigatorContentExtension[] overridingExtensionsForPossibleChild = overrideableExtension.getOverridingExtensionsForPossibleChild(refreshable);
for (int i=0; i<overridingExtensionsForPossibleChild.length; i++) {
try {
if (overridingExtensionsForPossibleChild[i].getContentProvider() instanceof IPipelinedTreeContentProvider) {
intercepted |= ((IPipelinedTreeContentProvider) overridingExtensionsForPossibleChild[i]
.getContentProvider())
.interceptRefresh(aRefreshSynchronization);
if (overridingExtensionsForPossibleChild[i].getDescriptor().hasOverridingExtensions())
intercepted |= pipelineInterceptRefresh(overridingExtensionsForPossibleChild[i], aRefreshSynchronization, refreshable);
}
} catch (Throwable e) {
String msg = e.getMessage() != null ? e.getMessage() : e.toString();
NavigatorPlugin.logError(0, msg, e);
}
}
return intercepted;
}
/**
* Intercept calls to viewer <code>update()</code> methods.
*
* <p>
* Clients may modify the given update to add or remove the elements to be
* updated. Clients may also add or remove properties for the given targets
* to optimize the refresh. Clients may return the same instance that was
* passed in for the next downstream extension.
* </p>
*
* <p>
* <b>Clients should not call any of the add, remove, refresh, or update
* methods on the viewer from this method or any code invoked by the
* implementation of this method.</b>
* </p>
*
* @param anUpdateSynchronization
* The (current) update to execute against the viewer.
* @return The (potentially reshaped) update to execute against the viewer.
*/
public boolean interceptUpdate(
PipelinedViewerUpdate anUpdateSynchronization) {
boolean pipelined = false;
Object refreshable = null;
Set overrideableExtensions = new LinkedHashSet();
for (Iterator iter = anUpdateSynchronization.getRefreshTargets().iterator(); iter.hasNext();) {
refreshable = iter.next();
overrideableExtensions.addAll(contentService.findOverrideableContentExtensionsForPossibleChild(refreshable));
}
for (Iterator overrideableExtensionItr = overrideableExtensions.iterator(); overrideableExtensionItr.hasNext();) {
pipelined |= pipelineInterceptUpdate((NavigatorContentExtension) overrideableExtensionItr.next(), anUpdateSynchronization, refreshable);
}
return pipelined;
}
private boolean pipelineInterceptUpdate(NavigatorContentExtension overrideableExtension,
PipelinedViewerUpdate anUpdateSynchronization, Object refreshable) {
boolean intercepted = false;
NavigatorContentExtension[] overridingExtensionsForPossibleChild = overrideableExtension.getOverridingExtensionsForPossibleChild(refreshable);
for (int i=0; i<overridingExtensionsForPossibleChild.length; i++) {
try {
if (overridingExtensionsForPossibleChild[i].getContentProvider() instanceof IPipelinedTreeContentProvider) {
intercepted |= ((IPipelinedTreeContentProvider) overridingExtensionsForPossibleChild[i]
.getContentProvider())
.interceptUpdate(anUpdateSynchronization);
if (overridingExtensionsForPossibleChild[i].getDescriptor().hasOverridingExtensions())
intercepted |= pipelineInterceptUpdate(overridingExtensionsForPossibleChild[i], anUpdateSynchronization, refreshable);
}
} catch (Throwable e) {
String msg = e.getMessage() != null ? e.getMessage() : e.toString();
NavigatorPlugin.logError(0, msg, e);
}
}
return intercepted;
}
}