| /******************************************************************************* |
| * Copyright (c) 2006 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.team.internal.core.mapping; |
| |
| import java.util.*; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.mapping.ResourceTraversal; |
| import org.eclipse.core.runtime.IPath; |
| |
| /** |
| * Helper class that accumulates several traversals in order |
| * to generate a final set of traversals and to perform certain |
| * queries on a set of traversals. |
| */ |
| public class CompoundResourceTraversal { |
| |
| private Set deepFolders = new HashSet(); |
| private Set shallowFolders = new HashSet(); |
| private Set zeroFolders = new HashSet(); |
| private Set files = new HashSet(); |
| |
| public synchronized void addTraversals(ResourceTraversal[] traversals) { |
| for (int i = 0; i < traversals.length; i++) { |
| ResourceTraversal traversal = traversals[i]; |
| addTraversal(traversal); |
| } |
| } |
| |
| public synchronized void addTraversal(ResourceTraversal traversal) { |
| IResource[] resources = traversal.getResources(); |
| for (int i = 0; i < resources.length; i++) { |
| IResource resource = resources[i]; |
| addResource(resource, traversal.getDepth()); |
| } |
| } |
| |
| public synchronized void addResource(IResource resource, int depth) { |
| if (resource.getType() == IResource.FILE) { |
| if (!isCovered(resource, IResource.DEPTH_ZERO)) |
| files.add(resource); |
| } |
| switch (depth) { |
| case IResource.DEPTH_INFINITE: |
| addDeepFolder(resource); |
| break; |
| case IResource.DEPTH_ONE: |
| addShallowFolder(resource); |
| break; |
| case IResource.DEPTH_ZERO: |
| addZeroFolder(resource); |
| break; |
| } |
| } |
| |
| private void addShallowFolder(IResource resource) { |
| if (!isCovered(resource, IResource.DEPTH_ONE)) { |
| shallowFolders.add(resource); |
| removeDescendants(resource, IResource.DEPTH_ONE); |
| } |
| } |
| |
| public synchronized boolean isCovered(IResource resource, int depth) { |
| IPath fullPath = resource.getFullPath(); |
| // Regardless of the depth, look for a deep folder that covers the resource |
| for (Iterator iter = deepFolders.iterator(); iter.hasNext();) { |
| IResource deepFolder = (IResource) iter.next(); |
| if (deepFolder.getFullPath().isPrefixOf(fullPath)) { |
| return true; |
| } |
| } |
| // For files, look in the shallow folders and files |
| if (resource.getType() == IResource.FILE) { |
| return (shallowFolders.contains(resource.getParent()) || files.contains(resource)); |
| } |
| // For folders, look in appropriate sets |
| switch (depth) { |
| case IResource.DEPTH_ONE: |
| return (shallowFolders.contains(resource)); |
| case IResource.DEPTH_ZERO: |
| return (shallowFolders.contains(resource.getParent()) || zeroFolders.contains(resource)); |
| } |
| return false; |
| } |
| |
| private void addZeroFolder(IResource resource) { |
| if (!isCovered(resource, IResource.DEPTH_ZERO)) |
| zeroFolders.add(resource); |
| } |
| |
| private void addDeepFolder(IResource resource) { |
| if (!isCovered(resource, IResource.DEPTH_INFINITE)) { |
| deepFolders.add(resource); |
| removeDescendants(resource, IResource.DEPTH_INFINITE); |
| } |
| } |
| |
| private void removeDescendants(IResource resource, int depth) { |
| IPath fullPath = resource.getFullPath(); |
| // First, remove any files that are now covered |
| for (Iterator iter = files.iterator(); iter.hasNext();) { |
| IResource child = (IResource) iter.next(); |
| switch (depth) { |
| case IResource.DEPTH_INFINITE: |
| if (fullPath.isPrefixOf(child.getFullPath())) { |
| iter.remove(); |
| } |
| break; |
| case IResource.DEPTH_ONE: |
| if (fullPath.equals(child.getFullPath().removeLastSegments(1))) { |
| iter.remove(); |
| } |
| break; |
| } |
| } |
| // Now, remove any shallow folders |
| if (depth == IResource.DEPTH_INFINITE) { |
| for (Iterator iter = shallowFolders.iterator(); iter.hasNext();) { |
| IResource child = (IResource) iter.next(); |
| if (fullPath.isPrefixOf(child.getFullPath())) { |
| iter.remove(); |
| } |
| } |
| } |
| // Finally, remove any zero folders |
| for (Iterator iter = zeroFolders.iterator(); iter.hasNext();) { |
| IResource child = (IResource) iter.next(); |
| switch (depth) { |
| case IResource.DEPTH_INFINITE: |
| if (fullPath.isPrefixOf(child.getFullPath())) { |
| iter.remove(); |
| } |
| break; |
| case IResource.DEPTH_ONE: |
| // TODO: Is a zero folder covered by a shallow folder? |
| if (fullPath.equals(child.getFullPath().removeLastSegments(1))) { |
| iter.remove(); |
| } |
| break; |
| } |
| } |
| } |
| |
| public synchronized void add(CompoundResourceTraversal compoundTraversal) { |
| // Technically, this code should synchronize on compoundTraversal. |
| // However, this makes deadlock possible and, in practive, I don't think that |
| // the provided traversal will be modified after it is passed to this method. |
| addResources( |
| (IResource[]) compoundTraversal.deepFolders.toArray(new IResource[compoundTraversal.deepFolders.size()]), |
| IResource.DEPTH_INFINITE); |
| addResources( |
| (IResource[]) compoundTraversal.shallowFolders.toArray(new IResource[compoundTraversal.shallowFolders.size()]), |
| IResource.DEPTH_ONE); |
| addResources( |
| (IResource[]) compoundTraversal.zeroFolders.toArray(new IResource[compoundTraversal.zeroFolders.size()]), |
| IResource.DEPTH_ZERO); |
| addResources( |
| (IResource[]) compoundTraversal.files.toArray(new IResource[compoundTraversal.files.size()]), |
| IResource.DEPTH_ZERO); |
| } |
| |
| public synchronized void addResources(IResource[] resources, int depth) { |
| for (int i = 0; i < resources.length; i++) { |
| IResource resource = resources[i]; |
| addResource(resource, depth); |
| } |
| |
| } |
| |
| /** |
| * Return the resources contained in the given traversals that are not covered by this traversal |
| * @param traversals the traversals being testes |
| * @return the resources contained in the given traversals that are not covered by this traversal |
| */ |
| public synchronized IResource[] getUncoveredResources(ResourceTraversal[] traversals) { |
| CompoundResourceTraversal newTraversals = new CompoundResourceTraversal(); |
| newTraversals.addTraversals(traversals); |
| return getUncoveredResources(newTraversals); |
| } |
| |
| /* |
| * Return any resources in the other traversal that are not covered by this traversal |
| */ |
| private IResource[] getUncoveredResources(CompoundResourceTraversal otherTraversal) { |
| Set result = new HashSet(); |
| for (Iterator iter = otherTraversal.files.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_ZERO)) { |
| result.add(resource); |
| } |
| } |
| for (Iterator iter = otherTraversal.zeroFolders.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_ZERO)) { |
| result.add(resource); |
| } |
| } |
| for (Iterator iter = otherTraversal.shallowFolders.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_ONE)) { |
| result.add(resource); |
| } |
| } |
| for (Iterator iter = otherTraversal.deepFolders.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_INFINITE)) { |
| result.add(resource); |
| } |
| } |
| return (IResource[]) result.toArray(new IResource[result.size()]); |
| } |
| |
| public synchronized ResourceTraversal[] asTraversals() { |
| List result = new ArrayList(); |
| if (!files.isEmpty() || ! zeroFolders.isEmpty()) { |
| Set combined = new HashSet(); |
| combined.addAll(files); |
| combined.addAll(zeroFolders); |
| result.add(new ResourceTraversal((IResource[]) combined.toArray(new IResource[combined.size()]), IResource.DEPTH_ZERO, IResource.NONE)); |
| } |
| if (!shallowFolders.isEmpty()) { |
| result.add(new ResourceTraversal((IResource[]) shallowFolders.toArray(new IResource[shallowFolders.size()]), IResource.DEPTH_ONE, IResource.NONE)); |
| } |
| if (!deepFolders.isEmpty()) { |
| result.add(new ResourceTraversal((IResource[]) deepFolders.toArray(new IResource[deepFolders.size()]), IResource.DEPTH_INFINITE, IResource.NONE)); |
| } |
| return (ResourceTraversal[]) result.toArray(new ResourceTraversal[result.size()]); |
| } |
| |
| public synchronized IResource[] getRoots() { |
| List result = new ArrayList(); |
| result.addAll(files); |
| result.addAll(zeroFolders); |
| result.addAll(shallowFolders); |
| result.addAll(deepFolders); |
| return (IResource[]) result.toArray(new IResource[result.size()]); |
| } |
| |
| public synchronized ResourceTraversal[] getUncoveredTraversals(ResourceTraversal[] traversals) { |
| CompoundResourceTraversal other = new CompoundResourceTraversal(); |
| other.addTraversals(traversals); |
| return getUncoveredTraversals(other); |
| } |
| |
| public ResourceTraversal[] getUncoveredTraversals(CompoundResourceTraversal otherTraversal) { |
| synchronized (otherTraversal) { |
| CompoundResourceTraversal uncovered = new CompoundResourceTraversal(); |
| for (Iterator iter = otherTraversal.files.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_ZERO)) { |
| uncovered.addResource(resource, IResource.DEPTH_ZERO); |
| } |
| } |
| for (Iterator iter = otherTraversal.zeroFolders.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_ZERO)) { |
| uncovered.addResource(resource, IResource.DEPTH_ZERO); |
| } |
| } |
| for (Iterator iter = otherTraversal.shallowFolders.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_ONE)) { |
| uncovered.addResource(resource, IResource.DEPTH_ONE); |
| } |
| } |
| for (Iterator iter = otherTraversal.deepFolders.iterator(); iter.hasNext();) { |
| IResource resource = (IResource) iter.next(); |
| if (!isCovered(resource, IResource.DEPTH_INFINITE)) { |
| uncovered.addResource(resource, IResource.DEPTH_INFINITE); |
| } |
| } |
| return uncovered.asTraversals(); |
| } |
| } |
| |
| public synchronized void clear() { |
| deepFolders.clear(); |
| shallowFolders.clear(); |
| zeroFolders.clear(); |
| files.clear(); |
| } |
| |
| } |