| /******************************************************************************* |
| * Copyright (c) 2000, 2007 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * 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.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"; //$NON-NLS-1$ |
| private final ICVSRepositoryLocation repository; |
| private final IPath path; |
| private final CVSTag tag; |
| private final String revision; |
| |
| /** |
| * Convert the given URI to a CVSURI. There are two supported formats: the |
| * original opaque format and a newer hierarchical format. |
| * <ul> |
| * <li>cvs://[:]method:user[:password]@host:[port]/root/path#project/path[,tagName]</li> |
| * <li>cvs://:method:user[:password]@host:[port]!root!path/project/path[?<version,branch,date,revision>=tagName]</li> |
| * </ul> |
| * @param uri the URI |
| * @return a CVS URI |
| */ |
| public static CVSURI fromUri(URI uri) { |
| try { |
| 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); |
| return new CVSURI(repository, path, tag); |
| } |
| } catch (CVSException e) { |
| CVSProviderPlugin.log(e); |
| throw new IllegalArgumentException(NLS.bind(CVSMessages.CVSURI_InvalidURI, new String[] {uri.toString(), e.getMessage()})); |
| } |
| } |
| |
| private static CVSTag getTag(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 (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.indexOf('/') != -1) |
| return null; |
| if (authority.indexOf('!') == -1) |
| return null; |
| authority = decodeAuthority(authority); |
| return CVSRepositoryLocation.fromString(authority); |
| } |
| |
| private static CVSTag getOldTag(URI uri) { |
| String f = uri.getFragment(); |
| int i = f.indexOf(','); |
| if (i == -1) { |
| return CVSTag.DEFAULT; |
| } |
| |
| 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); |
| } |
| |
| public CVSURI(ICVSRepositoryLocation repository, IPath path, CVSTag tag, String revision) { |
| this.repository = repository; |
| this.path = path; |
| this.tag = tag; |
| if (revision != null && !revision.equals(ResourceSyncInfo.ADDED_REVISION)) |
| this.revision = revision; |
| else |
| this.revision = null; |
| } |
| |
| 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$ |
| } |
| } |
| return new URI(SCHEME, 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; |
| } |
| } |