| /******************************************************************************* |
| * 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 |
| * Alexander Gurov - bug 230853 |
| *******************************************************************************/ |
| package org.eclipse.team.internal.ui.synchronize; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.compare.structuremergeviewer.IDiffContainer; |
| import org.eclipse.compare.structuremergeviewer.IDiffElement; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.viewers.ViewerSorter; |
| 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.internal.ui.ITeamUIImages; |
| import org.eclipse.team.internal.ui.TeamUIMessages; |
| import org.eclipse.team.internal.ui.TeamUIPlugin; |
| import org.eclipse.team.ui.synchronize.ISynchronizeModelElement; |
| import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration; |
| |
| public class CompressedFoldersModelProvider extends HierarchicalModelProvider { |
| |
| protected class UnchangedCompressedDiffNode extends UnchangedResourceModelElement { |
| public UnchangedCompressedDiffNode(IDiffContainer parent, IResource resource) { |
| super(parent, resource); |
| } |
| |
| @Override |
| public String getName() { |
| IResource resource = getResource(); |
| return resource.getProjectRelativePath().toString(); |
| } |
| |
| @Override |
| public ImageDescriptor getImageDescriptor(Object object) { |
| return TeamUIPlugin.getImageDescriptor(ITeamUIImages.IMG_COMPRESSED_FOLDER); |
| } |
| } |
| |
| /** |
| * A compressed folder appears under a project and contains out-of-sync resources |
| */ |
| public class CompressedFolderDiffNode extends SyncInfoModelElement { |
| |
| public CompressedFolderDiffNode(IDiffContainer parent, SyncInfo info) { |
| super(parent, info); |
| } |
| |
| @Override |
| public String getName() { |
| IResource resource = getResource(); |
| return resource.getProjectRelativePath().toString(); |
| } |
| |
| @Override |
| public ImageDescriptor getImageDescriptor(Object object) { |
| return TeamUIPlugin.getImageDescriptor(ITeamUIImages.IMG_COMPRESSED_FOLDER); |
| } |
| } |
| |
| public static class CompressedFolderModelProviderDescriptor implements ISynchronizeModelProviderDescriptor { |
| public static final String ID = TeamUIPlugin.ID + ".modelprovider_compressedfolders"; //$NON-NLS-1$ |
| @Override |
| public String getId() { |
| return ID; |
| } |
| @Override |
| public String getName() { |
| return TeamUIMessages.CompressedFoldersModelProvider_0; |
| } |
| @Override |
| public ImageDescriptor getImageDescriptor() { |
| return TeamUIPlugin.getImageDescriptor(ITeamUIImages.IMG_COMPRESSED_FOLDER); |
| } |
| } |
| private static final CompressedFolderModelProviderDescriptor compressedDescriptor = new CompressedFolderModelProviderDescriptor(); |
| |
| public CompressedFoldersModelProvider(ISynchronizePageConfiguration configuration, SyncInfoSet set) { |
| super(configuration, set); |
| } |
| |
| public CompressedFoldersModelProvider( |
| AbstractSynchronizeModelProvider parentProvider, |
| ISynchronizeModelElement modelRoot, |
| ISynchronizePageConfiguration configuration, SyncInfoSet set) { |
| super(parentProvider, modelRoot, configuration, set); |
| } |
| |
| @Override |
| public ISynchronizeModelProviderDescriptor getDescriptor() { |
| return compressedDescriptor; |
| } |
| |
| @Override |
| public ViewerSorter getViewerSorter() { |
| return new SynchronizeModelElementSorter() { |
| @Override |
| protected int compareNames(IResource resource1, IResource resource2) { |
| if (resource1.getType() == IResource.FOLDER && resource2.getType() == IResource.FOLDER) { |
| return collator.compare(resource1.getProjectRelativePath().toString(), resource2.getProjectRelativePath().toString()); |
| } |
| return super.compareNames(resource1, resource2); |
| } |
| }; |
| } |
| |
| @Override |
| protected IDiffElement[] createModelObjects(ISynchronizeModelElement container) { |
| IResource resource = null; |
| if (container == getModelRoot()) { |
| resource = ResourcesPlugin.getWorkspace().getRoot(); |
| } else { |
| resource = container.getResource(); |
| } |
| if(resource != null) { |
| if (resource.getType() == IResource.PROJECT) { |
| return getProjectChildren(container, (IProject)resource); |
| } |
| if (resource.getType() == IResource.FOLDER) { |
| return getFolderChildren(container, resource); |
| } |
| } |
| return super.createModelObjects(container); |
| } |
| |
| private IDiffElement[] getFolderChildren(ISynchronizeModelElement parent, IResource resource) { |
| // Folders will only contain out-of-sync children |
| IResource[] children = getSyncInfoTree().members(resource); |
| List<IDiffElement> result = new ArrayList<>(); |
| for (int i = 0; i < children.length; i++) { |
| IResource child = children[i]; |
| if (child.getType() == IResource.FILE) { |
| result.add(createModelObject(parent, child)); |
| } |
| } |
| return result.toArray(new IDiffElement[result.size()]); |
| } |
| |
| private IDiffElement[] getProjectChildren(ISynchronizeModelElement parent, IProject project) { |
| // The out-of-sync elements could possibly include the project so the code |
| // below is written to ignore the project |
| SyncInfo[] outOfSync = getSyncInfoTree().getSyncInfos(project, IResource.DEPTH_INFINITE); |
| Set<IDiffElement> result = new HashSet<>(); |
| Set<IResource> resourcesToShow = new HashSet<>(); |
| for (int i = 0; i < outOfSync.length; i++) { |
| SyncInfo info = outOfSync[i]; |
| IResource local = info.getLocal(); |
| if (local.getProjectRelativePath().segmentCount() == 1 && local.getType() == IResource.FILE) { |
| resourcesToShow.add(local); |
| } else { |
| if (local.getType() == IResource.FILE) { |
| resourcesToShow.add(local.getParent()); |
| } else if (local.getType() == IResource.FOLDER){ |
| resourcesToShow.add(local); |
| } |
| } |
| } |
| for (Iterator iter = resourcesToShow.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| result.add(createModelObject(parent, resource)); |
| } |
| |
| return result.toArray(new IDiffElement[result.size()]); |
| } |
| |
| @Override |
| protected ISynchronizeModelElement createModelObject(ISynchronizeModelElement parent, IResource resource) { |
| if (resource.getType() == IResource.FOLDER) { |
| SyncInfo info = getSyncInfoTree().getSyncInfo(resource); |
| ISynchronizeModelElement newNode; |
| if(info != null) { |
| newNode = new CompressedFolderDiffNode(parent, info); |
| } else { |
| newNode = new UnchangedCompressedDiffNode(parent, resource); |
| } |
| addToViewer(newNode); |
| return newNode; |
| } |
| return super.createModelObject(parent, resource); |
| } |
| |
| /** |
| * Update the viewer for the sync set additions in the provided event. |
| * This method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>. |
| * Subclasses may override. |
| * @param event |
| */ |
| @Override |
| protected void handleResourceAdditions(ISyncInfoTreeChangeEvent event) { |
| SyncInfo[] infos = event.getAddedResources(); |
| for (int i = 0; i < infos.length; i++) { |
| SyncInfo info = infos[i]; |
| addResource(info); |
| } |
| } |
| |
| @Override |
| protected void addResource(SyncInfo info) { |
| IResource local = info.getLocal(); |
| ISynchronizeModelElement existingNode = getModelObject(local); |
| if (existingNode == null) { |
| if (local.getType() == IResource.FILE) { |
| ISynchronizeModelElement parentNode = getModelObject(local.getParent()); |
| if (parentNode == null) { |
| ISynchronizeModelElement projectNode = getModelObject(local.getProject()); |
| if (projectNode == null) { |
| projectNode = createModelObject(getModelRoot(), local.getProject()); |
| } |
| if (local.getParent().getType() == IResource.PROJECT) { |
| parentNode = projectNode; |
| } else { |
| parentNode = createModelObject(projectNode, local.getParent()); |
| } |
| } |
| createModelObject(parentNode, local); |
| } else { |
| ISynchronizeModelElement projectNode = getModelObject(local.getProject()); |
| if (projectNode == null) { |
| projectNode = createModelObject(getModelRoot(), local.getProject()); |
| } |
| if (local.getProject() != local) { |
| createModelObject(projectNode, local); |
| } |
| } |
| } else { |
| // Either The folder node was added as the parent of a newly added out-of-sync file |
| // or the file was somehow already there so just refresh |
| handleChange(existingNode, info); |
| } |
| } |
| |
| @Override |
| protected void handleResourceRemovals(ISyncInfoTreeChangeEvent event) { |
| IResource[] roots = event.getRemovedSubtreeRoots(); |
| |
| // First, deal with any projects that have been removed |
| List<IResource> removedProjects = new ArrayList<>(); |
| for (int i = 0; i < roots.length; i++) { |
| IResource resource = roots[i]; |
| if (resource.getType() == IResource.PROJECT) { |
| removeFromViewer(resource); |
| removedProjects.add(resource); |
| } |
| } |
| |
| IResource[] resources = event.getRemovedResources(); |
| List<IResource> resourcesToRemove = new ArrayList<IResource>(); |
| List<SyncInfo> resourcesToAdd = new ArrayList<>(); |
| for (int i = 0; i < resources.length; i++) { |
| IResource resource = resources[i]; |
| if (!removedProjects.contains(resource.getProject())) { |
| if (resource.getType() == IResource.FILE) { |
| if (isCompressedParentEmpty(resource) && !isOutOfSync(resource.getParent())) { |
| // The parent compressed folder is also empty so remove it |
| resourcesToRemove.add(resource.getParent()); |
| } else { |
| resourcesToRemove.add(resource); |
| } |
| } else { |
| // A folder has been removed (i.e. is in-sync) |
| // but may still contain children |
| resourcesToRemove.add(resource); |
| resourcesToAdd.addAll(Arrays.asList(getSyncInfosForFileMembers((IContainer)resource))); |
| } |
| } |
| } |
| if (!resourcesToRemove.isEmpty()) { |
| removeFromViewer(resourcesToRemove.toArray(new IResource[resourcesToRemove.size()])); |
| } |
| if (!resourcesToAdd.isEmpty()) { |
| addResources(resourcesToAdd.toArray(new SyncInfo[resourcesToAdd.size()])); |
| } |
| } |
| |
| @Override |
| protected int getLogicalModelDepth(IResource resource) { |
| if(resource.getType() == IResource.PROJECT) { |
| return IResource.DEPTH_INFINITE; |
| } else { |
| return IResource.DEPTH_ONE; |
| } |
| } |
| |
| private boolean isCompressedParentEmpty(IResource resource) { |
| IContainer parent = resource.getParent(); |
| if (parent == null |
| || parent.getType() == IResource.ROOT |
| || parent.getType() == IResource.PROJECT) { |
| return false; |
| } |
| return !hasFileMembers(parent); |
| } |
| |
| private boolean hasFileMembers(IContainer parent) { |
| // Check if the sync set has any file children of the parent |
| IResource[] members = getSyncInfoTree().members(parent); |
| for (int i = 0; i < members.length; i++) { |
| IResource member = members[i]; |
| if (member.getType() == IResource.FILE) { |
| return true; |
| } |
| } |
| // The parent does not contain any files |
| return false; |
| } |
| |
| private SyncInfo[] getSyncInfosForFileMembers(IContainer parent) { |
| // Check if the sync set has any file children of the parent |
| List<SyncInfo> result = new ArrayList<>(); |
| IResource[] members = getSyncInfoTree().members(parent); |
| for (int i = 0; i < members.length; i++) { |
| SyncInfo info = getSyncInfoTree().getSyncInfo(members[i]); |
| if (info != null) { |
| result.add(info); |
| } |
| if (members[i] instanceof IContainer) { |
| result.addAll(Arrays.asList(this.getSyncInfosForFileMembers((IContainer)members[i]))); |
| } |
| } |
| return result.toArray(new SyncInfo[result.size()]); |
| } |
| } |