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 if (s == null)
355 return "";
356 else
357 return s;
358 }
359
360
361
362 private String cleanLeadingSlashes(String p, String s) {
363 if (p.length() >= 3
364 && p.charAt(0) == '/'
365 && p.charAt(2) == ':'
366 && ((p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')
367 || (p.charAt(1) >= 'a' && p.charAt(1) <= 'z')))
368 return p.substring(1);
369 else if (s != null && p.length() >= 2 && p.charAt(0) == '/'
370 && p.charAt(1) == '~')
371 return p.substring(1);
372 else
373 return p;
374 }
375
376
377
378
379
380
381
382 public URIish(URL u) {
383 scheme = u.getProtocol();
384 path = u.getPath();
385 path = cleanLeadingSlashes(path, scheme);
386 try {
387 rawPath = u.toURI().getRawPath();
388 rawPath = cleanLeadingSlashes(rawPath, scheme);
389 } catch (URISyntaxException e) {
390 throw new RuntimeException(e);
391 }
392
393 final String ui = u.getUserInfo();
394 if (ui != null) {
395 final int d = ui.indexOf(':');
396 user = d < 0 ? ui : ui.substring(0, d);
397 pass = d < 0 ? null : ui.substring(d + 1);
398 }
399
400 port = u.getPort();
401 host = u.getHost();
402 }
403
404
405
406
407 public URIish() {
408
409 }
410
411 private URIishsh" href="../../../../org/eclipse/jgit/transport/URIish.html#URIish">URIish(URIish u) {
412 this.scheme = u.scheme;
413 this.rawPath = u.rawPath;
414 this.path = u.path;
415 this.user = u.user;
416 this.pass = u.pass;
417 this.port = u.port;
418 this.host = u.host;
419 }
420
421
422
423
424
425
426 public boolean isRemote() {
427 return getHost() != null;
428 }
429
430
431
432
433
434
435 public String getHost() {
436 return host;
437 }
438
439
440
441
442
443
444
445
446 public URIish setHost(String n) {
447 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
448 r.host = n;
449 return r;
450 }
451
452
453
454
455
456
457 public String getScheme() {
458 return scheme;
459 }
460
461
462
463
464
465
466
467
468 public URIish setScheme(String n) {
469 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
470 r.scheme = n;
471 return r;
472 }
473
474
475
476
477
478
479 public String getPath() {
480 return path;
481 }
482
483
484
485
486
487
488 public String getRawPath() {
489 return rawPath;
490 }
491
492
493
494
495
496
497
498
499 public URIish setPath(String n) {
500 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
501 r.path = n;
502 r.rawPath = n;
503 return r;
504 }
505
506
507
508
509
510
511
512
513
514 public URIish setRawPath(String n) throws URISyntaxException {
515 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
516 r.path = unescape(n);
517 r.rawPath = n;
518 return r;
519 }
520
521
522
523
524
525
526 public String getUser() {
527 return user;
528 }
529
530
531
532
533
534
535
536
537 public URIish setUser(String n) {
538 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
539 r.user = n;
540 return r;
541 }
542
543
544
545
546
547
548 public String getPass() {
549 return pass;
550 }
551
552
553
554
555
556
557
558
559 public URIish setPass(String n) {
560 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
561 r.pass = n;
562 return r;
563 }
564
565
566
567
568
569
570 public int getPort() {
571 return port;
572 }
573
574
575
576
577
578
579
580
581 public URIish setPort(int n) {
582 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
583 r.port = n > 0 ? n : -1;
584 return r;
585 }
586
587
588 @Override
589 public int hashCode() {
590 int hc = 0;
591 if (getScheme() != null)
592 hc = hc * 31 + getScheme().hashCode();
593 if (getUser() != null)
594 hc = hc * 31 + getUser().hashCode();
595 if (getPass() != null)
596 hc = hc * 31 + getPass().hashCode();
597 if (getHost() != null)
598 hc = hc * 31 + getHost().hashCode();
599 if (getPort() > 0)
600 hc = hc * 31 + getPort();
601 if (getPath() != null)
602 hc = hc * 31 + getPath().hashCode();
603 return hc;
604 }
605
606
607 @Override
608 public boolean equals(Object obj) {
609 if (!(obj instanceof URIish))
610 return false;
611 final URIishref="../../../../org/eclipse/jgit/transport/URIish.html#URIish">URIish b = (URIish) obj;
612 if (!eq(getScheme(), b.getScheme()))
613 return false;
614 if (!eq(getUser(), b.getUser()))
615 return false;
616 if (!eq(getPass(), b.getPass()))
617 return false;
618 if (!eq(getHost(), b.getHost()))
619 return false;
620 if (getPort() != b.getPort())
621 return false;
622 if (!eq(getPath(), b.getPath()))
623 return false;
624 return true;
625 }
626
627 private static boolean eq(String a, String b) {
628 if (References.isSameObject(a, b)) {
629 return true;
630 }
631 if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b))
632 return true;
633 if (a == null || b == null)
634 return false;
635 return a.equals(b);
636 }
637
638
639
640
641
642
643 public String toPrivateString() {
644 return format(true, false);
645 }
646
647
648 @Override
649 public String toString() {
650 return format(false, false);
651 }
652
653 private String format(boolean includePassword, boolean escapeNonAscii) {
654 final StringBuilder r = new StringBuilder();
655 if (getScheme() != null) {
656 r.append(getScheme());
657 r.append("://"); //$NON-NLS-1$
658 }
659
660 if (getUser() != null) {
661 r.append(escape(getUser(), true, escapeNonAscii));
662 if (includePassword && getPass() != null) {
663 r.append(':');
664 r.append(escape(getPass(), true, escapeNonAscii));
665 }
666 }
667
668 if (getHost() != null) {
669 if (getUser() != null && getUser().length() > 0)
670 r.append('@');
671 r.append(escape(getHost(), false, escapeNonAscii));
672 if (getScheme() != null && getPort() > 0) {
673 r.append(':');
674 r.append(getPort());
675 }
676 }
677
678 if (getPath() != null) {
679 if (getScheme() != null) {
680 if (!getPath().startsWith("/") && !getPath().isEmpty())
681 r.append('/');
682 } else if (getHost() != null)
683 r.append(':');
684 if (getScheme() != null)
685 if (escapeNonAscii)
686 r.append(escape(getPath(), false, escapeNonAscii));
687 else
688 r.append(getRawPath());
689 else
690 r.append(getPath());
691 }
692
693 return r.toString();
694 }
695
696
697
698
699
700
701 public String toASCIIString() {
702 return format(false, true);
703 }
704
705
706
707
708
709
710
711
712 public String toPrivateASCIIString() {
713 return format(true, true);
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
752
753
754 public String getHumanishName() throws IllegalArgumentException {
755 String s = getPath();
756 if ("/".equals(s) || "".equals(s))
757 s = getHost();
758 if (s == null)
759 throw new IllegalArgumentException();
760
761 String[] elements;
762 if ("file".equals(scheme) || LOCAL_FILE.matcher(s).matches())
763 elements = s.split("[\\" + File.separatorChar + "/]");
764 else
765 elements = s.split("/+");
766 if (elements.length == 0)
767 throw new IllegalArgumentException();
768 String result = elements[elements.length - 1];
769 if (Constants.DOT_GIT.equals(result))
770 result = elements[elements.length - 2];
771 else if (result.endsWith(Constants.DOT_GIT_EXT))
772 result = result.substring(0, result.length()
773 - Constants.DOT_GIT_EXT.length());
774 return result;
775 }
776
777 }