blob: eb98eefc968e1686ff6b00b9a142bdc2b3d4ec41 [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.filesystem;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.ProjectSetCapability;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
public class CVSURI {
private static final String SCHEME_CVS = "cvs"; //$NON-NLS-1$
private final ICVSRepositoryLocation repository;
private final IPath path;
private final CVSTag tag;
private final String revision;
private final String projectName;
/**
* Convert the given URI to a CVSURI. There are three supported formats: the
* original opaque format, a newer hierarchical format and a CVS SCM URL
* format.
*
* In the last format, as delimiter you can use either colon ':' or, if you
* use a colon for one of the variables (e.g. a windows path), a pipe '|'.
* For more information visit http://maven.apache.org/scm/cvs.html. Please
* note, that URIs with the pipe separator are currently not supported.
*
* <ul>
* <li>{@literal cvs://[:]method:user[:password]@host:[port]/root/path#project/path[,tagName]}</li>
* <li>{@literal cvs://_method_user[_password]~host_[port]!root!path/project/path[?<version,branch,date,revision>=tagName]}</li>
* <li>{@literal scm:cvs<delimiter>method<delimiter>[user[<delimiter>password]@]host[<delimiter>port]<delimiter>/root/path<delimiter>project/path[;project="projectName"][;tag=tagName]}</li>
* </ul>
* @param uri the URI
* @return a CVS URI
*/
public static CVSURI fromUri(URI uri) {
try {
if (ProjectSetCapability.SCHEME_SCM.equals(uri.getScheme())) {
uri = convert(uri);
}
ICVSRepositoryLocation repository = getRepository(uri);
if (repository != null) {
IPath path = new Path(null, uri.getPath());
CVSTag tag = getTag(uri);
String revision = getRevision(uri);
return new CVSURI(repository, path, tag, revision);
} else {
repository = getOldRepository(uri);
IPath path = getOldPath(uri);
CVSTag tag = getOldTag(uri);
String projectName = getProjectName(uri);
return new CVSURI(repository, path, tag, null, projectName);
}
} catch (CVSException e) {
CVSProviderPlugin.log(e);
throw new IllegalArgumentException(NLS.bind(CVSMessages.CVSURI_InvalidURI, new String[] {uri.toString(), e.getMessage()}));
}
}
private static URI convert(URI uri) {
StringBuilder sb = new StringBuilder();
String ssp = uri.getSchemeSpecificPart();
int i = ssp.lastIndexOf(':');
sb.append(ssp.substring(0, i)).append('#');
int j = ssp.indexOf(';');
if (j != -1) {
sb.append(ssp.substring(i + 1, j));
String[] params = ssp.substring(j).split(";"); //$NON-NLS-1$
String projectName = ""; //$NON-NLS-1$
for (String param : params) {
// PDE way of providing tags
if (param.startsWith("tag=")) { //$NON-NLS-1$
sb.append(",version="); //$NON-NLS-1$
sb.append(param.substring(param.indexOf('=') + 1));
} else if (param.startsWith("version=")) { //$NON-NLS-1$
sb.append(',').append(param);
} else if (param.startsWith("project=")) { //$NON-NLS-1$
projectName = param.substring(param.indexOf('=') + 1);
}
}
sb.append(',').append(projectName); // can be ""
} else {
sb.append(ssp.substring(i + 1));
}
return URI.create(sb.toString());
}
private static CVSTag getTag(URI uri) {
String query = uri.getQuery();
if (query == null)
return null;
return getTag(query);
}
private static CVSTag getTag(String s) {
StringTokenizer tokens = new StringTokenizer(s, ","); //$NON-NLS-1$
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
int index = token.indexOf('=');
if (index != -1) {
String type = token.substring(0, index);
String value = token.substring(index + 1);
if (value.length() > 0) {
int tagType = getTagType(type);
if (tagType != -1)
return new CVSTag(value, tagType);
}
}
}
return null;
}
private static String getRevision(URI uri) {
String query = uri.getQuery();
if (query == null)
return null;
StringTokenizer tokens = new StringTokenizer(query, ","); //$NON-NLS-1$
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
int index = token.indexOf('=');
if (index != -1) {
String type = token.substring(0, index);
String value = token.substring(index + 1);
if (type.equals("revision") && isValidRevision(value)) { //$NON-NLS-1$
return value;
}
}
}
return null;
}
private static boolean isValidRevision(String value) {
return value.matches("\\d+\\.\\d+(?:\\.\\d+)*"); //$NON-NLS-1$
}
private static int getTagType(String type) {
if (type.equalsIgnoreCase("version")) //$NON-NLS-1$
return CVSTag.VERSION;
if (type.equalsIgnoreCase("branch")) //$NON-NLS-1$
return CVSTag.BRANCH;
if (type.equalsIgnoreCase("date")) //$NON-NLS-1$
return CVSTag.DATE;
return -1;
}
private static ICVSRepositoryLocation getRepository(URI uri) throws CVSException {
String authority = uri.getAuthority();
if (authority == null)
return null;
if (authority.indexOf('/') != -1)
return null;
if (authority.indexOf('!') == -1)
return null;
authority = decodeAuthority(authority);
return CVSRepositoryLocation.fromString(authority);
}
private static String getProjectName(URI uri) {
String f = uri.getFragment();
if (f != null) {
int i = f.lastIndexOf(',');
if (i != -1) {
String s = f.substring(i + 1);
if (!s.equals("")) //$NON-NLS-1$
return s;
}
}
return null;
}
private static CVSTag getOldTag(URI uri) {
String f = uri.getFragment();
int i = f.indexOf(',');
if (i == -1) {
return CVSTag.DEFAULT;
}
CVSTag tag = getTag(f.substring(i + 1));
if (tag != null)
return tag;
return CVSTag.DEFAULT;//just use HEAD for now (name, CVSTag.BRANCH);
}
private static IPath getOldPath(URI uri) {
String path = uri.getFragment();
int i = path.indexOf(',');
if (i != -1) {
path = path.substring(0, i);
}
return new Path(path);
}
private static ICVSRepositoryLocation getOldRepository(URI uri) throws CVSException {
String ssp = uri.getSchemeSpecificPart();
if (!ssp.startsWith(":")) { //$NON-NLS-1$
ssp = ":" + ssp; //$NON-NLS-1$
}
return CVSRepositoryLocation.fromString(ssp);
}
public CVSURI(ICVSRepositoryLocation repository, IPath path, CVSTag tag) {
this(repository, path, tag, null, null);
}
public CVSURI(ICVSRepositoryLocation repository, IPath path, CVSTag tag, String revision) {
this(repository, path, tag, revision, null);
}
public CVSURI(ICVSRepositoryLocation repository, IPath path, CVSTag tag, String revision, String projectName) {
this.repository = repository;
this.path = path;
this.tag = tag;
if (revision != null && !revision.equals(ResourceSyncInfo.ADDED_REVISION))
this.revision = revision;
else
this.revision = null;
this.projectName = projectName;
}
public CVSURI append(String name) {
return new CVSURI(repository, path.append(name), tag);
}
public CVSURI append(IPath childPath) {
return new CVSURI(repository, path.append(childPath), tag);
}
public String getLastSegment() {
return path.lastSegment();
}
public URI toURI() {
try {
String authority = repository.getLocation(false);
authority = ensureRegistryBasedAuthority(authority);
String pathString = path.toString();
if (!pathString.startsWith("/")) { //$NON-NLS-1$
pathString = "/" + pathString; //$NON-NLS-1$
}
String query = null;
if (tag != null && tag.getType() != CVSTag.HEAD) {
query = getQueryType(tag) + "=" + tag.getName(); //$NON-NLS-1$
}
if (revision != null) {
String string = "revision=" + revision; //$NON-NLS-1$
if (query == null) {
query = string;
} else {
query = query + "," + string; //$NON-NLS-1$
}
}
if (projectName != null) {
String string = "project=" + projectName; //$NON-NLS-1$
if (query == null) {
query = string;
} else {
query = query + "," + string; //$NON-NLS-1$
}
}
return new URI(SCHEME_CVS, authority, pathString, query, null);
} catch (URISyntaxException e) {
CVSProviderPlugin.log(IStatus.ERROR, NLS.bind("An error occurred while creating a URI for {0} {1}", repository, path), e); //$NON-NLS-1$
throw new IllegalStateException(e.getMessage());
}
}
/*
* Ensure that the authority will not be confused with a
* server based authority. To do this, we need to convert
* any /, : and @ to another form.
*/
private String ensureRegistryBasedAuthority(String authority) {
// Encode / so the authority doesn't conflict with the path
authority = encode('/', '!', authority);
// Encode @ to avoid URI interpreting the authority as a server based authority
authority = encode('@', '~', authority);
// Encode : to avoid URI interpreting the authority as a server based authority
authority = encode(':', '_', authority);
return authority;
}
private static String decodeAuthority(String authority) {
authority = decode('/', '!', authority);
authority = decode('@', '~', authority);
authority = decode(':', '_', authority);
return authority;
}
private String encode(char charToEncode, char encoding, String string) {
// First, escape any occurrences of the encoding character
String result = string.replaceAll(new String(new char[] { encoding }), new String(new char[] { encoding, encoding }));
// Convert / to ! to avoid URI parsing part of the authority as the path
return result.replace(charToEncode, encoding);
}
private static String decode(char encodedChar, char encoding, String string) {
// Convert the encoded char back
String reuslt = string.replace(encoding, encodedChar);
// Convert any double occurrences of the encoded char back to the encoding
return reuslt.replaceAll(new String(new char[] { encodedChar, encodedChar }), new String(new char[] { encoding }));
}
private static String getQueryType(CVSTag tag) {
switch (tag.getType()) {
case CVSTag.BRANCH:
return "branch"; //$NON-NLS-1$
case CVSTag.DATE:
return "date"; //$NON-NLS-1$
}
return "version"; //$NON-NLS-1$
}
public boolean isRepositoryRoot() {
return path.segmentCount() == 0;
}
public CVSURI removeLastSegment() {
return new CVSURI(repository, path.removeLastSegments(1), tag);
}
public ICVSRemoteFolder getParentFolder() {
return removeLastSegment().toFolder();
}
public String getRepositoryName() {
return repository.toString();
}
public CVSURI getProjectURI(){
return new CVSURI(repository, path.uptoSegment(1), tag);
}
public ICVSRemoteFolder toFolder() {
return new RemoteFolder(null, repository, path.toString(), tag);
}
public ICVSRemoteFile toFile() {
// TODO: What about keyword mode?
return RemoteFile.create(path.toString(), repository, tag, revision);
}
public String toString() {
return "[Path: "+this.path.toString()+" Tag: "+tag.getName()+ " Repo: " +repository.getRootDirectory() +"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
public IPath getPath(){
return path;
}
public IPath getProjectStrippedPath() {
if (path.segmentCount() > 1)
return path.removeFirstSegments(1);
return path;
}
public ICVSRepositoryLocation getRepository() {
return repository;
}
public CVSTag getTag() {
return tag;
}
public String getRevision() {
return revision;
}
public String getProjectName() {
return projectName;
}
}