| /******************************************************************************* |
| * Copyright (c) 2002 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v0.5 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v05.html |
| * |
| * Contributors: |
| * IBM - Initial implementation |
| ******************************************************************************/ |
| package org.eclipse.team.internal.ccvs.core.resources; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| 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.ISynchronizer; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.team.internal.ccvs.core.CVSException; |
| import org.eclipse.team.internal.ccvs.core.ICVSFolder; |
| import org.eclipse.team.internal.ccvs.core.ICVSResource; |
| import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo; |
| import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo; |
| import org.eclipse.team.internal.ccvs.core.util.SyncFileWriter; |
| import org.eclipse.team.internal.ccvs.core.util.Util; |
| |
| /** |
| * This cache uses session properties to hold the bytes representing the sync |
| * info |
| */ |
| /*package*/ class SynchronizerSyncInfoCache extends SyncInfoCache { |
| |
| public SynchronizerSyncInfoCache() { |
| getWorkspaceSynchronizer().add(FOLDER_SYNC_KEY); |
| getWorkspaceSynchronizer().add(RESOURCE_SYNC_KEY); |
| getWorkspaceSynchronizer().add(DIRTY_COUNT); |
| } |
| /** |
| * Return the Eclipse Workspace Synchronizer (from org.eclipse.core.resources) |
| */ |
| private ISynchronizer getWorkspaceSynchronizer() { |
| return ResourcesPlugin.getWorkspace().getSynchronizer(); |
| } |
| |
| /** |
| * Convert a FolderSyncInfo into a byte array that can be stored |
| * in the workspace synchronizer |
| */ |
| private byte[] getBytes(FolderSyncInfo info) throws CVSException { |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| DataOutputStream dos = new DataOutputStream(out); |
| try { |
| dos.writeUTF(info.getRoot()); |
| dos.writeUTF(info.getRepository()); |
| CVSEntryLineTag tag = info.getTag(); |
| if (tag == null) { |
| dos.writeUTF(""); //$NON-NLS-1$ |
| } else { |
| dos.writeUTF(tag.toString()); |
| } |
| dos.writeBoolean(info.getIsStatic()); |
| dos.close(); |
| } catch (IOException e) { |
| throw CVSException.wrapException(e); |
| } |
| return out.toByteArray(); |
| } |
| |
| /** |
| * Convert a byte array that was created using getBytes(FolderSyncInfo) |
| * into a FolderSyncInfo |
| */ |
| private FolderSyncInfo getFolderSyncInfo(byte[] bytes) throws CVSException { |
| ByteArrayInputStream in = new ByteArrayInputStream(bytes); |
| DataInputStream dis = new DataInputStream(in); |
| String root; |
| String repository; |
| CVSEntryLineTag tag; |
| boolean isStatic; |
| try { |
| root = dis.readUTF(); |
| repository = dis.readUTF(); |
| String tagName = dis.readUTF(); |
| if (tagName.length() == 0) { |
| tag = null; |
| } else { |
| tag = new CVSEntryLineTag(tagName); |
| } |
| isStatic = dis.readBoolean(); |
| } catch (IOException e) { |
| throw CVSException.wrapException(e); |
| } |
| return new FolderSyncInfo(repository, root, tag, isStatic); |
| } |
| |
| /** |
| * Method getBytes converts an array of bytes into a single byte array |
| * @param infos |
| * @return byte[] |
| */ |
| private byte[] getBytes(byte[][] infos) throws CVSException { |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| SyncFileWriter.writeLines(out, infos); |
| return out.toByteArray(); |
| } |
| |
| /** |
| * Convert a byte array that was created using getBytes(Map) |
| * into a Map of ResourceSyncInfo |
| */ |
| private byte[][] getResourceSyncInfo(byte[] bytes) throws CVSException { |
| byte[][] infos = SyncFileWriter.readLines(new ByteArrayInputStream(bytes)); |
| // check to make sure the info is not stored in the old format |
| if (infos.length != 0) { |
| byte[] firstLine = infos[0]; |
| if (firstLine.length != 0 && (firstLine[0] != (byte)'/' && firstLine[0] != (byte)'D')) { |
| Map oldInfos = getResourceSyncInfoMap(bytes); |
| infos = new byte[oldInfos.size()][]; |
| int i = 0; |
| for (Iterator iter = oldInfos.values().iterator(); iter.hasNext();) { |
| ResourceSyncInfo element = (ResourceSyncInfo) iter.next(); |
| infos[i++] = element.getBytes(); |
| } |
| // We can't convert the info to the new format because the caller |
| // may either not be in a workspace runnable or the resource tree |
| // may be closed for modification |
| } |
| } |
| return infos; |
| } |
| |
| /** |
| * ResourceSyncInfo used to be stored as a Map of ResourceSyncInfo. |
| * We need to be able to retrieve that info the way it was and |
| * convert it to the new way. |
| * |
| * Convert a byte array that was created using |
| * getBytes(Map) into a Map of ResourceSyncInfo |
| */ |
| private Map getResourceSyncInfoMap(byte[] bytes) throws CVSException { |
| ByteArrayInputStream in = new ByteArrayInputStream(bytes); |
| DataInputStream dis = new DataInputStream(in); |
| Map result = new HashMap(); |
| try { |
| int size = dis.readInt(); |
| for (int i = 0; i < size; i++) { |
| ResourceSyncInfo info = new ResourceSyncInfo(dis.readUTF(), null, null); |
| result.put(info.getName(), info); |
| } |
| } catch (IOException e) { |
| throw CVSException.wrapException(e); |
| } |
| return result; |
| } |
| |
| /*package*/ void flush(IProject project) throws CVSException { |
| try { |
| getWorkspaceSynchronizer().flushSyncInfo(FOLDER_SYNC_KEY, project, IResource.DEPTH_INFINITE); |
| getWorkspaceSynchronizer().flushSyncInfo(RESOURCE_SYNC_KEY, project, IResource.DEPTH_INFINITE); |
| getWorkspaceSynchronizer().flushSyncInfo(DIRTY_COUNT, project, IResource.DEPTH_INFINITE); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * Method flush. |
| * @param folder |
| */ |
| /*package*/ void flush(IFolder folder) throws CVSException { |
| try { |
| if (folder.exists() || folder.isPhantom()) { |
| getWorkspaceSynchronizer().flushSyncInfo(RESOURCE_SYNC_KEY, folder, IResource.DEPTH_ZERO); |
| } |
| if (folder.exists() || folder.isPhantom()) { |
| getWorkspaceSynchronizer().flushSyncInfo(FOLDER_SYNC_KEY, folder, IResource.DEPTH_ZERO); |
| } |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * Returns the folder sync info for the container; null if none. |
| * Folder must exist and must not be the workspace root. |
| * The folder sync info for the container MUST ALREADY BE CACHED. |
| * |
| * @param container the container |
| * @return the folder sync info for the folder, or null if none. |
| * @see #cacheFolderSync |
| */ |
| /*package*/ FolderSyncInfo getCachedFolderSync(IContainer container) throws CVSException { |
| try { |
| byte[] bytes = getWorkspaceSynchronizer().getSyncInfo(FOLDER_SYNC_KEY, container); |
| if (bytes == null) return null; |
| return getFolderSyncInfo(bytes); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * Sets the folder sync info for the container; if null, deletes it. |
| * Folder must exist and must not be the workspace root. |
| * The folder sync info for the container need not have previously been |
| * cached. |
| * |
| * @param container the container |
| * @param info the new folder sync info |
| */ |
| /*package*/ void setCachedFolderSync(IContainer container, FolderSyncInfo info) throws CVSException { |
| try { |
| if (info == null) { |
| if (container.exists() || container.isPhantom()) { |
| getWorkspaceSynchronizer().flushSyncInfo(FOLDER_SYNC_KEY, container, IResource.DEPTH_ZERO); |
| } |
| } else { |
| getWorkspaceSynchronizer().setSyncInfo(FOLDER_SYNC_KEY, container, getBytes(info)); |
| } |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#getCachedSyncBytes(org.eclipse.core.resources.IResource) |
| */ |
| /*package*/ byte[] getCachedSyncBytes(IResource resource) throws CVSException { |
| try { |
| return getWorkspaceSynchronizer().getSyncInfo(RESOURCE_SYNC_KEY, resource); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#setCachedSyncBytes(org.eclipse.core.resources.IResource, byte[]) |
| */ |
| /*package*/ void setCachedSyncBytes(IResource resource, byte[] syncBytes) throws CVSException { |
| byte[] oldBytes = getCachedSyncBytes(resource); |
| try { |
| if (syncBytes == null) { |
| if (oldBytes != null && (resource.exists() || resource.isPhantom())) { |
| getWorkspaceSynchronizer().flushSyncInfo(RESOURCE_SYNC_KEY, resource, IResource.DEPTH_ZERO); |
| } |
| } else { |
| // ensure that the sync info is not already set to the same thing. |
| // We do this to avoid causing a resource delta when the sync info is |
| // initially loaded (i.e. the synchronizer has it and so does the Entries file |
| if (oldBytes == null || !Util.equals(syncBytes, oldBytes)) |
| getWorkspaceSynchronizer().setSyncInfo(RESOURCE_SYNC_KEY, resource, syncBytes); |
| } |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.LowLevelSyncInfoCache#getDirtyIndicator(org.eclipse.core.resources.IResource) |
| */ |
| String getDirtyIndicator(IResource resource) throws CVSException { |
| if (resource.getType() == IResource.FILE) { |
| // if someone is asking about a non-existant file, it's probably dirty |
| return IS_DIRTY_INDICATOR; |
| } else { |
| int dirtyCount = getCachedDirtyCount((IContainer)resource); |
| switch (dirtyCount) { |
| case -1 : |
| return null; |
| case 0 : |
| return NOT_DIRTY_INDICATOR; |
| default : |
| return IS_DIRTY_INDICATOR; |
| } |
| } |
| } |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.LowLevelSyncInfoCache#setDirtyIndicator(org.eclipse.core.resources.IResource, java.lang.String) |
| */ |
| void setDirtyIndicator(IResource resource, String indicator) throws CVSException { |
| // The count is used as the indicator |
| } |
| |
| /** |
| * 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. |
| */ |
| /*package*/ int getCachedDirtyCount(IContainer container) throws CVSException { |
| // get the count from the synchronizer |
| int count = internalGetDirtyCount(container); |
| if (count == -1) { |
| count = calculateDirtyCountForPhantom(container); |
| //setDirtyCount(parent, count); |
| } |
| return count; |
| } |
| |
| /** |
| * Set the dirty count for the given container to the given count. |
| * |
| * @param container |
| * @param count |
| * @throws CVSException |
| */ |
| /*package*/ void setCachedDirtyCount(IContainer container, int count) throws CVSException { |
| try { |
| getWorkspaceSynchronizer().setSyncInfo(DIRTY_COUNT, container, getBytes(count)); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /* |
| * Convert an int to a byte array |
| */ |
| private byte[] getBytes(int count) { |
| byte[] result = new byte[4]; |
| result[0] = (byte)(count & 256); |
| result[1] = (byte)(count<<8 & 256); |
| result[1] = (byte)(count<<16 & 256); |
| result[1] = (byte)(count<<24 & 256); |
| return result; |
| } |
| |
| /* |
| * Convert a byte array to an int |
| */ |
| private int intFromBytes(byte[] bytes) { |
| return bytes[0] + (bytes[1]>>8) + (bytes[2]>>16) + (bytes[3]>>24); |
| } |
| |
| private int internalGetDirtyCount(IContainer parent) throws CVSException { |
| try { |
| byte[] bytes = getWorkspaceSynchronizer().getSyncInfo(DIRTY_COUNT, parent); |
| if (bytes == null) return -1; |
| return intFromBytes(bytes); |
| } catch (CoreException e) { |
| throw CVSException.wrapException(e); |
| } |
| } |
| |
| /* |
| * Calculate the dirty count for the given phantom folder, performing any |
| * necessary calculations on the childen as well |
| */ |
| private int calculateDirtyCountForPhantom(IContainer parent) throws CVSException { |
| ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(parent); |
| ICVSResource[] children = cvsFolder.members(ICVSFolder.MANAGED_MEMBERS | ICVSFolder.PHANTOM_MEMBERS); |
| int count = 0; |
| for (int i = 0; i < children.length; i++) { |
| ICVSResource resource = children[i]; |
| if (resource.isFolder()) { |
| int dc = getCachedDirtyCount((IContainer)resource.getIResource()); |
| if (dc > 0) count++; |
| } else { |
| // Any non-existant managed files are dirty (outgoing deletion) |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| /*package*/ void flushDirtyCache(IResource container) throws CVSException { |
| // if (container.exists() || container.isPhantom()) { |
| // try { |
| // getWorkspaceSynchronizer().flushSyncInfo(DIRTY_COUNT, container, IResource.DEPTH_ZERO); |
| // } catch (CoreException e) { |
| // throw CVSException.wrapException(e); |
| // } |
| // } |
| } |
| |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer#addDeletedChild(org.eclipse.core.resources.IContainer, org.eclipse.core.resources.IFile) |
| */ |
| protected boolean addDeletedChild(IContainer container, IFile file) throws CVSException { |
| // try { |
| // beginOperation(null); |
| // int oldCount = internalGetDirtyCount(container); |
| // if (oldCount == -1) { |
| // // there is no cached count so wait until the first query |
| // // or there was no deleted file |
| // return false; |
| // } |
| // int newCount = calculateDirtyCountForPhantom(container); |
| // // adjust the parent folder count if the newCount is 1; |
| // return oldCount == 0 && newCount == 1; |
| // } finally { |
| // endOperation(null); |
| // } |
| return true; |
| } |
| |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer#removeDeletedChild(org.eclipse.core.resources.IContainer, org.eclipse.core.resources.IFile) |
| */ |
| protected boolean removeDeletedChild(IContainer container, IFile file) throws CVSException { |
| // try { |
| // beginOperation(null); |
| // int oldCount = internalGetDirtyCount(container); |
| // if (oldCount == -1 || oldCount == 0) { |
| // // there is no cached count so wait until the first query |
| // // or there was no deleted file |
| // return false; |
| // } |
| // int newCount = calculateDirtyCountForPhantom(container); |
| // // adjust the parent folder count if the newCount is 0; |
| // return newCount == 0; |
| // } finally { |
| // endOperation(null); |
| // } |
| return true; |
| } |
| |
| /*package*/ boolean isSyncInfoLoaded(IContainer parent) throws CVSException { |
| return true; |
| } |
| |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#isResourceSyncInfoCached(org.eclipse.core.resources.IContainer) |
| */ |
| boolean isResourceSyncInfoCached(IContainer container) throws CVSException { |
| // the sync info is always cahced when using the synchronizer |
| return true; |
| } |
| |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#setResourceSyncInfoCached(org.eclipse.core.resources.IContainer) |
| */ |
| void setResourceSyncInfoCached(IContainer container) throws CVSException { |
| // do nothing |
| } |
| /** |
| * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#isFolderSyncInfoCached(org.eclipse.core.resources.IContainer) |
| */ |
| boolean isFolderSyncInfoCached(IContainer container) throws CVSException { |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#isDirtyCacheFlushed(org.eclipse.core.resources.IContainer) |
| */ |
| boolean isDirtyCacheFlushed(IContainer resource) throws CVSException { |
| return false; |
| } |
| } |