blob: a3127858cecd5c43b16e42d76fbba2059ab0bebe [file] [log] [blame]
/*******************************************************************************
* 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;
}
}