blob: a262f8faaf8558f2ac171f4fb98fceb235c5374a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.ui.mapping;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.mapping.ModelProvider;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.*;
import org.eclipse.team.core.diff.*;
import org.eclipse.team.core.mapping.IResourceMappingScope;
import org.eclipse.team.core.mapping.ISynchronizationContext;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.internal.ui.Utils;
import org.eclipse.team.ui.TeamUI;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.navigator.ICommonContentProvider;
import org.eclipse.ui.navigator.IExtensionStateModel;
/**
* Abstract team aware content provider that delegates to another content provider
* <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/Team team.
* </p>
*
* @since 3.2
*/
public abstract class SynchronizationContentProvider implements ICommonContentProvider, IDiffChangeListener, IPropertyChangeListener {
private IResourceMappingScope scope;
private ISynchronizationContext context;
private Viewer viewer;
private IExtensionStateModel stateModel;
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
*/
public Object[] getChildren(Object parent) {
if (parent instanceof IResourceMappingScope) {
IResourceMappingScope rms = (IResourceMappingScope) parent;
if (rms.getMappings(getModelProviderId()).length > 0) {
return new Object[] { getModelProvider() };
}
return new Object[0];
} else if (parent instanceof ISynchronizationContext) {
ISynchronizationContext sc = (ISynchronizationContext) parent;
if (sc.getScope().getMappings(getModelProviderId()).length > 0) {
if (filter(parent, getDelegateContentProvider().getChildren(getModelRoot())).length > 0)
return new Object[] { getModelProvider() };
}
return new Object[0];
}
if (parent == getModelProvider()) {
return filter(parent, getDelegateContentProvider().getChildren(getModelRoot()));
}
return filter(parent, getDelegateContentProvider().getElements(parent));
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
*/
public Object getParent(Object element) {
if (element instanceof ModelProvider)
return null;
if (element == getModelRoot())
return null;
Object parent = getDelegateContentProvider().getParent(element);
if (parent == getModelRoot())
return getModelProvider();
return parent;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
*/
public boolean hasChildren(Object element) {
if (element instanceof ModelProvider) {
element = getModelRoot();
}
return getDelegateContentProvider().hasChildren(element) && filter(element, getChildren(element)).length > 0;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
*/
public Object[] getElements(Object inputElement) {
if (inputElement instanceof IResourceMappingScope) {
IResourceMappingScope rms = (IResourceMappingScope) inputElement;
if (rms.getMappings(getModelProviderId()).length > 0) {
return new Object[] { getModelProvider() };
}
return new Object[0];
} else if (inputElement instanceof ISynchronizationContext) {
ISynchronizationContext sc = (ISynchronizationContext) inputElement;
if (sc.getScope().getMappings(getModelProviderId()).length > 0) {
if (filter(getModelRoot(), getDelegateContentProvider().getChildren(getModelRoot())).length > 0)
return new Object[] { getModelProvider() };
}
return new Object[0];
}
if (inputElement == getModelProvider()) {
return filter(inputElement, getDelegateContentProvider().getChildren(getModelRoot()));
}
return filter(inputElement, getDelegateContentProvider().getElements(inputElement));
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
public void dispose() {
stateModel.removePropertyChangeListener(this);
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.viewer = viewer;
getDelegateContentProvider().inputChanged(viewer, oldInput, newInput);
}
/* (non-Javadoc)
* @see org.eclipse.ui.navigator.internal.extensions.ICommonContentProvider#init(org.eclipse.ui.navigator.IExtensionStateModel, org.eclipse.ui.IMemento)
*/
public void init(IExtensionStateModel aStateModel, IMemento aMemento) {
stateModel = aStateModel;
stateModel.addPropertyChangeListener(this);
scope = (IResourceMappingScope)aStateModel.getProperty(TeamUI.RESOURCE_MAPPING_SCOPE);
context = (ISynchronizationContext)aStateModel.getProperty(TeamUI.SYNCHRONIZATION_CONTEXT);
ITreeContentProvider provider = getDelegateContentProvider();
if (provider instanceof ICommonContentProvider) {
((ICommonContentProvider) provider).init(aStateModel, aMemento);
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
// TODO: this could happen at the root as well
if (event.getProperty().equals(ISynchronizePageConfiguration.P_MODE)) {
refresh();
}
}
/**
* Return whether elements with the given synchronization kind (as define in
* the {@link SyncInfo} class) should be included in the contents. This
* method is invoked by the {@link #getChildrenInContext(Object, Object[]) }
* method to filter the list of children returned when {@link #getChildren(Object) }
* is called. It accessing the <code>ISynchronizePageConfiguration.P_MODE</code>
* property on the state model provided by the view to determine what kinds
* should be included.
*
* @param direction the synchronization kind as described in the {@link SyncInfo}
* class
* @return whether elements with the given synchronization kind should be
* included in the contents
*/
protected boolean includeDirection(int direction) {
int mode = stateModel.getIntProperty(ISynchronizePageConfiguration.P_MODE);
switch (mode) {
case ISynchronizePageConfiguration.BOTH_MODE:
return true;
case ISynchronizePageConfiguration.CONFLICTING_MODE:
return direction == IThreeWayDiff.CONFLICTING;
case ISynchronizePageConfiguration.INCOMING_MODE:
return direction == IThreeWayDiff.CONFLICTING || direction == IThreeWayDiff.INCOMING;
case ISynchronizePageConfiguration.OUTGOING_MODE:
return direction == IThreeWayDiff.CONFLICTING || direction == IThreeWayDiff.OUTGOING;
default:
break;
}
return true;
}
/**
* Return the synchronization context associated with the view to which
* this content provider applies. A <code>null</code> is returned if
* no context is available.
* @return the synchronization context or <code>null</code>
*/
protected ISynchronizationContext getContext() {
return context;
}
/**
* Return the resource mapping scope associated with the view to which
* this content provider applies. A <code>null</code> is returned if
* no scope is available.
* @return the resource mapping scope or <code>null</code>
*/
protected IResourceMappingScope getScope() {
return scope;
}
/* (non-Javadoc)
* @see org.eclipse.ui.navigator.IMementoAware#restoreState(org.eclipse.ui.IMemento)
*/
public void restoreState(IMemento aMemento) {
ITreeContentProvider provider = getDelegateContentProvider();
if (provider instanceof ICommonContentProvider) {
((ICommonContentProvider) provider).restoreState(aMemento);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.navigator.IMementoAware#saveState(org.eclipse.ui.IMemento)
*/
public void saveState(IMemento aMemento) {
ITreeContentProvider provider = getDelegateContentProvider();
if (provider instanceof ICommonContentProvider) {
((ICommonContentProvider) provider).saveState(aMemento);
}
}
/* (non-Javadoc)
* @see org.eclipse.team.core.delta.ISyncDeltaChangeListener#syncDeltaTreeChanged(org.eclipse.team.core.delta.ISyncDeltaChangeEvent, org.eclipse.core.runtime.IProgressMonitor)
*/
public void diffChanged(IDiffChangeEvent event, IProgressMonitor monitor) {
refresh();
}
/**
* Refresh the subtree associated with this model.
*/
protected void refresh() {
Utils.syncExec(new Runnable() {
public void run() {
TreeViewer treeViewer = ((TreeViewer)getViewer());
treeViewer.refresh(getModelProvider());
}
}, getViewer().getControl());
}
/**
* Return the model content provider that the team aware content
* provider delegates to.
* @return the model content provider
*/
protected abstract ITreeContentProvider getDelegateContentProvider();
/**
* Filter the obtained children of the given parent so that only the
* desired elements are shown. By default the {@link #getChildrenInScope(Object, Object[]) }
* and {@link #getChildrenInContext(Object, Object[]) } methods are used
* to filter the set of elements returned from the delegate provider.
* Subclass may override.
* @param parentElement the parent element
* @param children the children
* @return the filtered children
*/
protected Object[] filter(Object parentElement, Object[] children) {
children = getChildrenInScope(parentElement, children);
children = getChildrenInContext(parentElement, children);
return children;
}
/**
* Return the model provider for this content provider.
* @return the model provider for this content provider
*/
protected final ModelProvider getModelProvider() {
try {
return ModelProvider.getModelProviderDescriptor(getModelProviderId()).getModelProvider();
} catch (CoreException e) {
// TODO: this is a bit harsh. can we do something less destructive
throw new IllegalStateException();
}
}
/**
* Return the id of model provider for this content provider.
* @return the model provider for this content provider
*/
protected abstract String getModelProviderId();
/**
* Return the object that acts as the model root. It is used when getting the children
* for a model provider.
* @return the object that acts as the model root
*/
protected abstract Object getModelRoot();
/**
* Return the viewer to which the content provider is associated.
* @return the viewer to which the content provider is associated
*/
protected final Viewer getViewer() {
return viewer;
}
/**
* Return the subset of the given children that are in the
* scope of the content provider or are parents
* of elements that are in scope. If the content provider
* is not scope (i.e. <code>getScope() == null</code>),
* all the children are returned.
* @param parent the parent of the given children
* @param children all the children of the parent that are in scope.
* @return the subset of the given children that are in the
* scope of the content provider
*/
protected Object[] getChildrenInScope(Object parent, Object[] children) {
IResourceMappingScope scope = getScope();
if (scope == null)
return children;
List result = new ArrayList();
for (int i = 0; i < children.length; i++) {
Object object = children[i];
if (isInScope(parent, object)) {
result.add(object);
}
}
return result.toArray(new Object[result.size()]);
}
/**
* Return the subset of children that are of interest from the given context.
* If there is no context, all the children are returned.
* @param parent the parent of the children
* @param children the children
* @return the subset of children that are of interest from the given context
*/
protected Object[] getChildrenInContext(Object parentElemnt, Object[] children) {
ISynchronizationContext context = getContext();
if (context == null)
return children;
List result = new ArrayList();
for (int i = 0; i < children.length; i++) {
Object object = children[i];
ResourceTraversal[] traversals = getTraversals(object);
IDiffNode[] deltas = context.getDiffs(traversals);
if (deltas.length > 0) {
boolean include = false;
for (int j = 0; j < deltas.length; j++) {
IDiffNode delta = deltas[j];
if (delta instanceof IThreeWayDiff) {
IThreeWayDiff twd = (IThreeWayDiff) delta;
if (includeDirection(twd.getDirection())) {
include = true;
break;
}
}
}
if (include)
result.add(object);
}
}
// TODO: may need to get phantoms as well
return result.toArray(new Object[result.size()]);
}
/**
* Return the traversals for the given model object. The traversals
* should be obtained from the scope.
* @param object the model object
* @return the traversals for the given object in the scope of this content provider
*/
protected abstract ResourceTraversal[] getTraversals(Object object);
/**
* Return whether the given object is within the scope of this
* content provider. The object is in scope if it is part of
* a resource mapping in the scope or is the parent of resources
* covered by one or more resource mappings in the scope.
* @param parent the parent of the object
* @param object the object
* @return whether the given object is within the scope of this
* content provider
*/
protected abstract boolean isInScope(Object parent, Object object);
}