| package org.eclipse.team.internal.ccvs.core.resources; |
| |
| /* |
| * (c) Copyright IBM Corp. 2000, 2001. |
| * All Rights Reserved. |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceStatus; |
| import org.eclipse.core.resources.IResourceVisitor; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.team.internal.ccvs.core.CVSException; |
| import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin; |
| import org.eclipse.team.internal.ccvs.core.CVSStatus; |
| import org.eclipse.team.internal.ccvs.core.ICVSFolder; |
| import org.eclipse.team.internal.ccvs.core.ICVSResource; |
| import org.eclipse.team.internal.ccvs.core.ICVSRunnable; |
| import org.eclipse.team.internal.ccvs.core.Policy; |
| import org.eclipse.team.internal.ccvs.core.syncinfo.BaserevInfo; |
| import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo; |
| import org.eclipse.team.internal.ccvs.core.syncinfo.NotifyInfo; |
| import org.eclipse.team.internal.ccvs.core.syncinfo.ReentrantLock; |
| import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo; |
| import org.eclipse.team.internal.ccvs.core.util.Assert; |
| import org.eclipse.team.internal.ccvs.core.util.SyncFileWriter; |
| |
| /** |
| * A synchronizer is responsible for managing synchronization information for local |
| * CVS resources. |
| * |
| * Special processing has been added for linked folders and their childen so |
| * that their CVS meta files are never read or written. |
| * |
| * @see ResourceSyncInfo |
| * @see FolderSyncInfo |
| */ |
| public class EclipseSynchronizer { |
| protected static final String IS_DIRTY_INDICATOR = SyncInfoCache.IS_DIRTY_INDICATOR; |
| protected static final String NOT_DIRTY_INDICATOR = SyncInfoCache.NOT_DIRTY_INDICATOR; |
| |
| // the cvs eclipse synchronizer is a singleton |
| private static EclipseSynchronizer instance; |
| |
| // track resources that have changed in a given operation |
| private ReentrantLock lock = new ReentrantLock(); |
| |
| private Set changedResources = new HashSet(); |
| private Set changedFolders = new HashSet(); |
| |
| private SessionPropertySyncInfoCache sessionPropertyCache = new SessionPropertySyncInfoCache(); |
| private SynchronizerSyncInfoCache synchronizerCache = new SynchronizerSyncInfoCache(); |
| |
| /* |
| * Package private contructor to allow specialized subclass for handling folder deletions |
| */ |
| EclipseSynchronizer() { |
| } |
| |
| /** |
| * Returns the singleton instance of the synchronizer. |
| */ |
| public static EclipseSynchronizer getInstance() { |
| if(instance==null) { |
| instance = new EclipseSynchronizer(); |
| } |
| return instance; |
| } |
| |
| public SyncInfoCache getSyncInfoCacheFor(IResource resource) { |
| if (resource.exists()) { |
| return sessionPropertyCache; |
| } else { |
| return synchronizerCache; |
| } |
| } |
| |
| private boolean isValid(IResource resource) { |
| return resource.exists() || resource.isPhantom(); |
| } |
| |
| /** |
| * Sets the folder sync info for the specified folder. |
| * The folder must exist and must not be the workspace root. |
| * |
| * @param folder the folder |
| * @param info the folder sync info, must not be null |
| * @see #getFolderSync, #deleteFolderSync |
| */ |
| public void setFolderSync(IContainer folder, FolderSyncInfo info) throws CVSException { |
| Assert.isNotNull(info); // enforce the use of deleteFolderSync |
| // ignore folder sync on the root (i.e. CVSROOT/config/TopLevelAdmin=yes but we just ignore it) |
| if (folder.getType() == IResource.ROOT) return; |
| if (!isValid(folder)) { |
| throw new CVSException(IStatus.ERROR, CVSException.UNABLE, |
| Policy.bind("EclipseSynchronizer.ErrorSettingFolderSync", folder.getFullPath().toString())); //$NON-NLS-1$ |
| } |
| try { |
| beginOperation(null); |
| // set folder sync and notify |
| getSyncInfoCacheFor(folder).setCachedFolderSync(folder, info); |
| changedFolders.add(folder); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Gets the folder sync info for the specified folder. |
| * |
| * @param folder the folder |
| * @return the folder sync info associated with the folder, or null if none. |
| * @see #setFolderSync, #deleteFolderSync |
| */ |
| public FolderSyncInfo getFolderSync(IContainer folder) throws CVSException { |
| if (folder.getType() == IResource.ROOT || !isValid(folder)) return null; |
| try { |
| beginOperation(null); |
| cacheFolderSync(folder); |
| return getSyncInfoCacheFor(folder).getCachedFolderSync(folder); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Deletes the folder sync for the specified folder and the resource sync |
| * for all of its children. Does not recurse. |
| * |
| * @param folder the folder |
| * @see #getFolderSync, #setFolderSync |
| */ |
| public void deleteFolderSync(IContainer folder) throws CVSException { |
| if (folder.getType() == IResource.ROOT || !isValid(folder)) return; |
| try { |
| beginOperation(null); |
| // iterate over all children with sync info and prepare notifications |
| // this is done first since deleting the folder sync may remove a phantom |
| cacheResourceSyncForChildren(folder); |
| IResource[] children = folder.members(true); |
| for (int i = 0; i < children.length; i++) { |
| IResource resource = children[i]; |
| changedResources.add(resource); |
| // delete resource sync for all children |
| getSyncInfoCacheFor(resource).setCachedSyncBytes(resource, null); |
| } |
| // delete folder sync |
| getSyncInfoCacheFor(folder).setCachedFolderSync(folder, null); |
| changedFolders.add(folder); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Sets the resource sync info for the specified resource. |
| * The parent folder must exist and must not be the workspace root. |
| * |
| * @param resource the resource |
| * @param info the resource sync info, must not be null |
| * @see #getResourceSync, #deleteResourceSync |
| */ |
| public void setResourceSync(IResource resource, ResourceSyncInfo info) throws CVSException { |
| Assert.isNotNull(info); // enforce the use of deleteResourceSync |
| IContainer parent = resource.getParent(); |
| if (parent == null || parent.getType() == IResource.ROOT || !isValid(parent)) { |
| throw new CVSException(IStatus.ERROR, CVSException.UNABLE, |
| Policy.bind("EclipseSynchronizer.ErrorSettingResourceSync", resource.getFullPath().toString())); //$NON-NLS-1$ |
| } |
| try { |
| beginOperation(null); |
| // cache resource sync for siblings, set for self, then notify |
| cacheResourceSyncForChildren(parent); |
| setCachedResourceSync(resource, info); |
| changedResources.add(resource); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Gets the resource sync info for the specified folder. |
| * |
| * @param resource the resource |
| * @return the resource sync info associated with the resource, or null if none. |
| * @see #setResourceSync, #deleteResourceSync |
| */ |
| public ResourceSyncInfo getResourceSync(IResource resource) throws CVSException { |
| byte[] info = getSyncBytes(resource); |
| if (info == null) return null; |
| return new ResourceSyncInfo(info); |
| } |
| |
| /** |
| * Gets the resource sync info for the specified folder. |
| * |
| * @param resource the resource |
| * @return the resource sync info associated with the resource, or null if none. |
| * @see #setResourceSync, #deleteResourceSync |
| */ |
| public byte[] getSyncBytes(IResource resource) throws CVSException { |
| IContainer parent = resource.getParent(); |
| if (parent == null || parent.getType() == IResource.ROOT || !isValid(parent)) return null; |
| try { |
| beginOperation(null); |
| // cache resource sync for siblings, then return for self |
| try { |
| cacheResourceSyncForChildren(parent); |
| } catch (CVSException e) { |
| if (e.getStatus().getCode() == IResourceStatus.WORKSPACE_LOCKED) { |
| // This can occur if the resource sync is loaded during the POST_CHANGE delta phase. |
| // We will resort to loading the sync info for the requested resource from disk |
| return getSyncBytesFromDisk(resource); |
| } else { |
| throw e; |
| } |
| } |
| return getCachedSyncBytes(resource); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Sets the resource sync info for the specified resource. |
| * The parent folder must exist and must not be the workspace root. |
| * |
| * @param resource the resource |
| * @param info the resource sync info, must not be null |
| * @see #getResourceSync, #deleteResourceSync |
| */ |
| public void setSyncBytes(IResource resource, byte[] syncBytes) throws CVSException { |
| Assert.isNotNull(syncBytes); // enforce the use of deleteResourceSync |
| IContainer parent = resource.getParent(); |
| if (parent == null || parent.getType() == IResource.ROOT || !isValid(parent)) { |
| throw new CVSException(IStatus.ERROR, CVSException.UNABLE, |
| Policy.bind("EclipseSynchronizer.ErrorSettingResourceSync", resource.getFullPath().toString())); //$NON-NLS-1$ |
| } |
| try { |
| beginOperation(null); |
| // cache resource sync for siblings, set for self, then notify |
| cacheResourceSyncForChildren(parent); |
| setCachedSyncBytes(resource, syncBytes); |
| changedResources.add(resource); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Deletes the resource sync info for the specified resource, if it exists. |
| * |
| * @param resource the resource |
| * @see #getResourceSync, #setResourceSync |
| */ |
| public void deleteResourceSync(IResource resource) throws CVSException { |
| IContainer parent = resource.getParent(); |
| if (parent == null || parent.getType() == IResource.ROOT || !isValid(parent)) return; |
| try { |
| beginOperation(null); |
| // cache resource sync for siblings, delete for self, then notify |
| cacheResourceSyncForChildren(parent); |
| if (getCachedSyncBytes(resource) != null) { // avoid redundant notifications |
| setCachedSyncBytes(resource, null); |
| changedResources.add(resource); |
| } |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Gets the array of ignore patterns for the specified folder. |
| * |
| * @param folder the folder |
| * @return the patterns, or an empty array if none |
| * @see #addIgnored |
| */ |
| public String[] getIgnored(IContainer folder) throws CVSException { |
| if (folder.getType() == IResource.ROOT || ! folder.exists()) return SessionPropertySyncInfoCache.NULL_IGNORES; |
| try { |
| beginOperation(null); |
| return cacheFolderIgnores(folder); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Adds a pattern to the set of ignores for the specified folder. |
| * |
| * @param folder the folder |
| * @param pattern the pattern |
| */ |
| public void addIgnored(IContainer folder, String pattern) throws CVSException { |
| if (folder.getType() == IResource.ROOT || ! folder.exists()) { |
| throw new CVSException(IStatus.ERROR, CVSException.UNABLE, |
| Policy.bind("EclipseSynchronizer.ErrorSettingIgnorePattern", folder.getFullPath().toString())); //$NON-NLS-1$ |
| } |
| try { |
| beginOperation(null); |
| String[] ignores = cacheFolderIgnores(folder); |
| if (ignores != null) { |
| // verify that the pattern has not already been added |
| for (int i = 0; i < ignores.length; i++) { |
| if (ignores[i].equals(pattern)) return; |
| } |
| // add the pattern |
| String[] oldIgnores = ignores; |
| ignores = new String[oldIgnores.length + 1]; |
| System.arraycopy(oldIgnores, 0, ignores, 0, oldIgnores.length); |
| ignores[oldIgnores.length] = pattern; |
| } else { |
| ignores = new String[] { pattern }; |
| } |
| setCachedFolderIgnores(folder, ignores); |
| SyncFileWriter.writeCVSIgnoreEntries(folder, ignores); |
| // broadcast changes to unmanaged children - they are the only candidates for being ignored |
| List possibleIgnores = new ArrayList(); |
| accumulateNonManagedChildren(folder, possibleIgnores); |
| CVSProviderPlugin.broadcastSyncInfoChanges((IResource[])possibleIgnores.toArray(new IResource[possibleIgnores.size()])); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Returns the members of this folder including deleted resources with sync info, |
| * but excluding special resources such as CVS subdirectories. |
| * |
| * @param folder the container to list |
| * @return the array of members |
| */ |
| public IResource[] members(IContainer folder) throws CVSException { |
| if (! isValid(folder)) return new IResource[0]; |
| try { |
| beginOperation(null); |
| if (folder.getType() != IResource.ROOT) { |
| // ensure that the sync info is cached so any required phantoms are created |
| cacheResourceSyncForChildren(folder); |
| } |
| return folder.members(true); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Begins a batch of operations. |
| * |
| * @param monitor the progress monitor, may be null |
| */ |
| public void beginOperation(IProgressMonitor monitor) throws CVSException { |
| lock.acquire(); |
| |
| if (lock.getNestingCount() == 1) { |
| prepareCache(monitor); |
| } |
| } |
| |
| /** |
| * Ends a batch of operations. Pending changes are committed only when |
| * the number of calls to endOperation() balances those to beginOperation(). |
| * <p> |
| * Progress cancellation is ignored while writting the cache to disk. This |
| * is to ensure cache to disk consistency. |
| * </p> |
| * |
| * @param monitor the progress monitor, may be null |
| * @exception CVSException with a status with code <code>COMMITTING_SYNC_INFO_FAILED</code> |
| * if all the CVS sync information could not be written to disk. |
| */ |
| public void endOperation(IProgressMonitor monitor) throws CVSException { |
| try { |
| IStatus status = SyncInfoCache.STATUS_OK; |
| if (lock.getNestingCount() == 1) { |
| status = commitCache(monitor); |
| } |
| if (!status.isOK()) { |
| throw new CVSException(status); |
| } |
| } finally { |
| lock.release(); |
| } |
| } |
| |
| /** |
| * Flushes unwritten sync information to disk. |
| * <p> |
| * Recursively commits unwritten sync information for all resources |
| * below the root, and optionally purges the cached data from memory |
| * so that the next time it is accessed it will be retrieved from disk. |
| * May flush more sync information than strictly needed, but never less. |
| * </p> |
| * <p> |
| * Will throw a CVS Exception with a status with code = CVSStatus.DELETION_FAILED |
| * if the flush could not perform CVS folder deletions. In this case, all other |
| * aspects of the operation succeeded. |
| * </p> |
| * |
| * @param root the root of the subtree to flush |
| * @param purgeCache if true, purges the cache from memory as well |
| * @param deep purge sync from child folders |
| * @param monitor the progress monitor, may be null |
| */ |
| public void flush(IContainer root, boolean purgeCache, boolean deep, IProgressMonitor monitor) throws CVSException { |
| // flush unwritten sync info to disk |
| monitor = Policy.monitorFor(monitor); |
| monitor.beginTask(null, 10); |
| try { |
| beginOperation(Policy.subMonitorFor(monitor, 1)); |
| |
| IStatus status = commitCache(Policy.subMonitorFor(monitor, 7)); |
| |
| // purge from memory too if we were asked to |
| if (purgeCache) sessionPropertyCache.purgeCache(root, deep); |
| |
| // prepare for the operation again if we cut the last one short |
| prepareCache(Policy.subMonitorFor(monitor, 1)); |
| |
| if (!status.isOK()) { |
| throw new CVSException(status); |
| } |
| } finally { |
| endOperation(Policy.subMonitorFor(monitor, 1)); |
| monitor.done(); |
| } |
| } |
| |
| private void purgeCache(IResource resource, boolean deep) throws CVSException { |
| sessionPropertyCache.purgeResourceSyncCache(resource); |
| if (resource.getType() != IResource.FILE) { |
| sessionPropertyCache.purgeCache((IContainer)resource, deep); |
| } |
| } |
| |
| /** |
| * Called to notify the synchronizer that meta files have changed on disk, outside |
| * of the workbench. The cache will be flushed for this folder and it's immediate |
| * children and appropriate state change events are broadcasts to state change |
| * listeners. |
| */ |
| public void syncFilesChanged(IContainer[] roots) throws CVSException { |
| try { |
| for (int i = 0; i < roots.length; i++) { |
| IContainer root = roots[i]; |
| flush(root, true, false /*don't flush children*/, null); |
| List changedPeers = new ArrayList(); |
| changedPeers.add(root); |
| changedPeers.addAll(Arrays.asList(root.members())); |
| IResource[] resources = (IResource[]) changedPeers.toArray(new IResource[changedPeers.size()]); |
| CVSProviderPlugin.broadcastSyncInfoChanges(resources); |
| CVSProviderPlugin.getPlugin().getFileModificationManager().syncInfoChanged(resources); |
| } |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * The folder is about to be deleted (including its CVS subfolder). |
| * Take any appropriate action to remember the CVS information. |
| */ |
| public void prepareForDeletion(IResource resource) throws CVSException { |
| if (!resource.exists()) return; |
| try { |
| beginOperation(null); |
| // Flush the dirty info for the resource and it's ancestors. |
| // Although we could be smarter, we need to do this because the |
| // deletion may fail. |
| String indicator = EclipseSynchronizer.getInstance().getDirtyIndicator(resource); |
| if (indicator != null) { |
| flushDirtyCacheWithAncestors(resource); |
| } |
| if (resource.getType() == IResource.FILE) { |
| byte[] syncBytes = getSyncBytes(resource); |
| if (syncBytes != null) { |
| if (!ResourceSyncInfo.isAddition(syncBytes)) { |
| syncBytes = convertToDeletion(syncBytes); |
| synchronizerCache.setCachedSyncBytes(resource, syncBytes); |
| } |
| changedResources.add(resource); |
| } |
| } else { |
| IContainer container = (IContainer)resource; |
| if (container.getType() == IResource.PROJECT) { |
| synchronizerCache.flush((IProject)container); |
| } else { |
| // Move the folder sync info into phantom space |
| FolderSyncInfo info = getFolderSync(container); |
| if (info == null) return; |
| synchronizerCache.setCachedFolderSync(container, info); |
| changedFolders.add(container); |
| // move the resource sync as well |
| byte[] syncBytes = getSyncBytes(resource); |
| synchronizerCache.setCachedSyncBytes(resource, syncBytes); |
| // todo |
| // Move the dirty count into phantom space |
| int dirtyCount = getDirtyCount(container); |
| if (dirtyCount != -1) { |
| synchronizerCache.setCachedDirtyCount(container, dirtyCount); |
| } |
| } |
| } |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Prepare for a move or delete within the move/delete hook by moving the |
| * sync info into phantom space and flushing the session properties cache. |
| * This will allow sync info for deletions to be maintained in the source |
| * location and sync info at the destination to be preserved as well. |
| * |
| * @param resource |
| * @param monitor |
| * @throws CVSException |
| */ |
| public void prepareForMoveDelete(IResource resource, IProgressMonitor monitor) throws CVSException { |
| // Move sync info to phantom space for the resource and all it's children |
| try { |
| resource.accept(new IResourceVisitor() { |
| public boolean visit(IResource resource) throws CoreException { |
| try { |
| prepareForDeletion(resource); |
| } catch (CVSException e) { |
| CVSProviderPlugin.log(e); |
| throw new CoreException(e.getStatus()); |
| } |
| return true; |
| } |
| }); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| // purge the sync info to clear the session properties |
| purgeCache(resource, true); |
| } |
| |
| public void created(IResource resource) throws CVSException { |
| if (resource.getType() == IResource.FILE) { |
| created((IFile)resource); |
| } else if (resource.getType() == IResource.FOLDER) { |
| created((IFolder)resource); |
| } |
| } |
| |
| /** |
| * Notify the receiver that a folder has been created. |
| * Any existing phantom sync info will be moved |
| * |
| * @param folder the folder that has been created |
| */ |
| public void created(IFolder folder) throws CVSException { |
| try { |
| // set the dirty count using what was cached in the phantom it |
| beginOperation(null); |
| FolderSyncInfo folderInfo = synchronizerCache.getCachedFolderSync(folder); |
| byte[] syncBytes = synchronizerCache.getCachedSyncBytes(folder); |
| if (folderInfo != null && syncBytes != null) { |
| if (folder.getFolder(SyncFileWriter.CVS_DIRNAME).exists()) { |
| // There is already a CVS subdirectory which indicates that |
| // either the folder was recreated by an external tool or that |
| // a folder with CVS information was copied from another location. |
| // To know the difference, we need to compare the folder sync info. |
| // If they are mapped to the same root and repository then just |
| // purge the phantom info. Otherwise, keep the original sync info. |
| |
| // Get the new folder sync info |
| FolderSyncInfo newFolderInfo = getFolderSync(folder); |
| if (newFolderInfo.getRoot().equals(folderInfo.getRoot()) |
| && newFolderInfo.getRepository().equals(folderInfo.getRepository())) { |
| // The folder is the same so use what is on disk |
| return; |
| } |
| |
| // The folder is mapped to a different location. |
| // Purge new resource sync before restoring from phantom |
| ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(folder); |
| ICVSResource[] children = cvsFolder.members(ICVSFolder.MANAGED_MEMBERS); |
| for (int i = 0; i < children.length; i++) { |
| ICVSResource resource = children[i]; |
| deleteResourceSync(resource.getIResource()); |
| } |
| } |
| |
| // set the sync info using what was cached in the phantom |
| setFolderSync(folder, folderInfo); |
| setCachedSyncBytes(folder, syncBytes); |
| } |
| } finally { |
| try { |
| endOperation(null); |
| } finally { |
| synchronizerCache.flush(folder); |
| } |
| } |
| } |
| |
| /** |
| * Notify the receiver that a file has been created. Any existing phantom |
| * sync info will be moved |
| * |
| * @param file the file that has been created |
| */ |
| public void created(IFile file) throws CVSException { |
| try { |
| // set the dirty count using what was cached in the phantom it |
| beginOperation(null); |
| byte[] syncBytes = synchronizerCache.getCachedSyncBytes(file); |
| if (syncBytes == null) return; |
| byte[] newBytes = getSyncBytes(file); |
| if (newBytes == null) { |
| // only move the sync info if there is no new sync info |
| setSyncBytes(file, convertFromDeletion(syncBytes)); |
| } |
| } finally { |
| try { |
| endOperation(null); |
| } finally { |
| synchronizerCache.setCachedSyncBytes(file, null); |
| } |
| } |
| } |
| |
| /** |
| * If not already cached, loads and caches the resource sync for the children of the container. |
| * Folder must exist and must not be the workspace root. |
| * |
| * @param container the container |
| */ |
| private void cacheResourceSyncForChildren(IContainer container) throws CVSException { |
| // don't try to load if the information is already cached |
| if (! getSyncInfoCacheFor(container).isResourceSyncInfoCached(container)) { |
| // load the sync info from disk |
| byte[][] infos; |
| // do not load the sync info for resources that are linked |
| if (isLinkedResource(container)) { |
| infos = null; |
| } else { |
| infos = SyncFileWriter.readAllResourceSync(container); |
| } |
| if (infos != null) { |
| for (int i = 0; i < infos.length; i++) { |
| byte[] syncBytes = infos[i]; |
| IPath name = new Path(getName(syncBytes)); |
| IResource resource; |
| if (isFolder(syncBytes)) { |
| resource = container.getFolder(name); |
| } else { |
| resource = container.getFile(name); |
| } |
| getSyncInfoCacheFor(resource).setCachedSyncBytes(resource, syncBytes); |
| } |
| } |
| getSyncInfoCacheFor(container).setResourceSyncInfoCached(container); |
| } |
| } |
| |
| /** |
| * If not already cached, loads and caches the folder sync for the |
| * container. Folder must exist and must not be the workspace root. |
| * |
| * @param container the container |
| */ |
| private void cacheFolderSync(IContainer container) throws CVSException { |
| // don't try to load if the information is already cached |
| if (! getSyncInfoCacheFor(container).isFolderSyncInfoCached(container)) { |
| // load the sync info from disk |
| FolderSyncInfo info; |
| // do not load the sync info for resources that are linked |
| if (isLinkedResource(container)) { |
| info = null; |
| } else { |
| info = SyncFileWriter.readFolderSync(container); |
| } |
| getSyncInfoCacheFor(container).setCachedFolderSync(container, info); |
| } |
| } |
| |
| private boolean isLinkedResource(IResource resource) { |
| return CVSWorkspaceRoot.isLinkedResource(resource); |
| } |
| |
| /** |
| * Load the sync info for the given resource from disk |
| * @param resource |
| * @return byte[] |
| */ |
| private byte[] getSyncBytesFromDisk(IResource resource) throws CVSException { |
| byte[][] infos = SyncFileWriter.readAllResourceSync(resource.getParent()); |
| if (infos == null) return null; |
| for (int i = 0; i < infos.length; i++) { |
| byte[] syncBytes = infos[i]; |
| if (resource.getName().equals(getName(syncBytes))) { |
| return syncBytes; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Prepares the cache for a series of operations. |
| * |
| * @param monitor the progress monitor, may be null |
| */ |
| private void prepareCache(IProgressMonitor monitor) throws CVSException { |
| } |
| |
| /** |
| * Commits the cache after a series of operations. |
| * |
| * Will return STATUS_OK unless there were problems writting sync |
| * information to disk. If an error occurs a multistatus is returned |
| * with the list of reasons for the failures. Failures are recovered, |
| * and all changed resources are given a chance to be written to disk. |
| * |
| * @param monitor the progress monitor, may be null |
| */ |
| private IStatus commitCache(IProgressMonitor monitor) { |
| if (changedFolders.isEmpty() && changedResources.isEmpty()) { |
| return SyncInfoCache.STATUS_OK; |
| } |
| List errors = new ArrayList(); |
| try { |
| /*** prepare operation ***/ |
| // find parents of changed resources |
| Set dirtyParents = new HashSet(); |
| for(Iterator it = changedResources.iterator(); it.hasNext();) { |
| IResource resource = (IResource) it.next(); |
| IContainer folder = resource.getParent(); |
| dirtyParents.add(folder); |
| } |
| |
| monitor = Policy.monitorFor(monitor); |
| int numDirty = dirtyParents.size(); |
| int numResources = changedFolders.size() + numDirty; |
| monitor.beginTask(null, numResources); |
| if(monitor.isCanceled()) { |
| monitor.subTask(Policy.bind("EclipseSynchronizer.UpdatingSyncEndOperationCancelled")); //$NON-NLS-1$ |
| } else { |
| monitor.subTask(Policy.bind("EclipseSynchronizer.UpdatingSyncEndOperation")); //$NON-NLS-1$ |
| } |
| |
| /*** write sync info to disk ***/ |
| // folder sync info changes |
| for(Iterator it = changedFolders.iterator(); it.hasNext();) { |
| IContainer folder = (IContainer) it.next(); |
| if (folder.exists() && folder.getType() != IResource.ROOT) { |
| try { |
| FolderSyncInfo info = sessionPropertyCache.getCachedFolderSync(folder); |
| // Do not write the folder sync for linked resources |
| if (info == null) { |
| // deleted folder sync info since we loaded it |
| // (but don't overwrite the sync info for linked folders |
| if (!isLinkedResource(folder)) |
| SyncFileWriter.deleteFolderSync(folder); |
| dirtyParents.remove(folder); |
| } else { |
| // modified or created new folder sync info since we loaded it |
| SyncFileWriter.writeFolderSync(folder, info); |
| } |
| } catch(CVSException e) { |
| try { |
| sessionPropertyCache.purgeCache(folder, true /* deep */); |
| } catch(CVSException pe) { |
| errors.add(pe.getStatus()); |
| } |
| errors.add(e.getStatus()); |
| } |
| } |
| monitor.worked(1); |
| } |
| |
| // update progress for parents we will skip because they were deleted |
| monitor.worked(numDirty - dirtyParents.size()); |
| |
| // resource sync info changes |
| for (Iterator it = dirtyParents.iterator(); it.hasNext();) { |
| IContainer folder = (IContainer) it.next(); |
| if (folder.exists() && folder.getType() != IResource.ROOT) { |
| // write sync info for all children in one go |
| try { |
| List infos = new ArrayList(); |
| IResource[] children = folder.members(true); |
| for (int i = 0; i < children.length; i++) { |
| IResource resource = children[i]; |
| byte[] syncBytes = getSyncBytes(resource); |
| if (syncBytes != null) { |
| infos.add(syncBytes); |
| } |
| } |
| // do not overwrite the sync info for linked resources |
| if (infos.size() > 0 || !isLinkedResource(folder)) |
| SyncFileWriter.writeAllResourceSync(folder, |
| (byte[][]) infos.toArray(new byte[infos.size()][])); |
| } catch(CVSException e) { |
| try { |
| sessionPropertyCache.purgeCache(folder, false /* depth 1 */); |
| } catch(CVSException pe) { |
| errors.add(pe.getStatus()); |
| } |
| errors.add(e.getStatus()); |
| } catch (CoreException e) { |
| try { |
| sessionPropertyCache.purgeCache(folder, false /* depth 1 */); |
| } catch(CVSException pe) { |
| errors.add(pe.getStatus()); |
| } |
| errors.add(e.getStatus()); |
| } |
| } |
| monitor.worked(1); |
| } |
| |
| /*** broadcast events ***/ |
| changedResources.addAll(changedFolders); |
| IResource[] resources = (IResource[]) changedResources.toArray( |
| new IResource[changedResources.size()]); |
| broadcastResourceStateChanges(resources); |
| changedResources.clear(); |
| changedFolders.clear(); |
| if ( ! errors.isEmpty()) { |
| MultiStatus status = new MultiStatus(CVSProviderPlugin.ID, |
| CVSStatus.COMMITTING_SYNC_INFO_FAILED, |
| Policy.bind("EclipseSynchronizer.ErrorCommitting"), //$NON-NLS-1$ |
| null); |
| for (int i = 0; i < errors.size(); i++) { |
| status.merge((IStatus)errors.get(i)); |
| } |
| return status; |
| } |
| return SyncInfoCache.STATUS_OK; |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * Broadcasts the resource state changes for the given resources to CVS Provider Plugin |
| */ |
| void broadcastResourceStateChanges(IResource[] resources) { |
| if (resources.length > 0) { |
| CVSProviderPlugin.broadcastSyncInfoChanges(resources); |
| } |
| } |
| |
| /** |
| * Returns the resource sync info for the resource; null if none. |
| * Parent must exist and must not be the workspace root. |
| * The resource sync info for the children of the parent container MUST ALREADY BE CACHED. |
| * |
| * @param resource the resource |
| * @return the resource sync info for the resource, or null |
| * @see #cacheResourceSyncForChildren |
| */ |
| private byte[] getCachedSyncBytes(IResource resource) throws CVSException { |
| return getSyncInfoCacheFor(resource).getCachedSyncBytes(resource); |
| } |
| |
| /** |
| * Returns the resource sync info for the resource; null if none. |
| * Parent must exist and must not be the workspace root. |
| * The resource sync info for the children of the parent container MUST ALREADY BE CACHED. |
| * |
| * @param resource the resource |
| * @return the resource sync info for the resource, or null |
| * @see #cacheResourceSyncForChildren |
| */ |
| private void setCachedSyncBytes(IResource resource, byte[] syncBytes) throws CVSException { |
| getSyncInfoCacheFor(resource).setCachedSyncBytes(resource, syncBytes); |
| } |
| |
| /** |
| * Sets the resource sync info for the resource; if null, deletes it. Parent |
| * must exist and must not be the workspace root. The resource sync info for |
| * the children of the parent container MUST ALREADY BE CACHED. |
| * |
| * @param resource the resource |
| * @param info the new resource sync info |
| * @see #cacheResourceSyncForChildren |
| */ |
| private void setCachedResourceSync(IResource resource, ResourceSyncInfo info) throws CVSException { |
| //todo |
| byte[] syncBytes = null; |
| if (info != null) syncBytes = info.getBytes(); |
| getSyncInfoCacheFor(resource).setCachedSyncBytes(resource, syncBytes); |
| } |
| |
| /** |
| * If not already cached, loads and caches the folder ignores sync for the container. |
| * Folder must exist and must not be the workspace root. |
| * |
| * @param container the container |
| * @return the folder ignore patterns, or an empty array if none |
| */ |
| private String[] cacheFolderIgnores(IContainer container) throws CVSException { |
| return sessionPropertyCache.cacheFolderIgnores(container); |
| } |
| |
| /** |
| * Sets the array of folder ignore patterns for the container, must not be null. |
| * Folder must exist and must not be the workspace root. |
| * |
| * @param container the container |
| * @param ignores the array of ignore patterns |
| */ |
| private void setCachedFolderIgnores(IContainer container, String[] ignores) throws CVSException { |
| sessionPropertyCache.setCachedFolderIgnores(container, ignores); |
| } |
| |
| /** |
| * Recursively adds to the possibleIgnores list all children of the given |
| * folder that can be ignored. |
| * |
| * @param folder the folder to be searched |
| * @param possibleIgnores the list of IResources that can be ignored |
| */ |
| private void accumulateNonManagedChildren(IContainer folder, List possibleIgnores) throws CVSException { |
| try { |
| cacheResourceSyncForChildren(folder); |
| IResource[] children = folder.members(); |
| List folders = new ArrayList(); |
| // deal with all files first and then folders to be otimized for caching scheme |
| for (int i = 0; i < children.length; i++) { |
| IResource child = children[i]; |
| if(getCachedSyncBytes(child)==null) { |
| possibleIgnores.add(child); |
| } |
| if(child.getType()!=IResource.FILE) { |
| folders.add(child); |
| } |
| } |
| for (Iterator iter = folders.iterator(); iter.hasNext();) { |
| IContainer child = (IContainer) iter.next(); |
| accumulateNonManagedChildren(child, possibleIgnores); |
| } |
| } catch(CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * Add the entry to the CVS/Notify file. We are not initially concerned with efficiency |
| * since edit/unedit are typically issued on a small set of files. |
| * |
| * XXX If there was a previous notify entry for the resource, it is replaced. This is |
| * probably not the proper behavior (see EclipseFile). |
| * |
| * @param resource |
| * @param info |
| */ |
| public void setNotifyInfo(IResource resource, NotifyInfo info) throws CVSException { |
| NotifyInfo[] infos = SyncFileWriter.readAllNotifyInfo(resource.getParent()); |
| if (infos == null) { |
| infos = new NotifyInfo[] { info }; |
| } else { |
| Map infoMap = new HashMap(); |
| for (int i = 0; i < infos.length; i++) { |
| NotifyInfo notifyInfo = infos[i]; |
| infoMap.put(infos[i].getName(), infos[i]); |
| } |
| infoMap.put(info.getName(), info); |
| NotifyInfo[] newInfos = new NotifyInfo[infoMap.size()]; |
| int i = 0; |
| for (Iterator iter = infoMap.values().iterator(); iter.hasNext();) { |
| newInfos[i++] = (NotifyInfo) iter.next(); |
| } |
| infos = newInfos; |
| } |
| SyncFileWriter.writeAllNotifyInfo(resource.getParent(), infos); |
| } |
| |
| /** |
| * Method getNotifyInfo. |
| * @param resource |
| * @return NotifyInfo |
| */ |
| public NotifyInfo getNotifyInfo(IResource resource) throws CVSException { |
| NotifyInfo[] infos = SyncFileWriter.readAllNotifyInfo(resource.getParent()); |
| if (infos == null) return null; |
| for (int i = 0; i < infos.length; i++) { |
| NotifyInfo notifyInfo = infos[i]; |
| if (notifyInfo.getName().equals(resource.getName())) { |
| return notifyInfo; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Method deleteNotifyInfo. |
| * @param resource |
| */ |
| public void deleteNotifyInfo(IResource resource) throws CVSException { |
| NotifyInfo[] infos = SyncFileWriter.readAllNotifyInfo(resource.getParent()); |
| if (infos == null) return; |
| Map infoMap = new HashMap(); |
| for (int i = 0; i < infos.length; i++) { |
| NotifyInfo notifyInfo = infos[i]; |
| infoMap.put(infos[i].getName(), infos[i]); |
| } |
| infoMap.remove(resource.getName()); |
| NotifyInfo[] newInfos = new NotifyInfo[infoMap.size()]; |
| int i = 0; |
| for (Iterator iter = infoMap.values().iterator(); iter.hasNext();) { |
| newInfos[i++] = (NotifyInfo) iter.next(); |
| } |
| SyncFileWriter.writeAllNotifyInfo(resource.getParent(), newInfos); |
| } |
| |
| /** |
| * Add the entry to the CVS/Baserev file. We are not initially concerned |
| * with efficiency since edit/unedit are typically issued on a small set of |
| * files. |
| * |
| * XXX If there was a previous notify entry for the resource, it is replaced. This is |
| * probably not the proper behavior (see EclipseFile). |
| * |
| * @param resource |
| * @param info |
| */ |
| public void setBaserevInfo(IResource resource, BaserevInfo info) throws CVSException { |
| BaserevInfo[] infos = SyncFileWriter.readAllBaserevInfo(resource.getParent()); |
| if (infos == null) { |
| infos = new BaserevInfo[] { info }; |
| } else { |
| Map infoMap = new HashMap(); |
| for (int i = 0; i < infos.length; i++) { |
| infoMap.put(infos[i].getName(), infos[i]); |
| } |
| infoMap.put(info.getName(), info); |
| BaserevInfo[] newInfos = new BaserevInfo[infoMap.size()]; |
| int i = 0; |
| for (Iterator iter = infoMap.values().iterator(); iter.hasNext();) { |
| newInfos[i++] = (BaserevInfo) iter.next(); |
| } |
| infos = newInfos; |
| } |
| SyncFileWriter.writeAllBaserevInfo(resource.getParent(), infos); |
| } |
| |
| /** |
| * Method getBaserevInfo. |
| * @param resource |
| * @return BaserevInfo |
| */ |
| public BaserevInfo getBaserevInfo(IResource resource) throws CVSException { |
| BaserevInfo[] infos = SyncFileWriter.readAllBaserevInfo(resource.getParent()); |
| if (infos == null) return null; |
| for (int i = 0; i < infos.length; i++) { |
| BaserevInfo info = infos[i]; |
| if (info.getName().equals(resource.getName())) { |
| return info; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Method deleteNotifyInfo. |
| * @param resource |
| */ |
| public void deleteBaserevInfo(IResource resource) throws CVSException { |
| BaserevInfo[] infos = SyncFileWriter.readAllBaserevInfo(resource.getParent()); |
| if (infos == null) return; |
| Map infoMap = new HashMap(); |
| for (int i = 0; i < infos.length; i++) { |
| infoMap.put(infos[i].getName(), infos[i]); |
| } |
| infoMap.remove(resource.getName()); |
| BaserevInfo[] newInfos = new BaserevInfo[infoMap.size()]; |
| int i = 0; |
| for (Iterator iter = infoMap.values().iterator(); iter.hasNext();) { |
| newInfos[i++] = (BaserevInfo) iter.next(); |
| } |
| SyncFileWriter.writeAllBaserevInfo(resource.getParent(), newInfos); |
| } |
| |
| public void copyFileToBaseDirectory(final IFile file, IProgressMonitor monitor) throws CVSException { |
| run(new ICVSRunnable() { |
| public void run(IProgressMonitor monitor) throws CVSException { |
| ResourceSyncInfo info = getResourceSync(file); |
| // The file must exist remotely and locally |
| if (info == null || info.isAdded() || info.isDeleted()) |
| return; |
| SyncFileWriter.writeFileToBaseDirectory(file, monitor); |
| changedResources.add(file); |
| } |
| }, monitor); |
| } |
| |
| public void restoreFileFromBaseDirectory(final IFile file, IProgressMonitor monitor) throws CVSException { |
| run(new ICVSRunnable() { |
| public void run(IProgressMonitor monitor) throws CVSException { |
| ResourceSyncInfo info = getResourceSync(file); |
| // The file must exist remotely |
| if (info == null || info.isAdded()) |
| return; |
| SyncFileWriter.restoreFileFromBaseDirectory(file, monitor); |
| changedResources.add(file); |
| } |
| }, monitor); |
| } |
| |
| public void deleteFileFromBaseDirectory(final IFile file, IProgressMonitor monitor) throws CVSException { |
| ResourceSyncInfo info = getResourceSync(file); |
| // The file must exist remotely |
| if (info == null || info.isAdded()) |
| return; |
| SyncFileWriter.deleteFileFromBaseDirectory(file, monitor); |
| } |
| |
| /** |
| * Method isSyncInfoLoaded returns true if all the sync info for the |
| * provided resources is loaded into the internal cache. |
| * |
| * @param resources |
| * @param i |
| * @return boolean |
| */ |
| public boolean isSyncInfoLoaded(IResource[] resources, int depth) throws CVSException { |
| // get the folders involved |
| IContainer[] folders = getParentFolders(resources, depth); |
| // for all folders that have a CVS folder, ensure the sync info is cached |
| for (int i = 0; i < folders.length; i++) { |
| IContainer parent = folders[i]; |
| if (!getSyncInfoCacheFor(parent).isSyncInfoLoaded(parent)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Method ensureSyncInfoLoaded loads all the relevent sync info into the cache |
| * @param resources |
| * @param i |
| * @return Object |
| */ |
| public void ensureSyncInfoLoaded(IResource[] resources, int depth) throws CVSException { |
| // get the folders involved |
| IContainer[] folders = getParentFolders(resources, depth); |
| // Cache the sync info for all the folders |
| for (int i = 0; i < folders.length; i++) { |
| IContainer parent = folders[i]; |
| try { |
| beginOperation(null); |
| cacheResourceSyncForChildren(parent); |
| cacheFolderSync(parent); |
| cacheFolderIgnores(parent); |
| } finally { |
| endOperation(null); |
| } |
| } |
| } |
| |
| /* |
| * Collect the projects and parent folders of the resources since |
| * thats were the sync info is kept. |
| */ |
| private IContainer[] getParentFolders(IResource[] resources, int depth) throws CVSException { |
| final Set folders = new HashSet(); |
| for (int i = 0; i < resources.length; i++) { |
| IResource resource = resources[i]; |
| folders.add(resource.getProject()); |
| if (resource.getType() != IResource.PROJECT) { |
| folders.add(resource.getParent()); |
| } |
| // use the depth to gather child folders when appropriate |
| if (depth != IResource.DEPTH_ZERO) { |
| try { |
| resource.accept(new IResourceVisitor() { |
| public boolean visit(IResource resource) throws CoreException { |
| if (resource.getType() == IResource.FOLDER) |
| folders.add(resource); |
| // let the depth determine who we visit |
| return true; |
| } |
| }, depth, false); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| } |
| return (IContainer[]) folders.toArray(new IContainer[folders.size()]); |
| } |
| |
| public void run(ICVSRunnable job, IProgressMonitor monitor) throws CVSException { |
| monitor = Policy.monitorFor(monitor); |
| monitor.beginTask(null, 100); |
| try { |
| beginOperation(Policy.subMonitorFor(monitor, 5)); |
| job.run(Policy.subMonitorFor(monitor, 60)); |
| } finally { |
| endOperation(Policy.subMonitorFor(monitor, 35)); |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * Method isEdited returns true if a "cvs edit" was performed on the given |
| * file and no commit or unedit has yet been performed. |
| * @param iResource |
| * @return boolean |
| */ |
| public boolean isEdited(IFile resource) throws CVSException { |
| return SyncFileWriter.isEdited(resource); |
| } |
| |
| protected void setDirtyIndicator(IResource resource, String indicator) throws CVSException { |
| if (Policy.DEBUG_DIRTY_CACHING) { |
| System.out.println("Dirty indicator for " //$NON-NLS-1$ |
| + resource.getFullPath() |
| + " set to " //$NON-NLS-1$ |
| + (indicator.equals(IS_DIRTY_INDICATOR) ? "DIRTY" : "NOT_DIRTY")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| getSyncInfoCacheFor(resource).setDirtyIndicator(resource, indicator); |
| } |
| |
| protected String getDirtyIndicator(IResource resource) throws CVSException { |
| return getSyncInfoCacheFor(resource).getDirtyIndicator(resource); |
| } |
| |
| /* |
| * Return the dirty count for the given folder. For existing folders, the |
| * dirty count may not have been calculated yet and this method will return |
| * null in that case. For phantom folders, the dirty count is calculated if |
| * it does not exist yet. |
| */ |
| protected int getDirtyCount(IContainer container) throws CVSException { |
| try { |
| beginOperation(null); |
| return getSyncInfoCacheFor(container).getCachedDirtyCount(container); |
| } finally { |
| endOperation(null); |
| } |
| } |
| protected void setDirtyCount(IContainer container, int count) throws CVSException { |
| try { |
| beginOperation(null); |
| if (Policy.DEBUG_DIRTY_CACHING) { |
| System.out.println("Dirty count for " //$NON-NLS-1$ |
| + container.getFullPath() |
| + " set to " //$NON-NLS-1$ |
| + count); |
| } |
| getSyncInfoCacheFor(container).setCachedDirtyCount(container, count); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /* |
| * Mark the given resource as either modified or clean using a persistant |
| * property. Do nothing if the modified state is already what we want. |
| * Return true if the modification state was changed. |
| */ |
| protected boolean setModified(IResource container, boolean modified) throws CVSException { |
| String indicator = modified ? IS_DIRTY_INDICATOR : NOT_DIRTY_INDICATOR; |
| // if it's already set, no need to set the property or adjust the parents count |
| if (indicator.equals(getDirtyIndicator(container))) return false; |
| // set the dirty indicator and adjust the parent accordingly |
| setDirtyIndicator(container, indicator); |
| return true; |
| } |
| |
| /* |
| * Adjust the modified count for the given container and return true if the |
| * parent should be adjusted |
| */ |
| protected boolean adjustModifiedCount(IContainer container, boolean dirty) throws CVSException { |
| if (container.getType() == IResource.ROOT || !isValid(container)) return false; |
| int count = getDirtyCount(container); |
| boolean updateParent = false; |
| if (count == -1) { |
| // The number of dirty children has not been tallied for this parent. |
| // (i.e. no one has queried this folder yet) |
| if (dirty) { |
| // Make sure the parent and it's ansecestors |
| // are still marked as dirty (if they aren't already) |
| String indicator = getDirtyIndicator(container); |
| if (indicator == null) { |
| // The dirty state for the folder has never been cached |
| // or the cache was flushed due to an error of some sort. |
| // Let the next dirtyness query invoke the caching |
| } else if (indicator.equals(NOT_DIRTY_INDICATOR)) { |
| setModified(container, true); |
| updateParent = true; |
| } |
| } else { |
| // Let the initial query of dirtyness determine if the persistent |
| // property is still acurate. |
| } |
| } else { |
| if (dirty) { |
| count++; |
| if (count == 1) { |
| setModified(container, true); |
| updateParent = true; |
| } |
| } else { |
| Assert.isTrue(count > 0); |
| count--; |
| if (count == 0) { |
| setModified(container, false); |
| updateParent = true; |
| } |
| } |
| setDirtyCount(container, count); |
| } |
| return updateParent; |
| } |
| |
| /* |
| * Add the deleted child and return true if it didn't exist before |
| */ |
| protected boolean addDeletedChild(IContainer container, IFile file) throws CVSException { |
| try { |
| beginOperation(null); |
| getSyncInfoCacheFor(container).addDeletedChild(container, file); |
| return true; |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| protected boolean removeDeletedChild(IContainer container, IFile file) throws CVSException { |
| try { |
| beginOperation(null); |
| getSyncInfoCacheFor(container).removeDeletedChild(container, file); |
| return true; |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| protected void setDeletedChildren(IContainer parent, Set deletedFiles) throws CVSException { |
| if (!parent.exists()) return; |
| sessionPropertyCache.setDeletedChildren(parent, deletedFiles); |
| } |
| |
| protected void flushDirtyCache(IResource resource, int depth) throws CVSException { |
| if (resource.getType() == IResource.ROOT) return; |
| if (!isValid(resource)) return; |
| try { |
| final CVSException[] exception = new CVSException[] { null }; |
| beginOperation(null); |
| resource.accept(new IResourceVisitor() { |
| public boolean visit(IResource resource) throws CoreException { |
| try { |
| if (Policy.DEBUG_DIRTY_CACHING) { |
| System.out.println("Dirty cache flushed for " //$NON-NLS-1$ |
| + resource.getFullPath()); |
| } |
| getSyncInfoCacheFor(resource).flushDirtyCache(resource); |
| } catch (CVSException e) { |
| exception[0] = e; |
| } |
| return true; |
| } |
| }, depth, true); |
| if (exception[0] != null) { |
| throw exception[0]; |
| } |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /* |
| * Flush all cached info for the file and it's ancestors |
| */ |
| protected void flushDirtyCacheWithAncestors(IResource resource) throws CVSException { |
| if (resource.getType() == IResource.ROOT) return; |
| try { |
| beginOperation(null); |
| try { |
| if (Policy.DEBUG_DIRTY_CACHING) { |
| System.out.println("Dirty cache flushed for " //$NON-NLS-1$ |
| + resource.getFullPath()); |
| } |
| getSyncInfoCacheFor(resource).flushDirtyCache(resource); |
| } finally { |
| flushDirtyCacheWithAncestors(resource.getParent()); |
| } |
| } finally { |
| endOperation(null); |
| } |
| } |
| |
| /** |
| * Method updated flags the objetc as having been modfied by the updated |
| * handler. This flag is read during the resource delta to determine whether |
| * the modification made the file dirty or not. |
| * |
| * @param mFile |
| */ |
| public void markFileAsUpdated(IFile file) throws CVSException { |
| sessionPropertyCache.markFileAsUpdated(file); |
| } |
| |
| protected boolean contentsChangedByUpdate(IFile file) throws CVSException { |
| return sessionPropertyCache.contentsChangedByUpdate(file); |
| } |
| |
| /** |
| * Method getName. |
| * @param syncBytes |
| */ |
| private String getName(byte[] syncBytes) throws CVSException { |
| return ResourceSyncInfo.getName(syncBytes); |
| } |
| |
| /** |
| * Method isFolder. |
| * @param syncBytes |
| * @return boolean |
| */ |
| private boolean isFolder(byte[] syncBytes) { |
| return ResourceSyncInfo.isFolder(syncBytes); |
| } |
| |
| /** |
| * Method convertToDeletion. |
| * @param syncBytes |
| * @return byte[] |
| */ |
| private byte[] convertToDeletion(byte[] syncBytes) throws CVSException { |
| return ResourceSyncInfo.convertToDeletion(syncBytes); |
| } |
| |
| /** |
| * Method convertFromDeletion. |
| * @param syncBytes |
| */ |
| private byte[] convertFromDeletion(byte[] syncBytes) throws CVSException { |
| return ResourceSyncInfo.convertFromDeletion(syncBytes); |
| } |
| |
| /** |
| * Method createdByMove clears any session properties on the file so it |
| * appears as an ADDED file. |
| * |
| * @param destination |
| */ |
| public void createdByMove(IFile file) throws CVSException { |
| deleteResourceSync(file); |
| flushDirtyCache(file, IResource.DEPTH_ZERO); |
| } |
| |
| } |