/******************************************************************************* * Copyright (c) 2004, 2006 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.p4.internal; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.buckminster.core.common.model.ExpandingProperties; import org.eclipse.buckminster.core.version.VersionSelector; import org.eclipse.buckminster.p4.Messages; import org.eclipse.buckminster.p4.preferences.Client; import org.eclipse.buckminster.p4.preferences.P4Preferences; import org.eclipse.buckminster.p4.preferences.Server; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.URLUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.osgi.util.NLS; import org.osgi.service.prefs.BackingStoreException; /** * @author thhal */ public class DepotURI extends PropertyScope { private static Pattern s_p4PortPattern = Pattern.compile("^([^:]+):([0-9]+)$"); //$NON-NLS-1$ public static URI createURI(String uriString) throws CoreException { try { return new URI(uriString); } catch(URISyntaxException e) { throw BuckminsterException.fromMessage(e, NLS.bind(Messages.invalid_URL_used_for_P4_provider_0, uriString)); } } public static Client getClient(Map<String, ? extends Object> scope, Server server, String clientName) throws CoreException { try { Client client; if(clientName == null) client = server.getDefaultClient(); else { client = server.getClient(clientName); if(client == null) throw BuckminsterException.fromMessage(NLS.bind( Messages.no_preferences_for_P4_client_0_for_server_1, clientName, server.getName())); } return client; } catch(BackingStoreException e) { throw BuckminsterException.wrap(e); } } public static Client getClient(Map<String, ? extends Object> scope, URI uri) throws CoreException { return getClient(scope, getServer(scope, uri), uri.getFragment()); } public static Server getServer(Map<String, ? extends Object> scope, URI uri) throws CoreException { try { P4Preferences prefs = P4Preferences.getInstance(); String address = uri.getHost(); Server server = null; if(address == null) { server = prefs.getDefaultServer(); if(server == null) server = prefs.configureDefaultServer(scope, false); } else { int port = uri.getPort(); if(port != -1) address = address + ':' + port; server = prefs.getServer(address); if(server == null) throw BuckminsterException.fromMessage(NLS.bind( Messages.no_P4_server_with_address_0_has_been_configured, address)); } return server; } catch(BackingStoreException e) { throw BuckminsterException.wrap(e); } } /** * Compares <code>p1</code> and <code>p2</code> for equality. The last segment of the paths will be stripped of * prior to comparison if it is the "..." segment. * * @param p1 * @param p2 * @return <code>true</code> if the paths are equal. */ public static final boolean pathEquals(IPath p1, IPath p2) { if(p1 == p2) return true; if(p1 == null || p2 == null) return false; if(p1.lastSegment().equals("...")) //$NON-NLS-1$ p1 = p1.removeLastSegments(1); if(p2.lastSegment().equals("...")) //$NON-NLS-1$ p2 = p2.removeLastSegments(1); return p1.toFile().equals(p2.toFile()); } private final IPath m_depotPath; private final String m_address; private final Client m_client; private final String m_defaultBranch; private final boolean m_hasBranchDesignator; /** * @see #DepotURI(URI, String, Map) * @param urlString * @param branch * @param properties * @throws CoreException */ public DepotURI(String urlString, String branch, Map<String, ? extends Object> properties) throws CoreException { this(createURI(urlString), branch, properties); } /** * <p> * The repository urlString of the p4 provider should be in the form of a hierarchical URI using the (optional) * scheme quot;p4:". As opposed to a Perforce Depot Path that starts with "//" in order to * differentiate a depot from a local file, this uri uses "//" like a normal URL. The path of the this URL * always denotes and must should start with "/". * </p> * <p> * An path segment consiting of a sinlge "-" is considered a branch designator. If this element is * present, and if the <code>branch</code> argument is not <code>null</code>, it will be replaced by the * <code>branch</code> argument. If the element is present but the <code>branch</code> argument is <code>null</code> * , the resulting DepotURI will be truncated at that element. It will thus act as the root for the branches. This * mechanism is used by the {@link VersionFinder}. * </p> * <p> * The uri may also contain two properties expressed in the query section of the uri. The properties are: * <dl> * <dt>client</dt> * <dd>The name of the client</dd> * <dt>defaultBranch</dt> * <dd>The name of the default branch</dd> * </dl> * </p> * <p> * If the <code>branch</code> argument is equal to {@link VersionSelectorFactory#DEFAULT_BRANCH} it will be replaced * by the default branch of the uri (see below). * </p> * Example: * * <pre> * p4://public.perforce.com:1666/public/perforce/api/java/p4package?client=public * </pre> * * This would cause a lookup for the server name "public.perforce.com" on port 1666. The depot path would * be "//public/perforce/api/java/..." and the client for the connection would be named "public" * * @param uri * The uri as described above. * @param branch * The branch to use at branch designator position or null if the resulting DepotURI will be used when * locating available branches. * @param properties * The property scope. */ public DepotURI(URI uri, String branch, Map<String, ? extends Object> properties) throws CoreException { super(properties); String scheme = uri.getScheme(); if(!(scheme == null || "p4".equals(scheme))) //$NON-NLS-1$ throw BuckminsterException.fromMessage(NLS.bind(Messages.invalid_URI_0_scheme_is_not_p4, uri.toString())); if(uri.getUserInfo() != null) throw BuckminsterException.fromMessage(NLS.bind(Messages.invalid_URI_0_P4_URI_cannot_contain_user_info, uri .toString())); String defaultBranch = null; String clientName = null; String[] pairs = URLUtils.decodeToQueryPairs(uri.getQuery()); for(String pair : pairs) { // now split the pair on the first '=' only // (one '=' is required to be there, even if the value is blank) // String[] kv = pair.split("=", 2); //$NON-NLS-1$ if("client".equalsIgnoreCase(kv[0])) //$NON-NLS-1$ clientName = kv[1]; else if("defaultbranch".equalsIgnoreCase(kv[0])) //$NON-NLS-1$ defaultBranch = kv[1]; } Server server = getServer(properties, uri); m_address = ExpandingProperties.expand(properties, server.getName(), 0); m_client = getClient(properties, server, clientName); m_defaultBranch = defaultBranch; if(VersionSelector.DEFAULT_BRANCH.equals(branch) && defaultBranch != null) branch = defaultBranch; // Create the UNC path that points into the DEPOT // IPath depotPath = new Path("/" + uri.getPath()); //$NON-NLS-1$ // Check if we have a segment that is one single '-'. If we do, it // will be the branch designator. // int segmentCount = depotPath.segmentCount(); int branchDesignator = segmentCount; while(--branchDesignator >= 0) if("-".equals(depotPath.segment(branchDesignator))) //$NON-NLS-1$ break; m_hasBranchDesignator = branchDesignator >= 0; if(m_hasBranchDesignator) { // Insert the branch at designated position or truncate the path at // that position if no branch is given. // IPath branchInjected; if(branchDesignator == 0) branchInjected = new Path("//"); //$NON-NLS-1$ else branchInjected = depotPath.removeLastSegments(segmentCount - branchDesignator); if(branch != null) { branchInjected = branchInjected.append(branch); if(branchDesignator + 1 < segmentCount) branchInjected = branchInjected.append(depotPath.removeFirstSegments(branchDesignator + 1)); } m_depotPath = branchInjected; } else m_depotPath = depotPath; } /** * @return Returns the port. */ public final String getAddress() { return m_address; } /** * @return Client preferences. */ public final Client getClient() { return m_client; } /** * @return Returns the client. */ public final String getClientName() { return this.expand(m_client.getName()); } public final String getDefaultBranch() { return m_defaultBranch; } /** * @return The depot path. */ public final IPath getDepotPath() { return m_depotPath; } /** * @return Returns the local root */ public String getLocalRoot() { return this.expand(m_client.getLocalRoot()); } public IPath getMappingForDepot(IPath depotPath) throws CoreException { try { return m_client.getMappingForDepot(depotPath); } catch(BackingStoreException e) { throw BuckminsterException.wrap(e); } } /** * @return The password. */ public final String getPassword() { return m_client.getServer().getPassword(); } /** * @return Returns the user. */ public final String getUser() { return this.expand(m_client.getServer().getUser()); } public final boolean hasBranchDesignator() { return m_hasBranchDesignator; } @Override public String toString() { try { String host = this.expand(m_client.getServer().getName()); int port = -1; Matcher matcher = s_p4PortPattern.matcher(host); if(matcher.matches()) { host = matcher.group(1); port = Integer.parseInt(matcher.group(2)); } return new URI( "p4", null, host, port, m_depotPath.makeUNC(false).toString(), null, this.expand(m_client.getName())).toString(); //$NON-NLS-1$ } catch(URISyntaxException e) { throw new RuntimeException(e); } } }