1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 package org.eclipse.jgit.transport;
50
51 import static java.nio.charset.StandardCharsets.UTF_8;
52
53 import java.io.ByteArrayOutputStream;
54 import java.io.File;
55 import java.io.Serializable;
56 import java.net.URISyntaxException;
57 import java.net.URL;
58 import java.util.BitSet;
59 import java.util.regex.Matcher;
60 import java.util.regex.Pattern;
61
62 import org.eclipse.jgit.internal.JGitText;
63 import org.eclipse.jgit.lib.Constants;
64 import org.eclipse.jgit.util.RawParseUtils;
65 import org.eclipse.jgit.util.References;
66 import org.eclipse.jgit.util.StringUtils;
67
68
69
70
71
72
73
74 public class URIish implements Serializable {
75
76
77
78
79
80 private static final String SCHEME_P = "([a-z][a-z0-9+-]+)://"; //$NON-NLS-1$
81
82
83
84
85
86
87
88 private static final String OPT_USER_PWD_P = "(?:([^/:]+)(?::([^\\\\/]+))?@)?";
89
90
91
92
93
94 private static final String HOST_P = "((?:[^\\\\/:]+)|(?:\\[[0-9a-f:]+\\]))";
95
96
97
98
99
100 private static final String OPT_PORT_P = "(?::(\\d*))?";
101
102
103
104
105
106 private static final String USER_HOME_P = "(?:/~(?:[^\\\\/]+))";
107
108
109
110
111
112 private static final String OPT_DRIVE_LETTER_P = "(?:[A-Za-z]:)?";
113
114
115
116
117
118 private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*[^\\\\/]+[\\\\/]*)";
119
120
121
122
123
124 private static final String PATH_P = "(" + OPT_DRIVE_LETTER_P + "[\\\\/]?"
125 + RELATIVE_PATH_P + ")";
126
127 private static final long serialVersionUID = 1L;
128
129
130
131
132
133 private static final Pattern FULL_URI = Pattern.compile("^"
134 + SCHEME_P
135 + "(?:"
136
137 + OPT_USER_PWD_P
138 + HOST_P
139 + OPT_PORT_P
140 + "("
141 + (USER_HOME_P + "?")
142 + "(?:"
143
144 + "[\\\\/])|$"
145 + ")"
146
147 + ")?"
148 + "(.+)?"
149 + "$");
150
151
152
153
154
155 private static final Pattern LOCAL_FILE = Pattern.compile("^"
156 + "([\\\\/]?" + PATH_P + ")"
157 + "$");
158
159
160
161
162
163
164 private static final Pattern SINGLE_SLASH_FILE_URI = Pattern.compile("^"
165 + "(file):([\\\\/](?![\\\\/])"
166 + PATH_P
167 + ")$");
168
169
170
171
172 private static final Pattern RELATIVE_SCP_URI = Pattern.compile("^"
173 + OPT_USER_PWD_P
174 + HOST_P
175 + ":("
176 + ("(?:" + USER_HOME_P + "[\\\\/])?")
177 + RELATIVE_PATH_P
178 + ")$");
179
180
181
182
183 private static final Pattern ABSOLUTE_SCP_URI = Pattern.compile("^"
184 + OPT_USER_PWD_P
185 + "([^\\\\/:]{2,})"
186 + ":("
187 + "[\\\\/]" + RELATIVE_PATH_P
188 + ")$");
189
190 private String scheme;
191
192 private String path;
193
194 private String rawPath;
195
196 private String user;
197
198 private String pass;
199
200 private int port = -1;
201
202 private String host;
203
204
205
206
207
208
209
210
211
212 public URIish(String s) throws URISyntaxException {
213 if (StringUtils.isEmptyOrNull(s)) {
214 throw new URISyntaxException("The uri was empty or null",
215 JGitText.get().cannotParseGitURIish);
216 }
217 Matcher matcher = SINGLE_SLASH_FILE_URI.matcher(s);
218 if (matcher.matches()) {
219 scheme = matcher.group(1);
220 rawPath = cleanLeadingSlashes(matcher.group(2), scheme);
221 path = unescape(rawPath);
222 return;
223 }
224 matcher = FULL_URI.matcher(s);
225 if (matcher.matches()) {
226 scheme = matcher.group(1);
227 user = unescape(matcher.group(2));
228 pass = unescape(matcher.group(3));
229
230
231
232 String portString = matcher.group(5);
233 if ("file".equals(scheme) && "".equals(portString)) {
234 rawPath = cleanLeadingSlashes(
235 n2e(matcher.group(4)) + ":" + portString
236 + n2e(matcher.group(6)) + n2e(matcher.group(7)),
237 scheme);
238 } else {
239 host = unescape(matcher.group(4));
240 if (portString != null && portString.length() > 0) {
241 port = Integer.parseInt(portString);
242 }
243 rawPath = cleanLeadingSlashes(
244 n2e(matcher.group(6)) + n2e(matcher.group(7)), scheme);
245 }
246 path = unescape(rawPath);
247 return;
248 }
249 matcher = RELATIVE_SCP_URI.matcher(s);
250 if (matcher.matches()) {
251 user = matcher.group(1);
252 pass = matcher.group(2);
253 host = matcher.group(3);
254 rawPath = matcher.group(4);
255 path = rawPath;
256 return;
257 }
258 matcher = ABSOLUTE_SCP_URI.matcher(s);
259 if (matcher.matches()) {
260 user = matcher.group(1);
261 pass = matcher.group(2);
262 host = matcher.group(3);
263 rawPath = matcher.group(4);
264 path = rawPath;
265 return;
266 }
267 matcher = LOCAL_FILE.matcher(s);
268 if (matcher.matches()) {
269 rawPath = matcher.group(1);
270 path = rawPath;
271 return;
272 }
273 throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
274 }
275
276 private static int parseHexByte(byte c1, byte c2) {
277 return ((RawParseUtils.parseHexInt4(c1) << 4)
278 | RawParseUtils.parseHexInt4(c2));
279 }
280
281 private static String unescape(String s) throws URISyntaxException {
282 if (s == null)
283 return null;
284 if (s.indexOf('%') < 0)
285 return s;
286
287 byte[] bytes = s.getBytes(UTF_8);
288
289 byte[] os = new byte[bytes.length];
290 int j = 0;
291 for (int i = 0; i < bytes.length; ++i) {
292 byte c = bytes[i];
293 if (c == '%') {
294 if (i + 2 >= bytes.length)
295 throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
296 byte c1 = bytes[i + 1];
297 byte c2 = bytes[i + 2];
298 int val;
299 try {
300 val = parseHexByte(c1, c2);
301 } catch (ArrayIndexOutOfBoundsException e) {
302 throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
303 }
304 os[j++] = (byte) val;
305 i += 2;
306 } else
307 os[j++] = c;
308 }
309 return RawParseUtils.decode(os, 0, j);
310 }
311
312 private static final BitSet reservedChars = new BitSet(127);
313
314 static {
315 for (byte b : Constants.encodeASCII("!*'();:@&=+$,/?#[]"))
316 reservedChars.set(b);
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330 private static String escape(String s, boolean escapeReservedChars,
331 boolean encodeNonAscii) {
332 if (s == null)
333 return null;
334 ByteArrayOutputStream os = new ByteArrayOutputStream(s.length());
335 byte[] bytes = s.getBytes(UTF_8);
336 for (int i = 0; i < bytes.length; ++i) {
337 int b = bytes[i] & 0xFF;
338 if (b <= 32 || (encodeNonAscii && b > 127) || b == '%'
339 || (escapeReservedChars && reservedChars.get(b))) {
340 os.write('%');
341 byte[] tmp = Constants.encodeASCII(String.format("%02x",
342 Integer.valueOf(b)));
343 os.write(tmp[0]);
344 os.write(tmp[1]);
345 } else {
346 os.write(b);
347 }
348 }
349 byte[] buf = os.toByteArray();
350 return RawParseUtils.decode(buf, 0, buf.length);
351 }
352
353 private String n2e(String s) {
354 return s == null ? "" : s;
355 }
356
357
358
359 private String cleanLeadingSlashes(String p, String s) {
360 if (p.length() >= 3
361 && p.charAt(0) == '/'
362 && p.charAt(2) == ':'
363 && ((p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')
364 || (p.charAt(1) >= 'a' && p.charAt(1) <= 'z')))
365 return p.substring(1);
366 else if (s != null && p.length() >= 2 && p.charAt(0) == '/'
367 && p.charAt(1) == '~')
368 return p.substring(1);
369 else
370 return p;
371 }
372
373
374
375
376
377
378
379 public URIish(URL u) {
380 scheme = u.getProtocol();
381 path = u.getPath();
382 path = cleanLeadingSlashes(path, scheme);
383 try {
384 rawPath = u.toURI().getRawPath();
385 rawPath = cleanLeadingSlashes(rawPath, scheme);
386 } catch (URISyntaxException e) {
387 throw new RuntimeException(e);
388 }
389
390 final String ui = u.getUserInfo();
391 if (ui != null) {
392 final int d = ui.indexOf(':');
393 user = d < 0 ? ui : ui.substring(0, d);
394 pass = d < 0 ? null : ui.substring(d + 1);
395 }
396
397 port = u.getPort();
398 host = u.getHost();
399 }
400
401
402
403
404 public URIish() {
405
406 }
407
408 private URIishsh" href="../../../../org/eclipse/jgit/transport/URIish.html#URIish">URIish(URIish u) {
409 this.scheme = u.scheme;
410 this.rawPath = u.rawPath;
411 this.path = u.path;
412 this.user = u.user;
413 this.pass = u.pass;
414 this.port = u.port;
415 this.host = u.host;
416 }
417
418
419
420
421
422
423 public boolean isRemote() {
424 return getHost() != null;
425 }
426
427
428
429
430
431
432 public String getHost() {
433 return host;
434 }
435
436
437
438
439
440
441
442
443 public URIish setHost(String n) {
444 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
445 r.host = n;
446 return r;
447 }
448
449
450
451
452
453
454 public String getScheme() {
455 return scheme;
456 }
457
458
459
460
461
462
463
464
465 public URIish setScheme(String n) {
466 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
467 r.scheme = n;
468 return r;
469 }
470
471
472
473
474
475
476 public String getPath() {
477 return path;
478 }
479
480
481
482
483
484
485 public String getRawPath() {
486 return rawPath;
487 }
488
489
490
491
492
493
494
495
496 public URIish setPath(String n) {
497 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
498 r.path = n;
499 r.rawPath = n;
500 return r;
501 }
502
503
504
505
506
507
508
509
510
511 public URIish setRawPath(String n) throws URISyntaxException {
512 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
513 r.path = unescape(n);
514 r.rawPath = n;
515 return r;
516 }
517
518
519
520
521
522
523 public String getUser() {
524 return user;
525 }
526
527
528
529
530
531
532
533
534 public URIish setUser(String n) {
535 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
536 r.user = n;
537 return r;
538 }
539
540
541
542
543
544
545 public String getPass() {
546 return pass;
547 }
548
549
550
551
552
553
554
555
556 public URIish setPass(String n) {
557 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
558 r.pass = n;
559 return r;
560 }
561
562
563
564
565
566
567 public int getPort() {
568 return port;
569 }
570
571
572
573
574
575
576
577
578 public URIish setPort(int n) {
579 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
580 r.port = n > 0 ? n : -1;
581 return r;
582 }
583
584
585 @Override
586 public int hashCode() {
587 int hc = 0;
588 if (getScheme() != null)
589 hc = hc * 31 + getScheme().hashCode();
590 if (getUser() != null)
591 hc = hc * 31 + getUser().hashCode();
592 if (getPass() != null)
593 hc = hc * 31 + getPass().hashCode();
594 if (getHost() != null)
595 hc = hc * 31 + getHost().hashCode();
596 if (getPort() > 0)
597 hc = hc * 31 + getPort();
598 if (getPath() != null)
599 hc = hc * 31 + getPath().hashCode();
600 return hc;
601 }
602
603
604 @Override
605 public boolean equals(Object obj) {
606 if (!(obj instanceof URIish))
607 return false;
608 final URIishref="../../../../org/eclipse/jgit/transport/URIish.html#URIish">URIish b = (URIish) obj;
609 if (!eq(getScheme(), b.getScheme()))
610 return false;
611 if (!eq(getUser(), b.getUser()))
612 return false;
613 if (!eq(getPass(), b.getPass()))
614 return false;
615 if (!eq(getHost(), b.getHost()))
616 return false;
617 if (getPort() != b.getPort())
618 return false;
619 if (!eq(getPath(), b.getPath()))
620 return false;
621 return true;
622 }
623
624 private static boolean eq(String a, String b) {
625 if (References.isSameObject(a, b)) {
626 return true;
627 }
628 if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b))
629 return true;
630 if (a == null || b == null)
631 return false;
632 return a.equals(b);
633 }
634
635
636
637
638
639
640 public String toPrivateString() {
641 return format(true, false);
642 }
643
644
645 @Override
646 public String toString() {
647 return format(false, false);
648 }
649
650 private String format(boolean includePassword, boolean escapeNonAscii) {
651 final StringBuilder r = new StringBuilder();
652 if (getScheme() != null) {
653 r.append(getScheme());
654 r.append("://"); //$NON-NLS-1$
655 }
656
657 if (getUser() != null) {
658 r.append(escape(getUser(), true, escapeNonAscii));
659 if (includePassword && getPass() != null) {
660 r.append(':');
661 r.append(escape(getPass(), true, escapeNonAscii));
662 }
663 }
664
665 if (getHost() != null) {
666 if (getUser() != null && getUser().length() > 0)
667 r.append('@');
668 r.append(escape(getHost(), false, escapeNonAscii));
669 if (getScheme() != null && getPort() > 0) {
670 r.append(':');
671 r.append(getPort());
672 }
673 }
674
675 if (getPath() != null) {
676 if (getScheme() != null) {
677 if (!getPath().startsWith("/") && !getPath().isEmpty())
678 r.append('/');
679 } else if (getHost() != null)
680 r.append(':');
681 if (getScheme() != null)
682 if (escapeNonAscii)
683 r.append(escape(getPath(), false, escapeNonAscii));
684 else
685 r.append(getRawPath());
686 else
687 r.append(getPath());
688 }
689
690 return r.toString();
691 }
692
693
694
695
696
697
698 public String toASCIIString() {
699 return format(false, true);
700 }
701
702
703
704
705
706
707
708
709 public String toPrivateASCIIString() {
710 return format(true, true);
711 }
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751 public String getHumanishName() throws IllegalArgumentException {
752 String s = getPath();
753 if ("/".equals(s) || "".equals(s))
754 s = getHost();
755 if (s == null)
756 throw new IllegalArgumentException();
757
758 String[] elements;
759 if ("file".equals(scheme) || LOCAL_FILE.matcher(s).matches())
760 elements = s.split("[\\" + File.separatorChar + "/]");
761 else
762 elements = s.split("/+");
763 if (elements.length == 0)
764 throw new IllegalArgumentException();
765 String result = elements[elements.length - 1];
766 if (Constants.DOT_GIT.equals(result))
767 result = elements[elements.length - 2];
768 else if (result.endsWith(Constants.DOT_GIT_EXT))
769 result = result.substring(0, result.length()
770 - Constants.DOT_GIT_EXT.length());
771 return result;
772 }
773
774 }