001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.scm.provider.svn.repository;
020
021import org.apache.maven.scm.provider.ScmProviderRepository;
022import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
023import org.apache.maven.scm.provider.svn.SvnTagBranchUtils;
024
025/**
026 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
027 *
028 */
029public class SvnScmProviderRepository extends ScmProviderRepositoryWithHost {
030    /** */
031    private String url;
032
033    private String protocol;
034
035    /**
036     * The base directory for any tags. Can be relative to the repository URL or an absolute URL.
037     */
038    private String tagBase;
039
040    /**
041     * The base directory for any branches. Can be relative to the repository URL or an absolute URL.
042     */
043    private String branchBase;
044
045    public SvnScmProviderRepository(String url) {
046        parseUrl(url);
047
048        tagBase = SvnTagBranchUtils.resolveTagBase(url);
049
050        branchBase = SvnTagBranchUtils.resolveBranchBase(url);
051    }
052
053    public SvnScmProviderRepository(String url, String user, String password) {
054        this(url);
055
056        setUser(user);
057
058        setPassword(password);
059    }
060
061    public String getUrl() {
062        return url;
063    }
064
065    /**
066     * Returns the url/directory to be used when tagging this repository.
067     */
068    public String getTagBase() {
069        return tagBase;
070    }
071
072    /**
073     * Sets the url/directory to be used when tagging this repository.
074     * The TagBase is a way to override the default tag location for the
075     * repository.  The default tag location is automatically determined
076     * for repositories in the standard subversion layout (with /tags /branches /trunk).
077     * Specify this value only if the repository is using a directory other than "/tags" for tagging.
078     *
079     * @param tagBase an absolute or relative url to the base directory to create tags in.
080     *                URL should be in a format that svn client understands, not the scm url format.
081     */
082    public void setTagBase(String tagBase) {
083        this.tagBase = tagBase;
084    }
085
086    /**
087     * Returns the url/directory to be used when tagging this repository.
088     */
089    public String getBranchBase() {
090        return branchBase;
091    }
092
093    /**
094     * Sets the url/directory to be used when branching this repository.
095     * The BranchBase is a way to override the default branch location for the
096     * repository.  The default branch location is automatically determined
097     * for repositories in the standard subversion layout (with /tags /branches /trunk).
098     * Specify this value only if the repository is using a directory other than "/branches" for branching.
099     *
100     * @param branchBase an absolute or relative url to the base directory to create branch in.
101     *                   URL should be in a format that svn client understands, not the scm url format.
102     */
103    public void setBranchBase(String branchBase) {
104        this.branchBase = branchBase;
105    }
106
107    private void setProtocol(String protocol) {
108        this.protocol = protocol;
109    }
110
111    /**
112     * Get the protocol used in this repository (file://, http://, https://,...)
113     *
114     * @return the protocol
115     */
116    public String getProtocol() {
117        return protocol;
118    }
119
120    private void parseUrl(String url) {
121        if (url.startsWith("file")) {
122            setProtocol("file://");
123        } else if (url.startsWith("https")) {
124            setProtocol("https://");
125        } else if (url.startsWith("http")) {
126            setProtocol("http://");
127        } else if (url.startsWith("svn+")) {
128            setProtocol(url.substring(0, url.indexOf("://") + 3));
129        } else if (url.startsWith("svn")) {
130            setProtocol("svn://");
131        }
132
133        if (getProtocol() == null) {
134            return;
135        }
136
137        String urlPath = url.substring(getProtocol().length());
138
139        int indexAt = urlPath.indexOf('@');
140
141        // a file:// URL may contain userinfo according to RFC 8089, but our implementation is broken
142        // extract user information, broken see SCM-909
143        if (indexAt > 0 && !getProtocol().startsWith("svn+") && !getProtocol().equals("file://")) {
144            String userPassword = urlPath.substring(0, indexAt);
145            if (userPassword.indexOf(':') < 0) {
146                setUser(userPassword);
147            } else {
148                setUser(userPassword.substring(0, userPassword.indexOf(':')));
149                setPassword(userPassword.substring(userPassword.indexOf(':') + 1));
150            }
151
152            urlPath = urlPath.substring(indexAt + 1);
153
154            this.url = getProtocol() + urlPath;
155        } else {
156            this.url = getProtocol() + urlPath;
157        }
158
159        if (!"file://".equals(getProtocol())) {
160            int indexSlash = urlPath.indexOf('/');
161
162            String hostPort = urlPath;
163
164            if (indexSlash > 0) {
165                hostPort = urlPath.substring(0, indexSlash);
166            }
167
168            int indexColon = hostPort.indexOf(':');
169
170            if (indexColon > 0) {
171                setHost(hostPort.substring(0, indexColon));
172                setPort(Integer.parseInt(hostPort.substring(indexColon + 1)));
173            } else {
174                setHost(hostPort);
175            }
176        }
177    }
178
179    /** {@inheritDoc} */
180    public ScmProviderRepository getParent() {
181        String newUrl = getUrl().substring(getProtocol().length());
182
183        while (newUrl.endsWith("/.")) {
184            newUrl = newUrl.substring(0, newUrl.length() - 2);
185        }
186
187        while (newUrl.endsWith("/")) {
188            newUrl = newUrl.substring(0, newUrl.length() - 1);
189        }
190
191        int i = newUrl.lastIndexOf('/');
192
193        if (i < 0) {
194            return null;
195        }
196        newUrl = newUrl.substring(0, i);
197
198        return new SvnScmProviderRepository(getProtocol() + newUrl, getUser(), getPassword());
199    }
200
201    /** {@inheritDoc} */
202    public String getRelativePath(ScmProviderRepository ancestor) {
203        if (ancestor instanceof SvnScmProviderRepository) {
204            SvnScmProviderRepository svnAncestor = (SvnScmProviderRepository) ancestor;
205
206            String path = getUrl().replaceFirst(svnAncestor.getUrl() + "/", "");
207
208            if (!path.equals(getUrl())) {
209                return path;
210            }
211        }
212        return null;
213    }
214
215    /** {@inheritDoc} */
216    public String toString() {
217        return getUrl();
218    }
219}