| /******************************************************************************* |
| * 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()]); |
| } |
| } |