Introduce a named constant for the .git directory.
[jgit.git] / org.eclipse.jgit / src / org / eclipse / jgit / transport / URIish.java
blob4eb87c6ad0083818c875700db868a7d181f87544
1 /*
2 * Copyright (C) 2009, Mykola Nikishov <mn@mn.com.ua>
3 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * and other copyright owners as documented in the project's IP log.
7 * This program and the accompanying materials are made available
8 * under the terms of the Eclipse Distribution License v1.0 which
9 * accompanies this distribution, is reproduced below, and is
10 * available at http://www.eclipse.org/org/documents/edl-v10.php
12 * All rights reserved.
14 * Redistribution and use in source and binary forms, with or
15 * without modification, are permitted provided that the following
16 * conditions are met:
18 * - Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
21 * - Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials provided
24 * with the distribution.
26 * - Neither the name of the Eclipse Foundation, Inc. nor the
27 * names of its contributors may be used to endorse or promote
28 * products derived from this software without specific prior
29 * written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
43 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 package org.eclipse.jgit.transport;
48 import java.net.URISyntaxException;
49 import java.net.URL;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
53 import org.eclipse.jgit.lib.Constants;
55 /**
56 * This URI like construct used for referencing Git archives over the net, as
57 * well as locally stored archives. The most important difference compared to
58 * RFC 2396 URI's is that no URI encoding/decoding ever takes place. A space or
59 * any special character is written as-is.
61 public class URIish {
62 private static final Pattern FULL_URI = Pattern
63 .compile("^(?:([a-z][a-z0-9+-]+)://(?:([^/]+?)(?::([^/]+?))?@)?(?:([^/]+?))?(?::(\\d+))?)?((?:[A-Za-z]:)?/.+)$");
65 private static final Pattern SCP_URI = Pattern
66 .compile("^(?:([^@]+?)@)?([^:]+?):(.+)$");
68 private String scheme;
70 private String path;
72 private String user;
74 private String pass;
76 private int port = -1;
78 private String host;
80 /**
81 * Parse and construct an {@link URIish} from a string
83 * @param s
84 * @throws URISyntaxException
86 public URIish(String s) throws URISyntaxException {
87 s = s.replace('\\', '/');
88 Matcher matcher = FULL_URI.matcher(s);
89 if (matcher.matches()) {
90 scheme = matcher.group(1);
91 user = matcher.group(2);
92 pass = matcher.group(3);
93 host = matcher.group(4);
94 if (matcher.group(5) != null)
95 port = Integer.parseInt(matcher.group(5));
96 path = matcher.group(6);
97 if (path.length() >= 3
98 && path.charAt(0) == '/'
99 && path.charAt(2) == ':'
100 && (path.charAt(1) >= 'A' && path.charAt(1) <= 'Z'
101 || path.charAt(1) >= 'a' && path.charAt(1) <= 'z'))
102 path = path.substring(1);
103 } else {
104 matcher = SCP_URI.matcher(s);
105 if (matcher.matches()) {
106 user = matcher.group(1);
107 host = matcher.group(2);
108 path = matcher.group(3);
109 } else
110 throw new URISyntaxException(s, "Cannot parse Git URI-ish");
115 * Construct a URIish from a standard URL.
117 * @param u
118 * the source URL to convert from.
120 public URIish(final URL u) {
121 scheme = u.getProtocol();
122 path = u.getPath();
124 final String ui = u.getUserInfo();
125 if (ui != null) {
126 final int d = ui.indexOf(':');
127 user = d < 0 ? ui : ui.substring(0, d);
128 pass = d < 0 ? null : ui.substring(d + 1);
131 port = u.getPort();
132 host = u.getHost();
135 /** Create an empty, non-configured URI. */
136 public URIish() {
137 // Configure nothing.
140 private URIish(final URIish u) {
141 this.scheme = u.scheme;
142 this.path = u.path;
143 this.user = u.user;
144 this.pass = u.pass;
145 this.port = u.port;
146 this.host = u.host;
150 * @return true if this URI references a repository on another system.
152 public boolean isRemote() {
153 return getHost() != null;
157 * @return host name part or null
159 public String getHost() {
160 return host;
164 * Return a new URI matching this one, but with a different host.
166 * @param n
167 * the new value for host.
168 * @return a new URI with the updated value.
170 public URIish setHost(final String n) {
171 final URIish r = new URIish(this);
172 r.host = n;
173 return r;
177 * @return protocol name or null for local references
179 public String getScheme() {
180 return scheme;
184 * Return a new URI matching this one, but with a different scheme.
186 * @param n
187 * the new value for scheme.
188 * @return a new URI with the updated value.
190 public URIish setScheme(final String n) {
191 final URIish r = new URIish(this);
192 r.scheme = n;
193 return r;
197 * @return path name component
199 public String getPath() {
200 return path;
204 * Return a new URI matching this one, but with a different path.
206 * @param n
207 * the new value for path.
208 * @return a new URI with the updated value.
210 public URIish setPath(final String n) {
211 final URIish r = new URIish(this);
212 r.path = n;
213 return r;
217 * @return user name requested for transfer or null
219 public String getUser() {
220 return user;
224 * Return a new URI matching this one, but with a different user.
226 * @param n
227 * the new value for user.
228 * @return a new URI with the updated value.
230 public URIish setUser(final String n) {
231 final URIish r = new URIish(this);
232 r.user = n;
233 return r;
237 * @return password requested for transfer or null
239 public String getPass() {
240 return pass;
244 * Return a new URI matching this one, but with a different password.
246 * @param n
247 * the new value for password.
248 * @return a new URI with the updated value.
250 public URIish setPass(final String n) {
251 final URIish r = new URIish(this);
252 r.pass = n;
253 return r;
257 * @return port number requested for transfer or -1 if not explicit
259 public int getPort() {
260 return port;
264 * Return a new URI matching this one, but with a different port.
266 * @param n
267 * the new value for port.
268 * @return a new URI with the updated value.
270 public URIish setPort(final int n) {
271 final URIish r = new URIish(this);
272 r.port = n > 0 ? n : -1;
273 return r;
276 public int hashCode() {
277 int hc = 0;
278 if (getScheme() != null)
279 hc = hc * 31 + getScheme().hashCode();
280 if (getUser() != null)
281 hc = hc * 31 + getUser().hashCode();
282 if (getPass() != null)
283 hc = hc * 31 + getPass().hashCode();
284 if (getHost() != null)
285 hc = hc * 31 + getHost().hashCode();
286 if (getPort() > 0)
287 hc = hc * 31 + getPort();
288 if (getPath() != null)
289 hc = hc * 31 + getPath().hashCode();
290 return hc;
293 public boolean equals(final Object obj) {
294 if (!(obj instanceof URIish))
295 return false;
296 final URIish b = (URIish) obj;
297 if (!eq(getScheme(), b.getScheme()))
298 return false;
299 if (!eq(getUser(), b.getUser()))
300 return false;
301 if (!eq(getPass(), b.getPass()))
302 return false;
303 if (!eq(getHost(), b.getHost()))
304 return false;
305 if (getPort() != b.getPort())
306 return false;
307 if (!eq(getPath(), b.getPath()))
308 return false;
309 return true;
312 private static boolean eq(final String a, final String b) {
313 if (a == b)
314 return true;
315 if (a == null || b == null)
316 return false;
317 return a.equals(b);
321 * Obtain the string form of the URI, with the password included.
323 * @return the URI, including its password field, if any.
325 public String toPrivateString() {
326 return format(true);
329 public String toString() {
330 return format(false);
333 private String format(final boolean includePassword) {
334 final StringBuilder r = new StringBuilder();
335 if (getScheme() != null) {
336 r.append(getScheme());
337 r.append("://");
340 if (getUser() != null) {
341 r.append(getUser());
342 if (includePassword && getPass() != null) {
343 r.append(':');
344 r.append(getPass());
348 if (getHost() != null) {
349 if (getUser() != null)
350 r.append('@');
351 r.append(getHost());
352 if (getScheme() != null && getPort() > 0) {
353 r.append(':');
354 r.append(getPort());
358 if (getPath() != null) {
359 if (getScheme() != null) {
360 if (!getPath().startsWith("/"))
361 r.append('/');
362 } else if (getHost() != null)
363 r.append(':');
364 r.append(getPath());
367 return r.toString();
371 * Get the "humanish" part of the path. Some examples of a 'humanish' part
372 * for a full path:
373 * <table>
374 * <tr>
375 * <th>Path</th>
376 * <th>Humanish part</th>
377 * </tr>
378 * <tr>
379 * <td><code>/path/to/repo.git</code></td>
380 * <td rowspan="4"><code>repo</code></td>
381 * </tr>
382 * <tr>
383 * <td><code>/path/to/repo.git/</code></td>
384 * </tr>
385 * <tr>
386 * <td><code>/path/to/repo/.git</code></td>
387 * </tr>
388 * <tr>
389 * <td><code>/path/to/repo/</code></td>
390 * </tr>
391 * <tr>
392 * <td><code>/path//to</code></td>
393 * <td>an empty string</td>
394 * </tr>
395 * </table>
397 * @return the "humanish" part of the path. May be an empty string. Never
398 * {@code null}.
399 * @throws IllegalArgumentException
400 * if it's impossible to determine a humanish part, or path is
401 * {@code null} or empty
402 * @see #getPath
404 public String getHumanishName() throws IllegalArgumentException {
405 if ("".equals(getPath()) || getPath() == null)
406 throw new IllegalArgumentException();
407 String[] elements = getPath().split("/");
408 if (elements.length == 0)
409 throw new IllegalArgumentException();
410 String result = elements[elements.length - 1];
411 if (Constants.DOT_GIT.equals(result))
412 result = elements[elements.length - 2];
413 else if (result.endsWith(DOT_GIT))
414 result = result.substring(0, result.length() - DOT_GIT.length());
415 return result;