Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  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. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
  6. * Copyright (C) 2013, Robin Stocker <robin@nibor.org>
  7. * Copyright (C) 2015, Patrick Steinhardt <ps@pks.im> and others
  8. *
  9. * This program and the accompanying materials are made available under the
  10. * terms of the Eclipse Distribution License v. 1.0 which is available at
  11. * https://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * SPDX-License-Identifier: BSD-3-Clause
  14. */
  15. package org.eclipse.jgit.transport;
  16. import static java.nio.charset.StandardCharsets.UTF_8;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.File;
  19. import java.io.Serializable;
  20. import java.net.URISyntaxException;
  21. import java.net.URL;
  22. import java.util.BitSet;
  23. import java.util.regex.Matcher;
  24. import java.util.regex.Pattern;
  25. import org.eclipse.jgit.internal.JGitText;
  26. import org.eclipse.jgit.lib.Constants;
  27. import org.eclipse.jgit.util.RawParseUtils;
  28. import org.eclipse.jgit.util.References;
  29. import org.eclipse.jgit.util.StringUtils;
  30. /**
  31. * This URI like construct used for referencing Git archives over the net, as
  32. * well as locally stored archives. It is similar to RFC 2396 URI's, but also
  33. * support SCP and the malformed file://&lt;path&gt; syntax (as opposed to the correct
  34. * file:&lt;path&gt; syntax.
  35. */
  36. public class URIish implements Serializable {
  37. /**
  38. * Part of a pattern which matches the scheme part (git, http, ...) of an
  39. * URI. Defines one capturing group containing the scheme without the
  40. * trailing colon and slashes
  41. */
  42. private static final String SCHEME_P = "([a-z][a-z0-9+-]+)://"; //$NON-NLS-1$
  43. /**
  44. * Part of a pattern which matches the optional user/password part (e.g.
  45. * root:pwd@ in git://root:pwd@host.xyz/a.git) of URIs. Defines two
  46. * capturing groups: the first containing the user and the second containing
  47. * the password
  48. */
  49. private static final String OPT_USER_PWD_P = "(?:([^/:]+)(?::([^\\\\/]+))?@)?"; //$NON-NLS-1$
  50. /**
  51. * Part of a pattern which matches the host part of URIs. Defines one
  52. * capturing group containing the host name.
  53. */
  54. private static final String HOST_P = "((?:[^\\\\/:]+)|(?:\\[[0-9a-f:]+\\]))"; //$NON-NLS-1$
  55. /**
  56. * Part of a pattern which matches the optional port part of URIs. Defines
  57. * one capturing group containing the port without the preceding colon.
  58. */
  59. private static final String OPT_PORT_P = "(?::(\\d*))?"; //$NON-NLS-1$
  60. /**
  61. * Part of a pattern which matches the ~username part (e.g. /~root in
  62. * git://host.xyz/~root/a.git) of URIs. Defines no capturing group.
  63. */
  64. private static final String USER_HOME_P = "(?:/~(?:[^\\\\/]+))"; //$NON-NLS-1$
  65. /**
  66. * Part of a pattern which matches the optional drive letter in paths (e.g.
  67. * D: in file:///D:/a.txt). Defines no capturing group.
  68. */
  69. private static final String OPT_DRIVE_LETTER_P = "(?:[A-Za-z]:)?"; //$NON-NLS-1$
  70. /**
  71. * Part of a pattern which matches a relative path. Relative paths don't
  72. * start with slash or drive letters. Defines no capturing group.
  73. */
  74. private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*[^\\\\/]+[\\\\/]*)"; //$NON-NLS-1$
  75. /**
  76. * Part of a pattern which matches a relative or absolute path. Defines no
  77. * capturing group.
  78. */
  79. private static final String PATH_P = "(" + OPT_DRIVE_LETTER_P + "[\\\\/]?" //$NON-NLS-1$ //$NON-NLS-2$
  80. + RELATIVE_PATH_P + ")"; //$NON-NLS-1$
  81. private static final long serialVersionUID = 1L;
  82. /**
  83. * A pattern matching standard URI: </br>
  84. * <code>scheme "://" user_password? hostname? portnumber? path</code>
  85. */
  86. private static final Pattern FULL_URI = Pattern.compile("^" // //$NON-NLS-1$
  87. + SCHEME_P //
  88. + "(?:" // start a group containing hostname and all options only //$NON-NLS-1$
  89. // availabe when a hostname is there
  90. + OPT_USER_PWD_P //
  91. + HOST_P //
  92. + OPT_PORT_P //
  93. + "(" // open a group capturing the user-home-dir-part //$NON-NLS-1$
  94. + (USER_HOME_P + "?") //$NON-NLS-1$
  95. + "(?:" // start non capturing group for host //$NON-NLS-1$
  96. // separator or end of line
  97. + "[\\\\/])|$" //$NON-NLS-1$
  98. + ")" // close non capturing group for the host//$NON-NLS-1$
  99. // separator or end of line
  100. + ")?" // close the optional group containing hostname //$NON-NLS-1$
  101. + "(.+)?" //$NON-NLS-1$
  102. + "$"); //$NON-NLS-1$
  103. /**
  104. * A pattern matching the reference to a local file. This may be an absolute
  105. * path (maybe even containing windows drive-letters) or a relative path.
  106. */
  107. private static final Pattern LOCAL_FILE = Pattern.compile("^" // //$NON-NLS-1$
  108. + "([\\\\/]?" + PATH_P + ")" // //$NON-NLS-1$ //$NON-NLS-2$
  109. + "$"); //$NON-NLS-1$
  110. /**
  111. * A pattern matching a URI for the scheme 'file' which has only ':/' as
  112. * separator between scheme and path. Standard file URIs have '://' as
  113. * separator, but java.io.File.toURI() constructs those URIs.
  114. */
  115. private static final Pattern SINGLE_SLASH_FILE_URI = Pattern.compile("^" // //$NON-NLS-1$
  116. + "(file):([\\\\/](?![\\\\/])" // //$NON-NLS-1$
  117. + PATH_P //
  118. + ")$"); //$NON-NLS-1$
  119. /**
  120. * A pattern matching a SCP URI's of the form user@host:path/to/repo.git
  121. */
  122. private static final Pattern RELATIVE_SCP_URI = Pattern.compile("^" // //$NON-NLS-1$
  123. + OPT_USER_PWD_P //
  124. + HOST_P //
  125. + ":(" // //$NON-NLS-1$
  126. + ("(?:" + USER_HOME_P + "[\\\\/])?") // //$NON-NLS-1$ //$NON-NLS-2$
  127. + RELATIVE_PATH_P //
  128. + ")$"); //$NON-NLS-1$
  129. /**
  130. * A pattern matching a SCP URI's of the form user@host:/path/to/repo.git
  131. */
  132. private static final Pattern ABSOLUTE_SCP_URI = Pattern.compile("^" // //$NON-NLS-1$
  133. + OPT_USER_PWD_P //
  134. + "([^\\\\/:]{2,})" // //$NON-NLS-1$
  135. + ":(" // //$NON-NLS-1$
  136. + "[\\\\/]" + RELATIVE_PATH_P // //$NON-NLS-1$
  137. + ")$"); //$NON-NLS-1$
  138. private String scheme;
  139. private String path;
  140. private String rawPath;
  141. private String user;
  142. private String pass;
  143. private int port = -1;
  144. private String host;
  145. /**
  146. * Parse and construct an {@link org.eclipse.jgit.transport.URIish} from a
  147. * string
  148. *
  149. * @param s
  150. * a {@link java.lang.String} object.
  151. * @throws java.net.URISyntaxException
  152. */
  153. public URIish(String s) throws URISyntaxException {
  154. if (StringUtils.isEmptyOrNull(s)) {
  155. throw new URISyntaxException("The uri was empty or null", //$NON-NLS-1$
  156. JGitText.get().cannotParseGitURIish);
  157. }
  158. Matcher matcher = SINGLE_SLASH_FILE_URI.matcher(s);
  159. if (matcher.matches()) {
  160. scheme = matcher.group(1);
  161. rawPath = cleanLeadingSlashes(matcher.group(2), scheme);
  162. path = unescape(rawPath);
  163. return;
  164. }
  165. matcher = FULL_URI.matcher(s);
  166. if (matcher.matches()) {
  167. scheme = matcher.group(1);
  168. user = unescape(matcher.group(2));
  169. pass = unescape(matcher.group(3));
  170. // empty ports are in general allowed, except for URLs like
  171. // file://D:/path for which it is more desirable to parse with
  172. // host=null and path=D:/path
  173. String portString = matcher.group(5);
  174. if ("file".equals(scheme) && "".equals(portString)) { //$NON-NLS-1$ //$NON-NLS-2$
  175. rawPath = cleanLeadingSlashes(
  176. n2e(matcher.group(4)) + ":" + portString //$NON-NLS-1$
  177. + n2e(matcher.group(6)) + n2e(matcher.group(7)),
  178. scheme);
  179. } else {
  180. host = unescape(matcher.group(4));
  181. if (portString != null && portString.length() > 0) {
  182. port = Integer.parseInt(portString);
  183. }
  184. rawPath = cleanLeadingSlashes(
  185. n2e(matcher.group(6)) + n2e(matcher.group(7)), scheme);
  186. }
  187. path = unescape(rawPath);
  188. return;
  189. }
  190. matcher = RELATIVE_SCP_URI.matcher(s);
  191. if (matcher.matches()) {
  192. user = matcher.group(1);
  193. pass = matcher.group(2);
  194. host = matcher.group(3);
  195. rawPath = matcher.group(4);
  196. path = rawPath;
  197. return;
  198. }
  199. matcher = ABSOLUTE_SCP_URI.matcher(s);
  200. if (matcher.matches()) {
  201. user = matcher.group(1);
  202. pass = matcher.group(2);
  203. host = matcher.group(3);
  204. rawPath = matcher.group(4);
  205. path = rawPath;
  206. return;
  207. }
  208. matcher = LOCAL_FILE.matcher(s);
  209. if (matcher.matches()) {
  210. rawPath = matcher.group(1);
  211. path = rawPath;
  212. return;
  213. }
  214. throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
  215. }
  216. private static int parseHexByte(byte c1, byte c2) {
  217. return ((RawParseUtils.parseHexInt4(c1) << 4)
  218. | RawParseUtils.parseHexInt4(c2));
  219. }
  220. private static String unescape(String s) throws URISyntaxException {
  221. if (s == null)
  222. return null;
  223. if (s.indexOf('%') < 0)
  224. return s;
  225. byte[] bytes = s.getBytes(UTF_8);
  226. byte[] os = new byte[bytes.length];
  227. int j = 0;
  228. for (int i = 0; i < bytes.length; ++i) {
  229. byte c = bytes[i];
  230. if (c == '%') {
  231. if (i + 2 >= bytes.length)
  232. throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
  233. byte c1 = bytes[i + 1];
  234. byte c2 = bytes[i + 2];
  235. int val;
  236. try {
  237. val = parseHexByte(c1, c2);
  238. } catch (ArrayIndexOutOfBoundsException e) {
  239. URISyntaxException use = new URISyntaxException(s,
  240. JGitText.get().cannotParseGitURIish);
  241. use.initCause(e);
  242. throw use;
  243. }
  244. os[j++] = (byte) val;
  245. i += 2;
  246. } else
  247. os[j++] = c;
  248. }
  249. return RawParseUtils.decode(os, 0, j);
  250. }
  251. private static final BitSet reservedChars = new BitSet(127);
  252. static {
  253. for (byte b : Constants.encodeASCII("!*'();:@&=+$,/?#[]")) //$NON-NLS-1$
  254. reservedChars.set(b);
  255. }
  256. /**
  257. * Escape unprintable characters optionally URI-reserved characters
  258. *
  259. * @param s
  260. * The Java String to encode (may contain any character)
  261. * @param escapeReservedChars
  262. * true to escape URI reserved characters
  263. * @param encodeNonAscii
  264. * encode any non-ASCII characters
  265. * @return a URI-encoded string
  266. */
  267. private static String escape(String s, boolean escapeReservedChars,
  268. boolean encodeNonAscii) {
  269. if (s == null)
  270. return null;
  271. ByteArrayOutputStream os = new ByteArrayOutputStream(s.length());
  272. byte[] bytes = s.getBytes(UTF_8);
  273. for (byte c : bytes) {
  274. int b = c & 0xFF;
  275. if (b <= 32 || (encodeNonAscii && b > 127) || b == '%'
  276. || (escapeReservedChars && reservedChars.get(b))) {
  277. os.write('%');
  278. byte[] tmp = Constants.encodeASCII(String.format("%02x", //$NON-NLS-1$
  279. Integer.valueOf(b)));
  280. os.write(tmp[0]);
  281. os.write(tmp[1]);
  282. } else {
  283. os.write(b);
  284. }
  285. }
  286. byte[] buf = os.toByteArray();
  287. return RawParseUtils.decode(buf, 0, buf.length);
  288. }
  289. private String n2e(String s) {
  290. return s == null ? "" : s; //$NON-NLS-1$
  291. }
  292. // takes care to cut of a leading slash if a windows drive letter or a
  293. // user-home-dir specifications are
  294. private String cleanLeadingSlashes(String p, String s) {
  295. if (p.length() >= 3
  296. && p.charAt(0) == '/'
  297. && p.charAt(2) == ':'
  298. && ((p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')
  299. || (p.charAt(1) >= 'a' && p.charAt(1) <= 'z')))
  300. return p.substring(1);
  301. else if (s != null && p.length() >= 2 && p.charAt(0) == '/'
  302. && p.charAt(1) == '~')
  303. return p.substring(1);
  304. else
  305. return p;
  306. }
  307. /**
  308. * Construct a URIish from a standard URL.
  309. *
  310. * @param u
  311. * the source URL to convert from.
  312. */
  313. public URIish(URL u) {
  314. scheme = u.getProtocol();
  315. path = u.getPath();
  316. path = cleanLeadingSlashes(path, scheme);
  317. try {
  318. rawPath = u.toURI().getRawPath();
  319. rawPath = cleanLeadingSlashes(rawPath, scheme);
  320. } catch (URISyntaxException e) {
  321. throw new RuntimeException(e); // Impossible
  322. }
  323. final String ui = u.getUserInfo();
  324. if (ui != null) {
  325. final int d = ui.indexOf(':');
  326. user = d < 0 ? ui : ui.substring(0, d);
  327. pass = d < 0 ? null : ui.substring(d + 1);
  328. }
  329. port = u.getPort();
  330. host = u.getHost();
  331. }
  332. /**
  333. * Create an empty, non-configured URI.
  334. */
  335. public URIish() {
  336. // Configure nothing.
  337. }
  338. private URIish(URIish u) {
  339. this.scheme = u.scheme;
  340. this.rawPath = u.rawPath;
  341. this.path = u.path;
  342. this.user = u.user;
  343. this.pass = u.pass;
  344. this.port = u.port;
  345. this.host = u.host;
  346. }
  347. /**
  348. * Whether this URI references a repository on another system.
  349. *
  350. * @return true if this URI references a repository on another system.
  351. */
  352. public boolean isRemote() {
  353. return getHost() != null;
  354. }
  355. /**
  356. * Get host name part.
  357. *
  358. * @return host name part or null
  359. */
  360. public String getHost() {
  361. return host;
  362. }
  363. /**
  364. * Return a new URI matching this one, but with a different host.
  365. *
  366. * @param n
  367. * the new value for host.
  368. * @return a new URI with the updated value.
  369. */
  370. public URIish setHost(String n) {
  371. final URIish r = new URIish(this);
  372. r.host = n;
  373. return r;
  374. }
  375. /**
  376. * Get protocol name
  377. *
  378. * @return protocol name or null for local references
  379. */
  380. public String getScheme() {
  381. return scheme;
  382. }
  383. /**
  384. * Return a new URI matching this one, but with a different scheme.
  385. *
  386. * @param n
  387. * the new value for scheme.
  388. * @return a new URI with the updated value.
  389. */
  390. public URIish setScheme(String n) {
  391. final URIish r = new URIish(this);
  392. r.scheme = n;
  393. return r;
  394. }
  395. /**
  396. * Get path name component
  397. *
  398. * @return path name component
  399. */
  400. public String getPath() {
  401. return path;
  402. }
  403. /**
  404. * Get path name component
  405. *
  406. * @return path name component
  407. */
  408. public String getRawPath() {
  409. return rawPath;
  410. }
  411. /**
  412. * Return a new URI matching this one, but with a different path.
  413. *
  414. * @param n
  415. * the new value for path.
  416. * @return a new URI with the updated value.
  417. */
  418. public URIish setPath(String n) {
  419. final URIish r = new URIish(this);
  420. r.path = n;
  421. r.rawPath = n;
  422. return r;
  423. }
  424. /**
  425. * Return a new URI matching this one, but with a different (raw) path.
  426. *
  427. * @param n
  428. * the new value for path.
  429. * @return a new URI with the updated value.
  430. * @throws java.net.URISyntaxException
  431. */
  432. public URIish setRawPath(String n) throws URISyntaxException {
  433. final URIish r = new URIish(this);
  434. r.path = unescape(n);
  435. r.rawPath = n;
  436. return r;
  437. }
  438. /**
  439. * Get user name requested for transfer
  440. *
  441. * @return user name requested for transfer or null
  442. */
  443. public String getUser() {
  444. return user;
  445. }
  446. /**
  447. * Return a new URI matching this one, but with a different user.
  448. *
  449. * @param n
  450. * the new value for user.
  451. * @return a new URI with the updated value.
  452. */
  453. public URIish setUser(String n) {
  454. final URIish r = new URIish(this);
  455. r.user = n;
  456. return r;
  457. }
  458. /**
  459. * Get password requested for transfer
  460. *
  461. * @return password requested for transfer or null
  462. */
  463. public String getPass() {
  464. return pass;
  465. }
  466. /**
  467. * Return a new URI matching this one, but with a different password.
  468. *
  469. * @param n
  470. * the new value for password.
  471. * @return a new URI with the updated value.
  472. */
  473. public URIish setPass(String n) {
  474. final URIish r = new URIish(this);
  475. r.pass = n;
  476. return r;
  477. }
  478. /**
  479. * Get port number requested for transfer or -1 if not explicit
  480. *
  481. * @return port number requested for transfer or -1 if not explicit
  482. */
  483. public int getPort() {
  484. return port;
  485. }
  486. /**
  487. * Return a new URI matching this one, but with a different port.
  488. *
  489. * @param n
  490. * the new value for port.
  491. * @return a new URI with the updated value.
  492. */
  493. public URIish setPort(int n) {
  494. final URIish r = new URIish(this);
  495. r.port = n > 0 ? n : -1;
  496. return r;
  497. }
  498. /** {@inheritDoc} */
  499. @Override
  500. public int hashCode() {
  501. int hc = 0;
  502. if (getScheme() != null)
  503. hc = hc * 31 + getScheme().hashCode();
  504. if (getUser() != null)
  505. hc = hc * 31 + getUser().hashCode();
  506. if (getPass() != null)
  507. hc = hc * 31 + getPass().hashCode();
  508. if (getHost() != null)
  509. hc = hc * 31 + getHost().hashCode();
  510. if (getPort() > 0)
  511. hc = hc * 31 + getPort();
  512. if (getPath() != null)
  513. hc = hc * 31 + getPath().hashCode();
  514. return hc;
  515. }
  516. /** {@inheritDoc} */
  517. @Override
  518. public boolean equals(Object obj) {
  519. if (!(obj instanceof URIish))
  520. return false;
  521. final URIish b = (URIish) obj;
  522. if (!eq(getScheme(), b.getScheme()))
  523. return false;
  524. if (!eq(getUser(), b.getUser()))
  525. return false;
  526. if (!eq(getPass(), b.getPass()))
  527. return false;
  528. if (!eq(getHost(), b.getHost()))
  529. return false;
  530. if (getPort() != b.getPort())
  531. return false;
  532. if (!eq(getPath(), b.getPath()))
  533. return false;
  534. return true;
  535. }
  536. private static boolean eq(String a, String b) {
  537. if (References.isSameObject(a, b)) {
  538. return true;
  539. }
  540. if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b))
  541. return true;
  542. if (a == null || b == null)
  543. return false;
  544. return a.equals(b);
  545. }
  546. /**
  547. * Obtain the string form of the URI, with the password included.
  548. *
  549. * @return the URI, including its password field, if any.
  550. */
  551. public String toPrivateString() {
  552. return format(true, false);
  553. }
  554. /** {@inheritDoc} */
  555. @Override
  556. public String toString() {
  557. return format(false, false);
  558. }
  559. private String format(boolean includePassword, boolean escapeNonAscii) {
  560. final StringBuilder r = new StringBuilder();
  561. if (getScheme() != null) {
  562. r.append(getScheme());
  563. r.append("://"); //$NON-NLS-1$
  564. }
  565. if (getUser() != null) {
  566. r.append(escape(getUser(), true, escapeNonAscii));
  567. if (includePassword && getPass() != null) {
  568. r.append(':');
  569. r.append(escape(getPass(), true, escapeNonAscii));
  570. }
  571. }
  572. if (getHost() != null) {
  573. if (getUser() != null && getUser().length() > 0)
  574. r.append('@');
  575. r.append(escape(getHost(), false, escapeNonAscii));
  576. if (getScheme() != null && getPort() > 0) {
  577. r.append(':');
  578. r.append(getPort());
  579. }
  580. }
  581. if (getPath() != null) {
  582. if (getScheme() != null) {
  583. if (!getPath().startsWith("/") && !getPath().isEmpty()) //$NON-NLS-1$
  584. r.append('/');
  585. } else if (getHost() != null)
  586. r.append(':');
  587. if (getScheme() != null)
  588. if (escapeNonAscii)
  589. r.append(escape(getPath(), false, escapeNonAscii));
  590. else
  591. r.append(getRawPath());
  592. else
  593. r.append(getPath());
  594. }
  595. return r.toString();
  596. }
  597. /**
  598. * Get the URI as an ASCII string.
  599. *
  600. * @return the URI as an ASCII string. Password is not included.
  601. */
  602. public String toASCIIString() {
  603. return format(false, true);
  604. }
  605. /**
  606. * Convert the URI including password, formatted with only ASCII characters
  607. * such that it will be valid for use over the network.
  608. *
  609. * @return the URI including password, formatted with only ASCII characters
  610. * such that it will be valid for use over the network.
  611. */
  612. public String toPrivateASCIIString() {
  613. return format(true, true);
  614. }
  615. /**
  616. * Get the "humanish" part of the path. Some examples of a 'humanish' part
  617. * for a full path:
  618. * <table summary="path vs humanish path" border="1">
  619. * <tr>
  620. * <th>Path</th>
  621. * <th>Humanish part</th>
  622. * </tr>
  623. * <tr>
  624. * <td><code>/path/to/repo.git</code></td>
  625. * <td rowspan="4"><code>repo</code></td>
  626. * </tr>
  627. * <tr>
  628. * <td><code>/path/to/repo.git/</code></td>
  629. * </tr>
  630. * <tr>
  631. * <td><code>/path/to/repo/.git</code></td>
  632. * </tr>
  633. * <tr>
  634. * <td><code>/path/to/repo/</code></td>
  635. * </tr>
  636. * <tr>
  637. * <td><code>localhost</code></td>
  638. * <td><code>ssh://localhost/</code></td>
  639. * </tr>
  640. * <tr>
  641. * <td><code>/path//to</code></td>
  642. * <td>an empty string</td>
  643. * </tr>
  644. * </table>
  645. *
  646. * @return the "humanish" part of the path. May be an empty string. Never
  647. * {@code null}.
  648. * @throws java.lang.IllegalArgumentException
  649. * if it's impossible to determine a humanish part, or path is
  650. * {@code null} or empty
  651. * @see #getPath
  652. */
  653. public String getHumanishName() throws IllegalArgumentException {
  654. String s = getPath();
  655. if ("/".equals(s) || "".equals(s)) //$NON-NLS-1$ //$NON-NLS-2$
  656. s = getHost();
  657. if (s == null) // $NON-NLS-1$
  658. throw new IllegalArgumentException();
  659. String[] elements;
  660. if ("file".equals(scheme) || LOCAL_FILE.matcher(s).matches()) //$NON-NLS-1$
  661. elements = s.split("[\\" + File.separatorChar + "/]"); //$NON-NLS-1$ //$NON-NLS-2$
  662. else
  663. elements = s.split("/+"); //$NON-NLS-1$
  664. if (elements.length == 0)
  665. throw new IllegalArgumentException();
  666. String result = elements[elements.length - 1];
  667. if (Constants.DOT_GIT.equals(result))
  668. result = elements[elements.length - 2];
  669. else if (result.endsWith(Constants.DOT_GIT_EXT))
  670. result = result.substring(0, result.length()
  671. - Constants.DOT_GIT_EXT.length());
  672. if (("file".equals(scheme) || LOCAL_FILE.matcher(s) //$NON-NLS-1$
  673. .matches())
  674. && result.endsWith(Constants.DOT_BUNDLE_EXT)) {
  675. result = result.substring(0,
  676. result.length() - Constants.DOT_BUNDLE_EXT.length());
  677. }
  678. return result;
  679. }
  680. }