1414import java .nio .file .Path ;
1515import java .util .ArrayList ;
1616import java .util .Arrays ;
17+ import java .util .Comparator ;
1718import java .util .List ;
1819import java .util .Objects ;
1920
@@ -46,8 +47,8 @@ private FileAccessTree(FilesEntitlement filesEntitlement, PathLookup pathLookup)
4647 readPaths .add (tempDir );
4748 writePaths .add (tempDir );
4849
49- readPaths .sort (String :: compareTo );
50- writePaths .sort (String :: compareTo );
50+ readPaths .sort (PATH_ORDER );
51+ writePaths .sort (PATH_ORDER );
5152
5253 this .readPaths = pruneSortedPaths (readPaths ).toArray (new String [0 ]);
5354 this .writePaths = pruneSortedPaths (writePaths ).toArray (new String [0 ]);
@@ -60,7 +61,7 @@ private static List<String> pruneSortedPaths(List<String> paths) {
6061 prunedReadPaths .add (currentPath );
6162 for (int i = 1 ; i < paths .size (); ++i ) {
6263 String nextPath = paths .get (i );
63- if (nextPath . startsWith (currentPath ) == false ) {
64+ if (isParent (currentPath , nextPath ) == false ) {
6465 prunedReadPaths .add (nextPath );
6566 currentPath = nextPath ;
6667 }
@@ -88,21 +89,28 @@ static String normalizePath(Path path) {
8889 // Note that toAbsolutePath produces paths separated by the default file separator,
8990 // so on Windows, if the given path uses forward slashes, this consistently
9091 // converts it to backslashes.
91- return path .toAbsolutePath ().normalize ().toString ();
92+ String result = path .toAbsolutePath ().normalize ().toString ();
93+ while (result .endsWith (FILE_SEPARATOR )) {
94+ result = result .substring (0 , result .length () - FILE_SEPARATOR .length ());
95+ }
96+ return result ;
9297 }
9398
9499 private static boolean checkPath (String path , String [] paths ) {
95100 if (paths .length == 0 ) {
96101 return false ;
97102 }
98- int ndx = Arrays .binarySearch (paths , path );
103+ int ndx = Arrays .binarySearch (paths , path , PATH_ORDER );
99104 if (ndx < -1 ) {
100- String maybeParent = paths [-ndx - 2 ];
101- return path .startsWith (maybeParent ) && path .startsWith (FILE_SEPARATOR , maybeParent .length ());
105+ return isParent (paths [-ndx - 2 ], path );
102106 }
103107 return ndx >= 0 ;
104108 }
105109
110+ private static boolean isParent (String maybeParent , String path ) {
111+ return path .startsWith (maybeParent ) && path .startsWith (FILE_SEPARATOR , maybeParent .length ());
112+ }
113+
106114 @ Override
107115 public boolean equals (Object o ) {
108116 if (o == null || getClass () != o .getClass ()) return false ;
@@ -114,4 +122,30 @@ public boolean equals(Object o) {
114122 public int hashCode () {
115123 return Objects .hash (Arrays .hashCode (readPaths ), Arrays .hashCode (writePaths ));
116124 }
125+
126+ /**
127+ * For our lexicographic sort trick to work correctly, we must have path separators sort before
128+ * any other character so that files in a directory appear immediately after that directory.
129+ * For example, we require [/a, /a/b, /a.xml] rather than the natural order [/a, /a.xml, /a/b].
130+ */
131+ private static final Comparator <String > PATH_ORDER = (s1 , s2 ) -> {
132+ Path p1 = Path .of (s1 );
133+ Path p2 = Path .of (s2 );
134+ var i1 = p1 .iterator ();
135+ var i2 = p2 .iterator ();
136+ while (i1 .hasNext () && i2 .hasNext ()) {
137+ int cmp = i1 .next ().compareTo (i2 .next ());
138+ if (cmp != 0 ) {
139+ return cmp ;
140+ }
141+ }
142+ if (i1 .hasNext ()) {
143+ return 1 ;
144+ } else if (i2 .hasNext ()) {
145+ return -1 ;
146+ } else {
147+ assert p1 .equals (p2 );
148+ return 0 ;
149+ }
150+ };
117151}
0 commit comments