blob: e3a55a7f2347e5869975d56bb2f7609843aed5ad [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ui.synchronize;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IResource;
import org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.synchronize.SyncInfoSet;
import org.eclipse.team.core.synchronize.SyncInfoTree;
import org.eclipse.team.ui.synchronize.ISynchronizeModelElement;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
/**
* This class provides functionality for defining composite synchronize model
* providers. A composite provider is one that breaks up the displayed
* {@link SyncInfoSet} into subsets that may be displayed using one
* or more synchronize model providers.
*
*/
public abstract class CompositeModelProvider extends AbstractSynchronizeModelProvider {
private final List<ISynchronizeModelProvider> providers = new ArrayList<>();
private final Map<IResource, List <ISynchronizeModelElement>> resourceToElements = new HashMap<>();
private final Map<ISynchronizeModelElement, AbstractSynchronizeModelProvider> elementToProvider = new HashMap<>();
protected CompositeModelProvider(ISynchronizePageConfiguration configuration, SyncInfoSet set) {
super(configuration, set);
}
/**
* Add the provider to the list of providers.
* @param provider the provider to be added
*/
protected void addProvider(ISynchronizeModelProvider provider) {
providers.add(provider);
}
/**
* Remove the provider from the list of providers.
* @param provider the provider to be removed
*/
protected void removeProvider(ISynchronizeModelProvider provider) {
providers.remove(provider);
provider.dispose();
}
@Override
protected ISynchronizeModelProvider getProvider(ISynchronizeModelElement element) {
return elementToProvider.get(element);
}
@Override
public ISynchronizeModelElement[] getClosestExistingParents(IResource resource) {
ISynchronizeModelProvider[] providers = getProviders();
if (providers.length == 0) {
return new ISynchronizeModelElement[0];
}
if (providers.length == 1 && providers[0] instanceof AbstractSynchronizeModelProvider) {
return ((AbstractSynchronizeModelProvider)providers[0]).getClosestExistingParents(resource);
}
List<ISynchronizeModelElement> result = new ArrayList<>();
for (int i = 0; i < providers.length; i++) {
ISynchronizeModelProvider provider = providers[i];
if (provider instanceof AbstractSynchronizeModelProvider) {
ISynchronizeModelElement[] elements = ((AbstractSynchronizeModelProvider)provider).getClosestExistingParents(resource);
for (int j = 0; j < elements.length; j++) {
ISynchronizeModelElement element = elements[j];
result.add(element);
}
}
}
return result.toArray(new ISynchronizeModelElement[result.size()]);
}
/**
* Return all the sub-providers of this composite.
* @return the sub-providers of this composite
*/
protected ISynchronizeModelProvider[] getProviders() {
return providers.toArray(new ISynchronizeModelProvider[providers.size()]);
}
/**
* Return the providers that are displaying the given resource.
* @param resource the resource
* @return the providers displaying the resource
*/
protected ISynchronizeModelProvider[] getProvidersContaining(IResource resource) {
List<ISynchronizeModelElement> elements = resourceToElements.get(resource);
if (elements == null || elements.isEmpty()) {
return new ISynchronizeModelProvider[0];
}
List<ISynchronizeModelProvider> result = new ArrayList<>();
for (Iterator<ISynchronizeModelElement> iter = elements.iterator(); iter.hasNext();) {
ISynchronizeModelElement element = iter.next();
result.add(getProvider(element));
}
return result.toArray(new ISynchronizeModelProvider[result.size()]);
}
@Override
protected final void handleResourceAdditions(ISyncInfoTreeChangeEvent event) {
handleAdditions(event.getAddedResources());
}
/**
* Handle the resource additions by adding them to any existing
* sub-providers or by creating addition sub-providers as needed.
* @param resources
*/
protected void handleAdditions(SyncInfo[] resources) {
for (int i = 0; i < resources.length; i++) {
SyncInfo info = resources[i];
handleAddition(info);
}
}
/**
* Handle the addition of the given sync info to this provider
* @param info the added sync info
*/
protected abstract void handleAddition(SyncInfo info);
@Override
protected final void handleResourceChanges(ISyncInfoTreeChangeEvent event) {
SyncInfo[] infos = event.getChangedResources();
for (int i = 0; i < infos.length; i++) {
SyncInfo info = infos[i];
handleChange(info);
}
}
/**
* The state of the sync info for a resource has changed. Propagate the
* change to any sub-providers that contain the resource.
* @param info the sync info for the resource whose sync state has changed
*/
protected void handleChange(SyncInfo info) {
handleRemoval(info.getLocal());
handleAddition(info);
}
@Override
protected final void handleResourceRemovals(ISyncInfoTreeChangeEvent event) {
IResource[] resources = event.getRemovedResources();
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
handleRemoval(resource);
}
}
/**
* Remove the resource from all providers that are displaying it
* @param resource the resource to be removed
*/
protected void handleRemoval(IResource resource) {
ISynchronizeModelProvider[] providers = getProvidersContaining(resource);
for (int i = 0; i < providers.length; i++) {
ISynchronizeModelProvider provider = providers[i];
removeFromProvider(resource, provider);
}
}
/**
* Remove the resource from the sync set of the given provider
* unless the provider is this composite. Subclasses can
* override if they show resources directly.
* @param resource the resource to be removed
* @param provider the provider from which to remove the resource
*/
protected void removeFromProvider(IResource resource, ISynchronizeModelProvider provider) {
if (provider != this) {
provider.getSyncInfoSet().remove(resource);
}
}
@Override
protected void nodeAdded(ISynchronizeModelElement node, AbstractSynchronizeModelProvider provider) {
// Update the resource-to-element map and the element-to-provider map
IResource r = node.getResource();
if(r != null) {
List<ISynchronizeModelElement> elements = resourceToElements.get(r);
if(elements == null) {
elements = new ArrayList<>(2);
resourceToElements.put(r, elements);
}
elements.add(node);
}
elementToProvider.put(node, provider);
super.nodeAdded(node, provider);
}
@Override
public void modelObjectCleared(ISynchronizeModelElement node) {
super.modelObjectCleared(node);
IResource r = node.getResource();
if(r != null) {
List elements = resourceToElements.get(r);
if(elements != null) {
elements.remove(node);
if (elements.isEmpty()) {
resourceToElements.remove(r);
}
}
}
elementToProvider.remove(node);
}
@Override
protected void recursiveClearModelObjects(ISynchronizeModelElement node) {
super.recursiveClearModelObjects(node);
if (node == getModelRoot()) {
clearProviders();
}
}
private void clearProviders() {
for (Iterator iter = providers.iterator(); iter.hasNext();) {
ISynchronizeModelProvider provider = (ISynchronizeModelProvider) iter.next();
provider.dispose();
}
providers.clear();
resourceToElements.clear();
elementToProvider.clear();
}
/**
* Helper method for creating a provider for the given id.
* @param parent the root node for the new provider
* @param id the id of the providers descriptor
* @return the new provider
*/
protected ISynchronizeModelProvider createModelProvider(ISynchronizeModelElement parent, String id, SyncInfoTree syncInfoTree) {
if (id != null && id.endsWith(FlatModelProvider.FlatModelProviderDescriptor.ID)) {
return new FlatModelProvider(this, parent, getConfiguration(), syncInfoTree);
} else if (id != null && id.endsWith(CompressedFoldersModelProvider.CompressedFolderModelProviderDescriptor.ID)) {
return new CompressedFoldersModelProvider(this, parent, getConfiguration(), syncInfoTree);
} else {
return new HierarchicalModelProvider(this, parent, getConfiguration(), syncInfoTree);
}
}
@Override
public void dispose() {
clearProviders();
super.dispose();
}
@Override
protected boolean hasViewerState() {
return resourceToElements != null && !resourceToElements.isEmpty();
}
@Override
protected ISynchronizeModelElement[] getModelObjects(IResource resource) {
List<ISynchronizeModelElement> elements = resourceToElements.get(resource);
if (elements == null) {
return new ISynchronizeModelElement[0];
}
return elements.toArray(new ISynchronizeModelElement[elements.size()]);
}
}