You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

URIish.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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.
  6. *
  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
  11. *
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or
  15. * without modification, are permitted provided that the following
  16. * conditions are met:
  17. *
  18. * - Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. *
  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.
  25. *
  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.
  30. *
  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.
  44. */
  45. package org.eclipse.jgit.transport;
  46. import java.io.Serializable;
  47. import java.net.URISyntaxException;
  48. import java.net.URL;
  49. import java.util.regex.Matcher;
  50. import java.util.regex.Pattern;
  51. import org.eclipse.jgit.JGitText;
  52. import org.eclipse.jgit.lib.Constants;
  53. /**
  54. * This URI like construct used for referencing Git archives over the net, as
  55. * well as locally stored archives. The most important difference compared to
  56. * RFC 2396 URI's is that no URI encoding/decoding ever takes place. A space or
  57. * any special character is written as-is.
  58. */
  59. public class URIish implements Serializable {
  60. private static final long serialVersionUID = 1L;
  61. private static final Pattern FULL_URI = Pattern
  62. .compile("^(?:([a-z][a-z0-9+-]+)://(?:([^/]+?)(?::([^/]+?))?@)?(?:([^/]+?))?(?::(\\d+))?)?((?:[A-Za-z]:)?/.+)$");
  63. private static final Pattern SCP_URI = Pattern
  64. .compile("^(?:([^@]+?)@)?([^:]+?):(.+)$");
  65. private String scheme;
  66. private String path;
  67. private String user;
  68. private String pass;
  69. private int port = -1;
  70. private String host;
  71. /**
  72. * Parse and construct an {@link URIish} from a string
  73. *
  74. * @param s
  75. * @throws URISyntaxException
  76. */
  77. public URIish(String s) throws URISyntaxException {
  78. s = s.replace('\\', '/');
  79. Matcher matcher = FULL_URI.matcher(s);
  80. if (matcher.matches()) {
  81. scheme = matcher.group(1);
  82. user = matcher.group(2);
  83. pass = matcher.group(3);
  84. host = matcher.group(4);
  85. if (matcher.group(5) != null)
  86. port = Integer.parseInt(matcher.group(5));
  87. path = matcher.group(6);
  88. if (path.length() >= 3
  89. && path.charAt(0) == '/'
  90. && path.charAt(2) == ':'
  91. && (path.charAt(1) >= 'A' && path.charAt(1) <= 'Z'
  92. || path.charAt(1) >= 'a' && path.charAt(1) <= 'z'))
  93. path = path.substring(1);
  94. else if (scheme != null && path.length() >= 2
  95. && path.charAt(0) == '/' && path.charAt(1) == '~')
  96. path = path.substring(1);
  97. } else {
  98. matcher = SCP_URI.matcher(s);
  99. if (matcher.matches()) {
  100. user = matcher.group(1);
  101. host = matcher.group(2);
  102. path = matcher.group(3);
  103. } else
  104. throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
  105. }
  106. }
  107. /**
  108. * Construct a URIish from a standard URL.
  109. *
  110. * @param u
  111. * the source URL to convert from.
  112. */
  113. public URIish(final URL u) {
  114. scheme = u.getProtocol();
  115. path = u.getPath();
  116. final String ui = u.getUserInfo();
  117. if (ui != null) {
  118. final int d = ui.indexOf(':');
  119. user = d < 0 ? ui : ui.substring(0, d);
  120. pass = d < 0 ? null : ui.substring(d + 1);
  121. }
  122. port = u.getPort();
  123. host = u.getHost();
  124. }
  125. /** Create an empty, non-configured URI. */
  126. public URIish() {
  127. // Configure nothing.
  128. }
  129. private URIish(final URIish u) {
  130. this.scheme = u.scheme;
  131. this.path = u.path;
  132. this.user = u.user;
  133. this.pass = u.pass;
  134. this.port = u.port;
  135. this.host = u.host;
  136. }
  137. /**
  138. * @return true if this URI references a repository on another system.
  139. */
  140. public boolean isRemote() {
  141. return getHost() != null;
  142. }
  143. /**
  144. * @return host name part or null
  145. */
  146. public String getHost() {
  147. return host;
  148. }
  149. /**
  150. * Return a new URI matching this one, but with a different host.
  151. *
  152. * @param n
  153. * the new value for host.
  154. * @return a new URI with the updated value.
  155. */
  156. public URIish setHost(final String n) {
  157. final URIish r = new URIish(this);
  158. r.host = n;
  159. return r;
  160. }
  161. /**
  162. * @return protocol name or null for local references
  163. */
  164. public String getScheme() {
  165. return scheme;
  166. }
  167. /**
  168. * Return a new URI matching this one, but with a different scheme.
  169. *
  170. * @param n
  171. * the new value for scheme.
  172. * @return a new URI with the updated value.
  173. */
  174. public URIish setScheme(final String n) {
  175. final URIish r = new URIish(this);
  176. r.scheme = n;
  177. return r;
  178. }
  179. /**
  180. * @return path name component
  181. */
  182. public String getPath() {
  183. return path;
  184. }
  185. /**
  186. * Return a new URI matching this one, but with a different path.
  187. *
  188. * @param n
  189. * the new value for path.
  190. * @return a new URI with the updated value.
  191. */
  192. public URIish setPath(final String n) {
  193. final URIish r = new URIish(this);
  194. r.path = n;
  195. return r;
  196. }
  197. /**
  198. * @return user name requested for transfer or null
  199. */
  200. public String getUser() {
  201. return user;
  202. }
  203. /**
  204. * Return a new URI matching this one, but with a different user.
  205. *
  206. * @param n
  207. * the new value for user.
  208. * @return a new URI with the updated value.
  209. */
  210. public URIish setUser(final String n) {
  211. final URIish r = new URIish(this);
  212. r.user = n;
  213. return r;
  214. }
  215. /**
  216. * @return password requested for transfer or null
  217. */
  218. public String getPass() {
  219. return pass;
  220. }
  221. /**
  222. * Return a new URI matching this one, but with a different password.
  223. *
  224. * @param n
  225. * the new value for password.
  226. * @return a new URI with the updated value.
  227. */
  228. public URIish setPass(final String n) {
  229. final URIish r = new URIish(this);
  230. r.pass = n;
  231. return r;
  232. }
  233. /**
  234. * @return port number requested for transfer or -1 if not explicit
  235. */
  236. public int getPort() {
  237. return port;
  238. }
  239. /**
  240. * Return a new URI matching this one, but with a different port.
  241. *
  242. * @param n
  243. * the new value for port.
  244. * @return a new URI with the updated value.
  245. */
  246. public URIish setPort(final int n) {
  247. final URIish r = new URIish(this);
  248. r.port = n > 0 ? n : -1;
  249. return r;
  250. }
  251. public int hashCode() {
  252. int hc = 0;
  253. if (getScheme() != null)
  254. hc = hc * 31 + getScheme().hashCode();
  255. if (getUser() != null)
  256. hc = hc * 31 + getUser().hashCode();
  257. if (getPass() != null)
  258. hc = hc * 31 + getPass().hashCode();
  259. if (getHost() != null)
  260. hc = hc * 31 + getHost().hashCode();
  261. if (getPort() > 0)
  262. hc = hc * 31 + getPort();
  263. if (getPath() != null)
  264. hc = hc * 31 + getPath().hashCode();
  265. return hc;
  266. }
  267. public boolean equals(final Object obj) {
  268. if (!(obj instanceof URIish))
  269. return false;
  270. final URIish b = (URIish) obj;
  271. if (!eq(getScheme(), b.getScheme()))
  272. return false;
  273. if (!eq(getUser(), b.getUser()))
  274. return false;
  275. if (!eq(getPass(), b.getPass()))
  276. return false;
  277. if (!eq(getHost(), b.getHost()))
  278. return false;
  279. if (getPort() != b.getPort())
  280. return false;
  281. if (!eq(getPath(), b.getPath()))
  282. return false;
  283. return true;
  284. }
  285. private static boolean eq(final String a, final String b) {
  286. if (a == b)
  287. return true;
  288. if (a == null || b == null)
  289. return false;
  290. return a.equals(b);
  291. }
  292. /**
  293. * Obtain the string form of the URI, with the password included.
  294. *
  295. * @return the URI, including its password field, if any.
  296. */
  297. public String toPrivateString() {
  298. return format(true);
  299. }
  300. public String toString() {
  301. return format(false);
  302. }
  303. private String format(final boolean includePassword) {
  304. final StringBuilder r = new StringBuilder();
  305. if (getScheme() != null) {
  306. r.append(getScheme());
  307. r.append("://");
  308. }
  309. if (getUser() != null) {
  310. r.append(getUser());
  311. if (includePassword && getPass() != null) {
  312. r.append(':');
  313. r.append(getPass());
  314. }
  315. }
  316. if (getHost() != null) {
  317. if (getUser() != null)
  318. r.append('@');
  319. r.append(getHost());
  320. if (getScheme() != null && getPort() > 0) {
  321. r.append(':');
  322. r.append(getPort());
  323. }
  324. }
  325. if (getPath() != null) {
  326. if (getScheme() != null) {
  327. if (!getPath().startsWith("/"))
  328. r.append('/');
  329. } else if (getHost() != null)
  330. r.append(':');
  331. r.append(getPath());
  332. }
  333. return r.toString();
  334. }
  335. /**
  336. * Get the "humanish" part of the path. Some examples of a 'humanish' part
  337. * for a full path:
  338. * <table>
  339. * <tr>
  340. * <th>Path</th>
  341. * <th>Humanish part</th>
  342. * </tr>
  343. * <tr>
  344. * <td><code>/path/to/repo.git</code></td>
  345. * <td rowspan="4"><code>repo</code></td>
  346. * </tr>
  347. * <tr>
  348. * <td><code>/path/to/repo.git/</code></td>
  349. * </tr>
  350. * <tr>
  351. * <td><code>/path/to/repo/.git</code></td>
  352. * </tr>
  353. * <tr>
  354. * <td><code>/path/to/repo/</code></td>
  355. * </tr>
  356. * <tr>
  357. * <td><code>/path//to</code></td>
  358. * <td>an empty string</td>
  359. * </tr>
  360. * </table>
  361. *
  362. * @return the "humanish" part of the path. May be an empty string. Never
  363. * {@code null}.
  364. * @throws IllegalArgumentException
  365. * if it's impossible to determine a humanish part, or path is
  366. * {@code null} or empty
  367. * @see #getPath
  368. */
  369. public String getHumanishName() throws IllegalArgumentException {
  370. if ("".equals(getPath()) || getPath() == null)
  371. throw new IllegalArgumentException();
  372. String[] elements = getPath().split("/");
  373. if (elements.length == 0)
  374. throw new IllegalArgumentException();
  375. String result = elements[elements.length - 1];
  376. if (Constants.DOT_GIT.equals(result))
  377. result = elements[elements.length - 2];
  378. else if (result.endsWith(Constants.DOT_GIT_EXT))
  379. result = result.substring(0, result.length()
  380. - Constants.DOT_GIT_EXT.length());
  381. return result;
  382. }
  383. }