blob: d58011df44e5a6d5d0e31dfe98f0efd6df59e45b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ccvs.core.syncinfo;
import java.io.*;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.resources.CVSEntryLineTag;
import org.eclipse.team.internal.ccvs.core.util.Util;
/**
* Value (immutable) object that represents workspace state information about the contents of a
* folder that was retreived from a CVS repository. It is a specialized representation of the files from
* the CVS sub-directory that contain folder specific connection information (e.g. Root, Repository, Tag).
*
* @see ICVSFolder#getFolderSyncInfo()
*/
public class FolderSyncInfo {
// The Repository value for virtual directories (i.e. local with no corresponding remote)
public static final String VIRTUAL_DIRECTORY = "CVSROOT/Emptydir"; //$NON-NLS-1$
// relative path of this folder in the repository, project1/folder1/folder2
protected String repository;
// :pserver:user@host:/home/user/repo
protected String root;
// sticky tag (e.g. version, date, or branch tag applied to folder)
private CVSEntryLineTag tag;
// if true then it means only part of the folder was fetched from the repository, and CVS will not create
// additional files in that folder.
protected boolean isStatic;
/**
* Construct a folder sync object.
*
* @param repo the relative path of this folder in the repository, cannot be <code>null</code>.
* @param root the location of the repository, cannot be <code>null</code>.
* @param tag the tag set for the folder or <code>null</code> if there is no tag applied.
* @param isStatic to indicate is only part of the folder was fetched from the server.
*/
public FolderSyncInfo(String repo, String root, CVSTag tag, boolean isStatic) {
Assert.isNotNull(repo);
Assert.isNotNull(root);
this.repository = repo;
// intern the root so that caching of FolderSyncInfos for folders will use less space
this.root = root.intern();
ensureRepositoryRelativeToRoot();
this.isStatic = isStatic;
setTag(tag);
}
/**
* Method ensureRepositoryRelativeToRoot.
*/
private void ensureRepositoryRelativeToRoot() {
String rootDir;
try {
rootDir = getRootDirectory();
} catch (CVSException e) {
// Ignore the for now. Using the root will show the error to the user.
return;
}
if (repository.startsWith(rootDir)) {
repository = repository.substring(rootDir.length());
}
if (repository.startsWith(ResourceSyncInfo.SEPARATOR)) {
repository = repository.substring(ResourceSyncInfo.SEPARATOR.length());
}
}
public boolean equals(Object other) {
if(other == this) return true;
if (!(other instanceof FolderSyncInfo)) return false;
FolderSyncInfo syncInfo = ((FolderSyncInfo)other);
if (!getRoot().equals(syncInfo.getRoot())) return false;
if (!getRepository().equals(syncInfo.getRepository())) return false;
if (getIsStatic() != syncInfo.getIsStatic()) return false;
if ((getTag() == null) || (syncInfo.getTag() == null)) {
if ((getTag() == null) && (syncInfo.getTag() != null) && (syncInfo.getTag().getType() != CVSTag.HEAD)) {
return false;
} else if ((syncInfo.getTag() == null) && (getTag() != null) && (getTag().getType() != CVSTag.HEAD)) {
return false;
}
} else if (!getTag().equals(syncInfo.getTag())) {
return false;
}
return true;
}
/**
* Gets the root, cannot be <code>null.
*
* @return Returns a String
*/
public String getRoot() {
return root;
}
/**
* Answer the directory portion of the root. For example, if
* root = :pserver:user@host:/home/user/repo
* then /home/user/repo is return.
* <p>
* The root does not neccesarily contain a user name, in which cas the format is
* :pserver:host:/home/user/repo.
*
*
* @return String
*/
private String getRootDirectory() throws CVSException {
try {
String root = getRoot();
int index = root.lastIndexOf(CVSRepositoryLocation.HOST_SEPARATOR);
if (index == -1) {
// If the username is missing, we have to find the third ':'.
index = root.indexOf(CVSRepositoryLocation.COLON);
if (index == 0) {
// This indicates that the conection method is present.
// It is surrounded by two colons so skip them.
index = root.indexOf(CVSRepositoryLocation.COLON, index + 1);
index = root.indexOf(CVSRepositoryLocation.COLON, index + 1);
}
if (index == -1) {
// The host colon is missing.
// Look for a slash to find the path
index = root.indexOf(ResourceSyncInfo.SEPARATOR);
// Decrement the index since the slash is part of the path
if (index != -1) index--;
}
} else {
// If the username was there, we find the first ':' past the '@'
index = root.indexOf(CVSRepositoryLocation.COLON, index + 1);
}
index++;
// strip off a leading port if there is one
char c = root.charAt(index);
while (Character.isDigit(c)) {
c = root.charAt(++index);
}
return root.substring(index);
} catch (IndexOutOfBoundsException e) {
IStatus status = new CVSStatus(IStatus.ERROR,CVSMessages.FolderSyncInfo_Maleformed_root_4, e);
throw new CVSException(status);
}
}
/**
* Gets the tag, may be <code>null</code>.
*
* @return Returns a String
*/
public CVSEntryLineTag getTag() {
return tag;
}
/**
* Gets the repository, may be <code>null</code>.
*
* @return Returns a String
*/
public String getRepository() {
return repository;
}
/**
* Gets the isStatic.
*
* @return Returns a boolean
*/
public boolean getIsStatic() {
return isStatic;
}
/**
* Answers a full path to the folder on the remote server. This by appending the repository to the
* repository location speficied in the root.
*
* Example:
* root = :pserver:user@host:/home/user/repo
* repository = folder1/folder2
*
* Returns:
* /home/users/repo/folder1/folder2
*
* Note: CVS supports repository root directories that end in a slash (/).
* For these directories, the remote location must contain two slashes (//)
* between the root directory and the rest of the path. For example:
* root = :pserver:user@host:/home/user/repo/
* repository = folder1/folder2
* must return:
* /home/users/repo//folder1/folder2
*
* @return the full path of this folder on the server.
* @throws a CVSException if the root or repository is malformed.
*/
public String getRemoteLocation() throws CVSException {
return Util.appendPath(getRootDirectory(), getRepository());
}
/*
* Provide a hashCode() method that gaurentees that equal object will have the
* same hashCode
*/
public int hashCode() {
return getRoot().hashCode() | getRepository().hashCode();
}
/**
* Sets the tag for the folder.
*
* @param tag The tag to set
*/
protected void setTag(CVSTag tag) {
if (tag == null || tag.equals(CVSTag.DEFAULT)) {
this.tag = null;
} else {
this.tag = new CVSEntryLineTag(tag);
}
}
public String toString() {
return getRoot() + "/" + getRepository() + "/" + getTag(); //$NON-NLS-1$ //$NON-NLS-2$
}
public MutableFolderSyncInfo cloneMutable() {
MutableFolderSyncInfo newSync = new MutableFolderSyncInfo(this);
return newSync;
}
/**
* Return true if this FolderSyncInfo is mapped to the same remote directory
* as the other FolderSyncInfo passed as a parameter.
*
* @param remoteInfo
* @return
*/
public boolean isSameMapping(FolderSyncInfo other) {
if (other == null) return false;
return (this.getRoot().equals(other.getRoot())
&& this.getRepository().equals(other.getRepository())) ;
}
/**
* Convert a FolderSyncInfo into a byte array that can be stored
* in the workspace synchronizer
*/
public byte[] getBytes() throws CVSException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(out);
try {
dos.writeUTF(getRoot());
dos.writeUTF(getRepository());
CVSEntryLineTag t = getTag();
if (t == null) {
dos.writeUTF(""); //$NON-NLS-1$
} else {
dos.writeUTF(t.toString());
}
dos.writeBoolean(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
*/
public static FolderSyncInfo getFolderSyncInfo(byte[] bytes) throws CVSException {
Assert.isNotNull(bytes, "getFolderSyncInfo cannot be called with null parameter"); //$NON-NLS-1$
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) {
Status status = new Status(Status.ERROR, CVSProviderPlugin.ID, NLS.bind(CVSMessages.FolderSyncInfo_InvalidSyncInfoBytes, new String(bytes)), e);
CVSException ex = new CVSException(status);
throw ex;
}
return new FolderSyncInfo(repository, root, tag, isStatic);
}
/**
* Return whether the local directory is mapped to an existing remote
* directory or is just a local placeholder for child folders. a return type
* of <code>true</code> indicates that the local folder is not mapped to a
* remote folder.
* @return whether the directory is a local placeholder
*/
public boolean isVirtualDirectory() {
return getRepository().equals(VIRTUAL_DIRECTORY);
}
public FolderSyncInfo asImmutable() {
return this;
}
}