Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java
41159 views
1
/*
2
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package jdk.internal.jrtfs;
26
27
import java.io.File;
28
import java.io.IOError;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.io.OutputStream;
32
import java.net.URI;
33
import java.net.URISyntaxException;
34
import java.nio.channels.FileChannel;
35
import java.nio.channels.SeekableByteChannel;
36
import java.nio.file.*;
37
import java.nio.file.DirectoryStream.Filter;
38
import java.nio.file.attribute.BasicFileAttributes;
39
import java.nio.file.attribute.BasicFileAttributeView;
40
import java.nio.file.attribute.FileAttribute;
41
import java.nio.file.attribute.FileTime;
42
import java.util.Iterator;
43
import java.util.Map;
44
import java.util.NoSuchElementException;
45
import java.util.Objects;
46
import java.util.Set;
47
import static java.nio.file.StandardOpenOption.*;
48
import static java.nio.file.StandardCopyOption.*;
49
50
/**
51
* Base class for Path implementation of jrt file systems.
52
*
53
* @implNote This class needs to maintain JDK 8 source compatibility.
54
*
55
* It is used internally in the JDK to implement jimage/jrtfs access,
56
* but also compiled and delivered as part of the jrtfs.jar to support access
57
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
58
*/
59
final class JrtPath implements Path {
60
61
final JrtFileSystem jrtfs;
62
private final String path;
63
private volatile int[] offsets;
64
65
JrtPath(JrtFileSystem jrtfs, String path) {
66
this.jrtfs = jrtfs;
67
this.path = normalize(path);
68
this.resolved = null;
69
}
70
71
JrtPath(JrtFileSystem jrtfs, String path, boolean normalized) {
72
this.jrtfs = jrtfs;
73
this.path = normalized ? path : normalize(path);
74
this.resolved = null;
75
}
76
77
final String getName() {
78
return path;
79
}
80
81
@Override
82
public final JrtPath getRoot() {
83
if (this.isAbsolute()) {
84
return jrtfs.getRootPath();
85
} else {
86
return null;
87
}
88
}
89
90
@Override
91
public final JrtPath getFileName() {
92
if (path.isEmpty())
93
return this;
94
if (path.length() == 1 && path.charAt(0) == '/')
95
return null;
96
int off = path.lastIndexOf('/');
97
if (off == -1)
98
return this;
99
return new JrtPath(jrtfs, path.substring(off + 1), true);
100
}
101
102
@Override
103
public final JrtPath getParent() {
104
initOffsets();
105
int count = offsets.length;
106
if (count == 0) { // no elements so no parent
107
return null;
108
}
109
int off = offsets[count - 1] - 1;
110
if (off <= 0) { // parent is root only (may be null)
111
return getRoot();
112
}
113
return new JrtPath(jrtfs, path.substring(0, off));
114
}
115
116
@Override
117
public final int getNameCount() {
118
initOffsets();
119
return offsets.length;
120
}
121
122
@Override
123
public final JrtPath getName(int index) {
124
initOffsets();
125
if (index < 0 || index >= offsets.length) {
126
throw new IllegalArgumentException("index: " +
127
index + ", offsets length: " + offsets.length);
128
}
129
int begin = offsets[index];
130
int end;
131
if (index == (offsets.length - 1)) {
132
end = path.length();
133
} else {
134
end = offsets[index + 1];
135
}
136
return new JrtPath(jrtfs, path.substring(begin, end));
137
}
138
139
@Override
140
public final JrtPath subpath(int beginIndex, int endIndex) {
141
initOffsets();
142
if (beginIndex < 0 || endIndex > offsets.length ||
143
beginIndex >= endIndex) {
144
throw new IllegalArgumentException(
145
"beginIndex: " + beginIndex + ", endIndex: " + endIndex +
146
", offsets length: " + offsets.length);
147
}
148
// starting/ending offsets
149
int begin = offsets[beginIndex];
150
int end;
151
if (endIndex == offsets.length) {
152
end = path.length();
153
} else {
154
end = offsets[endIndex];
155
}
156
return new JrtPath(jrtfs, path.substring(begin, end));
157
}
158
159
@Override
160
public final JrtPath toRealPath(LinkOption... options) throws IOException {
161
return jrtfs.toRealPath(this, options);
162
}
163
164
@Override
165
public final JrtPath toAbsolutePath() {
166
if (isAbsolute())
167
return this;
168
return new JrtPath(jrtfs, "/" + path, true);
169
}
170
171
@Override
172
public final URI toUri() {
173
String p = toAbsolutePath().path;
174
if (!p.startsWith("/modules") || p.contains("..")) {
175
throw new IOError(new RuntimeException(p + " cannot be represented as URI"));
176
}
177
178
p = p.substring("/modules".length());
179
if (p.isEmpty()) {
180
p = "/";
181
}
182
return toUri(p);
183
}
184
185
private boolean equalsNameAt(JrtPath other, int index) {
186
int mbegin = offsets[index];
187
int mlen;
188
if (index == (offsets.length - 1)) {
189
mlen = path.length() - mbegin;
190
} else {
191
mlen = offsets[index + 1] - mbegin - 1;
192
}
193
int obegin = other.offsets[index];
194
int olen;
195
if (index == (other.offsets.length - 1)) {
196
olen = other.path.length() - obegin;
197
} else {
198
olen = other.offsets[index + 1] - obegin - 1;
199
}
200
if (mlen != olen) {
201
return false;
202
}
203
int n = 0;
204
while (n < mlen) {
205
if (path.charAt(mbegin + n) != other.path.charAt(obegin + n)) {
206
return false;
207
}
208
n++;
209
}
210
return true;
211
}
212
213
@Override
214
public final JrtPath relativize(Path other) {
215
final JrtPath o = checkPath(other);
216
if (o.equals(this)) {
217
return new JrtPath(jrtfs, "", true);
218
}
219
if (path.isEmpty()) {
220
return o;
221
}
222
if (jrtfs != o.jrtfs || isAbsolute() != o.isAbsolute()) {
223
throw new IllegalArgumentException(
224
"Incorrect filesystem or path: " + other);
225
}
226
final String tp = this.path;
227
final String op = o.path;
228
if (op.startsWith(tp)) { // fast path
229
int off = tp.length();
230
if (op.charAt(off - 1) == '/')
231
return new JrtPath(jrtfs, op.substring(off), true);
232
if (op.charAt(off) == '/')
233
return new JrtPath(jrtfs, op.substring(off + 1), true);
234
}
235
int mc = this.getNameCount();
236
int oc = o.getNameCount();
237
int n = Math.min(mc, oc);
238
int i = 0;
239
while (i < n) {
240
if (!equalsNameAt(o, i)) {
241
break;
242
}
243
i++;
244
}
245
int dotdots = mc - i;
246
int len = dotdots * 3 - 1;
247
if (i < oc) {
248
len += (o.path.length() - o.offsets[i] + 1);
249
}
250
StringBuilder sb = new StringBuilder(len);
251
while (dotdots > 0) {
252
sb.append("..");
253
if (sb.length() < len) { // no tailing slash at the end
254
sb.append('/');
255
}
256
dotdots--;
257
}
258
if (i < oc) {
259
sb.append(o.path, o.offsets[i], o.path.length());
260
}
261
return new JrtPath(jrtfs, sb.toString(), true);
262
}
263
264
@Override
265
public JrtFileSystem getFileSystem() {
266
return jrtfs;
267
}
268
269
@Override
270
public final boolean isAbsolute() {
271
return !path.isEmpty() && path.charAt(0) == '/';
272
}
273
274
@Override
275
public final JrtPath resolve(Path other) {
276
final JrtPath o = checkPath(other);
277
if (this.path.isEmpty() || o.isAbsolute()) {
278
return o;
279
}
280
if (o.path.isEmpty()) {
281
return this;
282
}
283
StringBuilder sb = new StringBuilder(path.length() + o.path.length() + 1);
284
sb.append(path);
285
if (path.charAt(path.length() - 1) != '/')
286
sb.append('/');
287
sb.append(o.path);
288
return new JrtPath(jrtfs, sb.toString(), true);
289
}
290
291
@Override
292
public final Path resolveSibling(Path other) {
293
Objects.requireNonNull(other, "other");
294
Path parent = getParent();
295
return (parent == null) ? other : parent.resolve(other);
296
}
297
298
@Override
299
public final boolean startsWith(Path other) {
300
if (!(Objects.requireNonNull(other) instanceof JrtPath))
301
return false;
302
final JrtPath o = (JrtPath)other;
303
final String tp = this.path;
304
final String op = o.path;
305
if (isAbsolute() != o.isAbsolute() || !tp.startsWith(op)) {
306
return false;
307
}
308
int off = op.length();
309
if (off == 0) {
310
return tp.isEmpty();
311
}
312
// check match is on name boundary
313
return tp.length() == off || tp.charAt(off) == '/' ||
314
off == 0 || op.charAt(off - 1) == '/';
315
}
316
317
@Override
318
public final boolean endsWith(Path other) {
319
if (!(Objects.requireNonNull(other) instanceof JrtPath))
320
return false;
321
final JrtPath o = (JrtPath)other;
322
final JrtPath t = this;
323
int olast = o.path.length() - 1;
324
if (olast > 0 && o.path.charAt(olast) == '/') {
325
olast--;
326
}
327
int last = t.path.length() - 1;
328
if (last > 0 && t.path.charAt(last) == '/') {
329
last--;
330
}
331
if (olast == -1) { // o.path.length == 0
332
return last == -1;
333
}
334
if ((o.isAbsolute() && (!t.isAbsolute() || olast != last))
335
|| last < olast) {
336
return false;
337
}
338
for (; olast >= 0; olast--, last--) {
339
if (o.path.charAt(olast) != t.path.charAt(last)) {
340
return false;
341
}
342
}
343
return o.path.charAt(olast + 1) == '/' ||
344
last == -1 || t.path.charAt(last) == '/';
345
}
346
347
@Override
348
public final JrtPath resolve(String other) {
349
return resolve(getFileSystem().getPath(other));
350
}
351
352
@Override
353
public final Path resolveSibling(String other) {
354
return resolveSibling(getFileSystem().getPath(other));
355
}
356
357
@Override
358
public final boolean startsWith(String other) {
359
return startsWith(getFileSystem().getPath(other));
360
}
361
362
@Override
363
public final boolean endsWith(String other) {
364
return endsWith(getFileSystem().getPath(other));
365
}
366
367
@Override
368
public final JrtPath normalize() {
369
String res = getResolved();
370
if (res == path) { // no change
371
return this;
372
}
373
return new JrtPath(jrtfs, res, true);
374
}
375
376
private JrtPath checkPath(Path path) {
377
Objects.requireNonNull(path);
378
if (!(path instanceof JrtPath))
379
throw new ProviderMismatchException("path class: " +
380
path.getClass());
381
return (JrtPath) path;
382
}
383
384
// create offset list if not already created
385
private void initOffsets() {
386
if (this.offsets == null) {
387
int len = path.length();
388
// count names
389
int count = 0;
390
int off = 0;
391
while (off < len) {
392
char c = path.charAt(off++);
393
if (c != '/') {
394
count++;
395
off = path.indexOf('/', off);
396
if (off == -1)
397
break;
398
}
399
}
400
// populate offsets
401
int[] offsets = new int[count];
402
count = 0;
403
off = 0;
404
while (off < len) {
405
char c = path.charAt(off);
406
if (c == '/') {
407
off++;
408
} else {
409
offsets[count++] = off++;
410
off = path.indexOf('/', off);
411
if (off == -1)
412
break;
413
}
414
}
415
this.offsets = offsets;
416
}
417
}
418
419
private volatile String resolved;
420
421
final String getResolvedPath() {
422
String r = resolved;
423
if (r == null) {
424
if (isAbsolute()) {
425
r = getResolved();
426
} else {
427
r = toAbsolutePath().getResolvedPath();
428
}
429
resolved = r;
430
}
431
return r;
432
}
433
434
// removes redundant slashs, replace "\" to separator "/"
435
// and check for invalid characters
436
private static String normalize(String path) {
437
int len = path.length();
438
if (len == 0) {
439
return path;
440
}
441
char prevC = 0;
442
for (int i = 0; i < len; i++) {
443
char c = path.charAt(i);
444
if (c == '\\' || c == '\u0000') {
445
return normalize(path, i);
446
}
447
if (c == '/' && prevC == '/') {
448
return normalize(path, i - 1);
449
}
450
prevC = c;
451
}
452
if (prevC == '/' && len > 1) {
453
return path.substring(0, len - 1);
454
}
455
return path;
456
}
457
458
private static String normalize(String path, int off) {
459
int len = path.length();
460
StringBuilder to = new StringBuilder(len);
461
to.append(path, 0, off);
462
char prevC = 0;
463
while (off < len) {
464
char c = path.charAt(off++);
465
if (c == '\\') {
466
c = '/';
467
}
468
if (c == '/' && prevC == '/') {
469
continue;
470
}
471
if (c == '\u0000') {
472
throw new InvalidPathException(path,
473
"Path: NUL character not allowed");
474
}
475
to.append(c);
476
prevC = c;
477
}
478
len = to.length();
479
if (len > 1 && to.charAt(len - 1) == '/') {
480
to.deleteCharAt(len - 1);
481
}
482
return to.toString();
483
}
484
485
// Remove DotSlash(./) and resolve DotDot (..) components
486
private String getResolved() {
487
int length = path.length();
488
if (length == 0 || (path.indexOf("./") == -1 && path.charAt(length - 1) != '.')) {
489
return path;
490
} else {
491
return resolvePath();
492
}
493
}
494
495
private String resolvePath() {
496
int length = path.length();
497
char[] to = new char[length];
498
int nc = getNameCount();
499
int[] lastM = new int[nc];
500
int lastMOff = -1;
501
int m = 0;
502
for (int i = 0; i < nc; i++) {
503
int n = offsets[i];
504
int len = (i == offsets.length - 1) ? length - n
505
: offsets[i + 1] - n - 1;
506
if (len == 1 && path.charAt(n) == '.') {
507
if (m == 0 && path.charAt(0) == '/') // absolute path
508
to[m++] = '/';
509
continue;
510
}
511
if (len == 2 && path.charAt(n) == '.' && path.charAt(n + 1) == '.') {
512
if (lastMOff >= 0) {
513
m = lastM[lastMOff--]; // retreat
514
continue;
515
}
516
if (path.charAt(0) == '/') { // "/../xyz" skip
517
if (m == 0)
518
to[m++] = '/';
519
} else { // "../xyz" -> "../xyz"
520
if (m != 0 && to[m-1] != '/')
521
to[m++] = '/';
522
while (len-- > 0)
523
to[m++] = path.charAt(n++);
524
}
525
continue;
526
}
527
if (m == 0 && path.charAt(0) == '/' || // absolute path
528
m != 0 && to[m-1] != '/') { // not the first name
529
to[m++] = '/';
530
}
531
lastM[++lastMOff] = m;
532
while (len-- > 0)
533
to[m++] = path.charAt(n++);
534
}
535
if (m > 1 && to[m - 1] == '/')
536
m--;
537
return (m == to.length) ? new String(to) : new String(to, 0, m);
538
}
539
540
@Override
541
public final String toString() {
542
return path;
543
}
544
545
@Override
546
public final int hashCode() {
547
return path.hashCode();
548
}
549
550
@Override
551
public final boolean equals(Object obj) {
552
return obj instanceof JrtPath &&
553
this.path.equals(((JrtPath) obj).path);
554
}
555
556
@Override
557
public final int compareTo(Path other) {
558
final JrtPath o = checkPath(other);
559
return path.compareTo(o.path);
560
}
561
562
@Override
563
public final WatchKey register(
564
WatchService watcher,
565
WatchEvent.Kind<?>[] events,
566
WatchEvent.Modifier... modifiers) {
567
Objects.requireNonNull(watcher, "watcher");
568
Objects.requireNonNull(events, "events");
569
Objects.requireNonNull(modifiers, "modifiers");
570
throw new UnsupportedOperationException();
571
}
572
573
@Override
574
public final WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
575
return register(watcher, events, new WatchEvent.Modifier[0]);
576
}
577
578
@Override
579
public final File toFile() {
580
throw new UnsupportedOperationException();
581
}
582
583
@Override
584
public final Iterator<Path> iterator() {
585
return new Iterator<Path>() {
586
private int i = 0;
587
588
@Override
589
public boolean hasNext() {
590
return (i < getNameCount());
591
}
592
593
@Override
594
public Path next() {
595
if (i < getNameCount()) {
596
Path result = getName(i);
597
i++;
598
return result;
599
} else {
600
throw new NoSuchElementException();
601
}
602
}
603
604
@Override
605
public void remove() {
606
throw new ReadOnlyFileSystemException();
607
}
608
};
609
}
610
611
// Helpers for JrtFileSystemProvider and JrtFileSystem
612
613
final JrtPath readSymbolicLink() throws IOException {
614
if (!jrtfs.isLink(this)) {
615
throw new IOException("not a symbolic link");
616
}
617
return jrtfs.resolveLink(this);
618
}
619
620
final boolean isHidden() {
621
return false;
622
}
623
624
final void createDirectory(FileAttribute<?>... attrs)
625
throws IOException {
626
jrtfs.createDirectory(this, attrs);
627
}
628
629
final InputStream newInputStream(OpenOption... options) throws IOException {
630
if (options.length > 0) {
631
for (OpenOption opt : options) {
632
if (opt != READ) {
633
throw new UnsupportedOperationException("'" + opt + "' not allowed");
634
}
635
}
636
}
637
return jrtfs.newInputStream(this);
638
}
639
640
final DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
641
throws IOException {
642
return new JrtDirectoryStream(this, filter);
643
}
644
645
final void delete() throws IOException {
646
jrtfs.deleteFile(this, true);
647
}
648
649
final void deleteIfExists() throws IOException {
650
jrtfs.deleteFile(this, false);
651
}
652
653
final JrtFileAttributes getAttributes(LinkOption... options) throws IOException {
654
JrtFileAttributes zfas = jrtfs.getFileAttributes(this, options);
655
if (zfas == null) {
656
throw new NoSuchFileException(toString());
657
}
658
return zfas;
659
}
660
661
final void setAttribute(String attribute, Object value, LinkOption... options)
662
throws IOException {
663
JrtFileAttributeView.setAttribute(this, attribute, value);
664
}
665
666
final Map<String, Object> readAttributes(String attributes, LinkOption... options)
667
throws IOException {
668
return JrtFileAttributeView.readAttributes(this, attributes, options);
669
}
670
671
final void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
672
throws IOException {
673
jrtfs.setTimes(this, mtime, atime, ctime);
674
}
675
676
final FileStore getFileStore() throws IOException {
677
// each JrtFileSystem only has one root (as requested for now)
678
if (exists()) {
679
return jrtfs.getFileStore(this);
680
}
681
throw new NoSuchFileException(path);
682
}
683
684
final boolean isSameFile(Path other) throws IOException {
685
if (this == other || this.equals(other)) {
686
return true;
687
}
688
if (other == null || this.getFileSystem() != other.getFileSystem()) {
689
return false;
690
}
691
this.checkAccess();
692
JrtPath o = (JrtPath) other;
693
o.checkAccess();
694
return this.getResolvedPath().equals(o.getResolvedPath()) ||
695
jrtfs.isSameFile(this, o);
696
}
697
698
final SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
699
FileAttribute<?>... attrs)
700
throws IOException
701
{
702
return jrtfs.newByteChannel(this, options, attrs);
703
}
704
705
final FileChannel newFileChannel(Set<? extends OpenOption> options,
706
FileAttribute<?>... attrs)
707
throws IOException {
708
return jrtfs.newFileChannel(this, options, attrs);
709
}
710
711
final void checkAccess(AccessMode... modes) throws IOException {
712
if (modes.length == 0) { // check if the path exists
713
jrtfs.checkNode(this); // no need to follow link. the "link" node
714
// is built from real node under "/module"
715
} else {
716
boolean w = false;
717
for (AccessMode mode : modes) {
718
switch (mode) {
719
case READ:
720
break;
721
case WRITE:
722
w = true;
723
break;
724
case EXECUTE:
725
throw new AccessDeniedException(toString());
726
default:
727
throw new UnsupportedOperationException();
728
}
729
}
730
jrtfs.checkNode(this);
731
if (w && jrtfs.isReadOnly()) {
732
throw new AccessDeniedException(toString());
733
}
734
}
735
}
736
737
final boolean exists() {
738
try {
739
return jrtfs.exists(this);
740
} catch (IOException x) {}
741
return false;
742
}
743
744
final OutputStream newOutputStream(OpenOption... options) throws IOException {
745
if (options.length == 0) {
746
return jrtfs.newOutputStream(this, CREATE_NEW, WRITE);
747
}
748
return jrtfs.newOutputStream(this, options);
749
}
750
751
final void move(JrtPath target, CopyOption... options) throws IOException {
752
if (this.jrtfs == target.jrtfs) {
753
jrtfs.copyFile(true, this, target, options);
754
} else {
755
copyToTarget(target, options);
756
delete();
757
}
758
}
759
760
final void copy(JrtPath target, CopyOption... options) throws IOException {
761
if (this.jrtfs == target.jrtfs) {
762
jrtfs.copyFile(false, this, target, options);
763
} else {
764
copyToTarget(target, options);
765
}
766
}
767
768
private void copyToTarget(JrtPath target, CopyOption... options)
769
throws IOException {
770
boolean replaceExisting = false;
771
boolean copyAttrs = false;
772
for (CopyOption opt : options) {
773
if (opt == REPLACE_EXISTING) {
774
replaceExisting = true;
775
} else if (opt == COPY_ATTRIBUTES) {
776
copyAttrs = true;
777
}
778
}
779
// attributes of source file
780
BasicFileAttributes jrtfas = getAttributes();
781
// check if target exists
782
boolean exists;
783
if (replaceExisting) {
784
try {
785
target.deleteIfExists();
786
exists = false;
787
} catch (DirectoryNotEmptyException x) {
788
exists = true;
789
}
790
} else {
791
exists = target.exists();
792
}
793
if (exists) {
794
throw new FileAlreadyExistsException(target.toString());
795
}
796
if (jrtfas.isDirectory()) {
797
// create directory or file
798
target.createDirectory();
799
} else {
800
try (InputStream is = jrtfs.newInputStream(this);
801
OutputStream os = target.newOutputStream()) {
802
byte[] buf = new byte[8192];
803
int n;
804
while ((n = is.read(buf)) != -1) {
805
os.write(buf, 0, n);
806
}
807
}
808
}
809
if (copyAttrs) {
810
BasicFileAttributeView view =
811
Files.getFileAttributeView(target, BasicFileAttributeView.class);
812
try {
813
view.setTimes(jrtfas.lastModifiedTime(),
814
jrtfas.lastAccessTime(),
815
jrtfas.creationTime());
816
} catch (IOException x) {
817
try {
818
target.delete(); // rollback?
819
} catch (IOException ignore) {}
820
throw x;
821
}
822
}
823
}
824
825
// adopted from sun.nio.fs.UnixUriUtils
826
private static URI toUri(String str) {
827
char[] path = str.toCharArray();
828
assert path[0] == '/';
829
StringBuilder sb = new StringBuilder();
830
sb.append(path[0]);
831
for (int i = 1; i < path.length; i++) {
832
char c = (char)(path[i] & 0xff);
833
if (match(c, L_PATH, H_PATH)) {
834
sb.append(c);
835
} else {
836
sb.append('%');
837
sb.append(hexDigits[(c >> 4) & 0x0f]);
838
sb.append(hexDigits[(c) & 0x0f]);
839
}
840
}
841
842
try {
843
return new URI("jrt:" + sb.toString());
844
} catch (URISyntaxException x) {
845
throw new AssertionError(x); // should not happen
846
}
847
}
848
849
// The following is copied from java.net.URI
850
851
// Compute the low-order mask for the characters in the given string
852
private static long lowMask(String chars) {
853
int n = chars.length();
854
long m = 0;
855
for (int i = 0; i < n; i++) {
856
char c = chars.charAt(i);
857
if (c < 64)
858
m |= (1L << c);
859
}
860
return m;
861
}
862
863
// Compute the high-order mask for the characters in the given string
864
private static long highMask(String chars) {
865
int n = chars.length();
866
long m = 0;
867
for (int i = 0; i < n; i++) {
868
char c = chars.charAt(i);
869
if ((c >= 64) && (c < 128))
870
m |= (1L << (c - 64));
871
}
872
return m;
873
}
874
875
// Compute a low-order mask for the characters
876
// between first and last, inclusive
877
private static long lowMask(char first, char last) {
878
long m = 0;
879
int f = Math.max(Math.min(first, 63), 0);
880
int l = Math.max(Math.min(last, 63), 0);
881
for (int i = f; i <= l; i++)
882
m |= 1L << i;
883
return m;
884
}
885
886
// Compute a high-order mask for the characters
887
// between first and last, inclusive
888
private static long highMask(char first, char last) {
889
long m = 0;
890
int f = Math.max(Math.min(first, 127), 64) - 64;
891
int l = Math.max(Math.min(last, 127), 64) - 64;
892
for (int i = f; i <= l; i++)
893
m |= 1L << i;
894
return m;
895
}
896
897
// Tell whether the given character is permitted by the given mask pair
898
private static boolean match(char c, long lowMask, long highMask) {
899
if (c < 64)
900
return ((1L << c) & lowMask) != 0;
901
if (c < 128)
902
return ((1L << (c - 64)) & highMask) != 0;
903
return false;
904
}
905
906
// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
907
// "8" | "9"
908
private static final long L_DIGIT = lowMask('0', '9');
909
private static final long H_DIGIT = 0L;
910
911
// upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
912
// "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
913
// "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
914
private static final long L_UPALPHA = 0L;
915
private static final long H_UPALPHA = highMask('A', 'Z');
916
917
// lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
918
// "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
919
// "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
920
private static final long L_LOWALPHA = 0L;
921
private static final long H_LOWALPHA = highMask('a', 'z');
922
923
// alpha = lowalpha | upalpha
924
private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
925
private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
926
927
// alphanum = alpha | digit
928
private static final long L_ALPHANUM = L_DIGIT | L_ALPHA;
929
private static final long H_ALPHANUM = H_DIGIT | H_ALPHA;
930
931
// mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
932
// "(" | ")"
933
private static final long L_MARK = lowMask("-_.!~*'()");
934
private static final long H_MARK = highMask("-_.!~*'()");
935
936
// unreserved = alphanum | mark
937
private static final long L_UNRESERVED = L_ALPHANUM | L_MARK;
938
private static final long H_UNRESERVED = H_ALPHANUM | H_MARK;
939
940
// pchar = unreserved | escaped |
941
// ":" | "@" | "&" | "=" | "+" | "$" | ","
942
private static final long L_PCHAR
943
= L_UNRESERVED | lowMask(":@&=+$,");
944
private static final long H_PCHAR
945
= H_UNRESERVED | highMask(":@&=+$,");
946
947
// All valid path characters
948
private static final long L_PATH = L_PCHAR | lowMask(";/");
949
private static final long H_PATH = H_PCHAR | highMask(";/");
950
951
private static final char[] hexDigits = {
952
'0', '1', '2', '3', '4', '5', '6', '7',
953
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
954
};
955
}
956
957