package org.eclipse.team.internal.ccvs.core.syncinfo;
/*
* (c) Copyright IBM Corp. 2000, 2002.
* All Rights Reserved.
*/
import org.eclipse.team.ccvs.core.CVSTag;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.resources.*;
import org.eclipse.team.internal.ccvs.core.util.Assert;
import org.eclipse.team.internal.ccvs.core.util.EmptyTokenizer;
import org.eclipse.team.internal.ccvs.core.Policy;
/**
* Value (immutable) object that represents workspace state information about a resource contained in
* a CVS repository. It is a specialized representation of a line in the CVS/Entry file with the addition of
* file permissions.
*
* Example entry line from the CVS/Entry file:
*
* /new.java/1.2/Fri Dec 07 00:17:52 2001/-kb/
* D/src////
*
* @see ICVSResource#getSyncInfo()
*/
public class ResourceSyncInfo {
// a directory sync info will have nothing more than a name
private boolean isDirectory = false;
// utility constants
private static final String DIRECTORY_PREFIX = "D/"; //$NON-NLS-1$
public static final String BINARY_TAG = "-kb"; //$NON-NLS-1$
public static final String USE_SERVER_MODE = ""; //$NON-NLS-1$
private static final String SEPERATOR = "/"; //$NON-NLS-1$
// Timestamp constants used to identify special cases
public static final String DUMMY_TIMESTAMP = "dummy timestamp"; //$NON-NLS-1$
public static final String RESULT_OF_MERGE = "Result of merge+"; //$NON-NLS-1$
// safe default permissions. Permissions are saved separatly so that the correct permissions
// can be sent back to the server on systems that don't save execute bits (e.g. windows).
public static final String DEFAULT_PERMISSIONS = "u=rw,g=rw,o=r"; //$NON-NLS-1$
// file sync information can be associated with a local resource that has been deleted. This is
// noted by prefixing the revision with this character.
// XXX Should this be private
public static final String DELETED_PREFIX = "-"; //$NON-NLS-1$
private boolean isDeleted = false;
// a sync element with a revision of '0' is considered a new file that has
// not been comitted to the repo. Is visible so that clients can create sync infos
// for new files.
public static final String ADDED_REVISION = "0"; //$NON-NLS-1$
// fields describing the synchronization of a resource in CVS parlance
private String name;
private String revision;
private String timeStamp;
private String keywordMode;
private CVSEntryLineTag tag;
private String permissions;
/**
* Constructor to create a sync object from entry line formats. The entry lines are parsed by this class.
*
* @param entryLine the entry line (e.g. /new.java/1.2/Fri Dec 07 00:17:52 2001/-kb/)
* @param permissions the file permission (e.g. u=rw,g=rw,o=r). May be null
.
* @param timestamp if not included in the entry line. Will overide the value in the entry line. The
* timestamp should be in the format specified in ICVSFile#getTimestamp(). May be null
.
*
* @exception CVSException is thrown if the entry cannot be parsed.
*/
public ResourceSyncInfo(String entryLine, String permissions, String timestamp) throws CVSException {
Assert.isNotNull(entryLine);
setEntryLine(entryLine);
if (permissions != null) {
this.permissions = permissions;
}
// override the timestamp that may of been in entryLine. In some cases the timestamp is not in the
// entry line (e.g. receiving entry lines from the server versus reading them from the Entry file).
if (isAdded()) {
this.timeStamp = DUMMY_TIMESTAMP;
} else if(timestamp!=null) {
this.timeStamp = timestamp;
}
}
/**
* Constructor to create a sync object from predefined values.
*
* @param name of the resource for which this sync state is associated, cannot be null
.
* @param revision of the resource, cannot be null
.
* @param timestamp can be null
.
* @param keywordMode can be null
* @param tag can be null
* @param permissions can be null
*/
public ResourceSyncInfo(String name, String revision, String timestamp, String keywordMode, CVSTag tag, String permissions) {
Assert.isNotNull(name);
Assert.isNotNull(revision);
this.name = name;
this.timeStamp = timestamp;
this.keywordMode = keywordMode;
this.permissions = permissions;
setRevision(revision);
setTag(tag);
}
/**
* Constructor to create a folder sync object.
*
* @param name of the resource for which this sync state is associatied, cannot be null
.
*/
public ResourceSyncInfo(String name) {
Assert.isNotNull(name);
this.name = name;
this.isDirectory = true;
}
/**
* Answers if this sync information is for a folder in which case only a name is
* available.
*
* @return true
if the sync information is for a folder and false
* if it is for a file.
*/
public boolean isDirectory() {
return isDirectory;
}
/**
* Answers if this sync information is for a file that has been added but not comitted
* to the CVS repository yet.
*
* @return true
if the sync information is new or false
if
* the sync is for an file that exists remotely. For folder sync info this returns
* false
.
*/
public boolean isAdded() {
if(!isDirectory) {
return getRevision().equals(ADDED_REVISION);
} else {
return false;
}
}
/**
* Answers if this sync information is for a file that is scheduled to be deleted
* from the repository but the deletion has not yet been comitted.
*
* @return true
if the sync information is deleted or false
if
* the sync is for an file that exists remotely.
*/
public boolean isDeleted() {
return isDeleted;
}
/**
* Answers a CVS compatible entry line. The client can use this line to store in the CVS/Entry file or
* sent it to the server.
*
* @param includeTimeStamp determines if the timestamp will be included in the returned entry line. In
* some usages the timestamp should not be included in entry lines, for example when sending the entries
* to the server.
*
* @return a file or folder entry line reflecting the state of this sync object.
*/
public String getEntryLine(boolean includeTimeStamp) {
StringBuffer result = new StringBuffer();
if(isDirectory) {
result.append(DIRECTORY_PREFIX);
result.append(name + "////"); //$NON-NLS-1$
} else {
result.append(SEPERATOR);
result.append(name);
result.append(SEPERATOR);
if(isDeleted){
result.append(DELETED_PREFIX);
}
result.append(revision);
result.append(SEPERATOR);
if(includeTimeStamp) {
result.append(timeStamp);
}
result.append(SEPERATOR);
result.append(keywordMode == null ? "" : keywordMode); //$NON-NLS-1$
result.append(SEPERATOR);
if (tag != null) {
result.append(tag.toEntryLineFormat(true));
}
}
return result.toString();
}
/**
* Anwsers the a compatible permissions line for files.
*
* @return a permission line for files and null
if this sync object is
* a directory.
*/
public String getPermissionLine() {
if(isDirectory) {
return null;
} else {
String permissions = this.permissions;
if (permissions == null)
permissions = DEFAULT_PERMISSIONS;
return SEPERATOR + name + SEPERATOR + permissions;
}
}
/**
* Gets the permissions or null
if permissions are not available.
*
* @return a string of the format "u=rw,g=rw,o=r"
*/
public String getPermissions() {
if(isDirectory) {
return null;
} else {
if(permissions==null) {
return DEFAULT_PERMISSIONS;
} else {
return permissions;
}
}
}
/**
* Gets the tag or null
if a tag is not available.
*
* @return Returns a String
*/
public CVSTag getTag() {
return tag;
}
/**
* Gets the timeStamp or null
if a timestamp is not available.
*
* @return a string of the format "Thu Oct 18 20:21:13 2001"
*/
public String getTimeStamp() {
return timeStamp;
}
/**
* Gets the version or null
if this is a folder sync info. The returned
* revision will never include the DELETED_PREFIX. To found out if this sync info is
* for a deleted resource call isDeleted().
*
* @return Returns a String
*/
public String getRevision() {
return revision;
}
/**
* Gets the name.
*
* @return Returns a String
*/
public String getName() {
return name;
}
/**
* Gets the keyword mode or null
if a keyword mode is available.
*
* @return
*/
public String getKeywordMode() {
return keywordMode;
}
/**
* Name equality between resource sync info objects.
*/
public boolean equals(Object other) {
if(other instanceof ResourceSyncInfo) {
ResourceSyncInfo syncInfo = ((ResourceSyncInfo)other);
if(other == this) return true;
if(getName() == syncInfo.getName()) return true;
return getName().equals(syncInfo.getName());
} else {
return false;
}
}
public int hashCode() {
return getName().hashCode();
}
/**
* Sets the tag for the resource.
*/
private void setTag(CVSTag tag) {
if(tag!=null) {
this.tag = new CVSEntryLineTag(tag);
} else {
this.tag = null;
}
}
/**
* Set the entry line
*
* @throws CVSException if the entryLine is malformed
*/
private void setEntryLine(String entryLine) throws CVSException {
if(entryLine.startsWith(DIRECTORY_PREFIX)) {
isDirectory = true;
entryLine = entryLine.substring(1);
} else {
isDirectory = false;
}
EmptyTokenizer tokenizer = new EmptyTokenizer(entryLine,SEPERATOR);
if(tokenizer.countTokens() != 5) {
throw new CVSException(Policy.bind("Malformed_entry_line___11") + entryLine); //$NON-NLS-1$
}
name = tokenizer.nextToken();
if(name.length()==0) {
throw new CVSException(Policy.bind("Malformed_entry_line,_missing_name___12") + entryLine); //$NON-NLS-1$
}
String rev = tokenizer.nextToken();
if(rev.length()==0 && !isDirectory()) {
throw new CVSException(Policy.bind("Malformed_entry_line,_missing_revision___13") + entryLine); //$NON-NLS-1$
} else {
setRevision(rev);
}
timeStamp = tokenizer.nextToken();
keywordMode = tokenizer.nextToken();
String tagEntry = tokenizer.nextToken();
if(tagEntry.length()>0) {
tag = new CVSEntryLineTag(tagEntry);
} else {
tag = null;
}
}
/**
* Sets the version and decides if the revision is for a deleted resource the revision field
* will not include the deleted prefix '-'.
*
* @param version the version to set
*/
private void setRevision(String revision) {
if(revision.startsWith(DELETED_PREFIX)) {
this.revision = revision.substring(DELETED_PREFIX.length());
isDeleted = true;
} else {
this.revision = revision;
isDeleted = false;
}
}
}