Skip to content

Commit 448d8d0

Browse files
committed
Fix resolvePath() method in UriUtils
1 parent b91a6c5 commit 448d8d0

4 files changed

Lines changed: 31 additions & 15 deletions

File tree

shellfx-dialogs/src/main/java/com/techsenger/shellfx/dialogs/file/FileChooserDialogPresenter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ void onLocationsOpened() {
385385
var previousUri = storage.getUri();
386386
for (var i = 0; i < segments.size(); i++) {
387387
var segment = segments.get(i);
388-
var segmentUri = UriUtils.resolvePath(previousUri, segment);
388+
var segmentUri = UriUtils.resolvePath(previousUri, segment, true);
389389
var directoryLocation = new Location(
390390
DialogIcons.FOLDER,
391391
segment,
@@ -456,7 +456,7 @@ protected void onRename(int fileIndex) {
456456
protected void onEditCommitted(T file) {
457457
switch (editType) {
458458
case NEW_DIRECTORY -> {
459-
var dirUri = UriUtils.resolvePath(directory, file.getName());
459+
var dirUri = UriUtils.resolvePath(directory, file.getName(), true);
460460
try {
461461
this.storage.createDirectory(dirUri);
462462
updateFiles(file);
@@ -727,7 +727,7 @@ private void createInitialColumns() {
727727
showWarning("The file '" + fileName + "' does not exist.");
728728
return null;
729729
}
730-
URI fileUri = UriUtils.resolvePath(getDirectory(), fileName);
730+
URI fileUri = UriUtils.resolvePath(getDirectory(), fileName, false);
731731
var file = this.storage.createVirtual(null, fileName, fileUri);
732732
return file;
733733
}

shellfx-storage/src/main/java/com/techsenger/shellfx/storage/DefaultGenericFile.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public DefaultGenericFile getChild(String childName, FileEntryType childEntryTyp
151151
child.storage = this.storage;
152152
child.entryType = childEntryType;
153153
child.name = childName;
154-
child.uri = UriUtils.resolvePath(this.uri, childName);
154+
child.uri = UriUtils.resolvePath(this.uri, childName, childEntryType == FileEntryType.DIRECTORY);
155155
child.virtual = true;
156156
return child;
157157
}
@@ -280,7 +280,7 @@ private List<DefaultGenericFile> buildParents(int limit) {
280280
var segments = UriUtils.getPathSegments(rootUri, this.uri);
281281
var parents = new ArrayList<DefaultGenericFile>(Math.min(segments.size(), limit));
282282
for (int i = segments.size() - 1; i >= 1 && parents.size() < limit; i--) {
283-
var parentUri = UriUtils.resolvePath(rootUri, String.join("/", segments.subList(0, i)));
283+
var parentUri = UriUtils.resolvePath(rootUri, String.join("/", segments.subList(0, i)), true);
284284
var parent = new DefaultGenericFile();
285285
parent.storage = this.storage;
286286
parent.entryType = FileEntryType.DIRECTORY;

shellfx-storage/src/main/java/com/techsenger/shellfx/storage/UriUtils.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,15 @@ public static List<String> getPathSegments(URI baseUri, URI childUri) {
137137
* @param baseUri the base URI (e.g., {@code "gs://bucket/folder/"}). Must not be {@code null}.
138138
* @param path the path to resolve against the base URI (e.g., {@code "file.txt"}). Leading/trailing slashes are
139139
* handled gracefully. Must not be {@code null}.
140+
* @param isDirectory whether the resolved path denotes a directory; when {@code true}, a trailing slash is
141+
* appended (if not already present) so the result matches the URI form a storage would report for that same
142+
* directory once it exists (e.g. from a directory listing) &mdash; this matters when the caller resolves a
143+
* path before the entry is created, so there is nothing on disk yet to derive the trailing slash from.
140144
* @return A new URI with the resolved path.
141145
* @throws IllegalArgumentException If {@code baseUri} or {@code path} is {@code null}, or if the resolved URI
142146
* is invalid.
143147
*/
144-
public static URI resolvePath(URI baseUri, String path) {
148+
public static URI resolvePath(URI baseUri, String path, boolean isDirectory) {
145149
if (baseUri == null || path == null) {
146150
throw new IllegalArgumentException("URI and path must not be null");
147151
}
@@ -150,10 +154,22 @@ public static URI resolvePath(URI baseUri, String path) {
150154
basePath = "/";
151155
}
152156
String joinedPath = joinPaths(basePath, path);
157+
if (isDirectory && !joinedPath.endsWith("/")) {
158+
joinedPath = joinedPath + "/";
159+
}
160+
// URI#getAuthority() returns null both when the authority component is truly absent and when it is
161+
// present but empty (e.g. "file:///path"). Passing that null straight to the URI constructor below
162+
// would silently drop the "//" marker, turning "file:///path" into "file:/path" - a different URI
163+
// string for the same file, which then fails equals() against URIs produced elsewhere (e.g. by
164+
// listing the directory), even though both resolve to the same Path.
165+
var authority = baseUri.getAuthority();
166+
if (authority == null && baseUri.toString().startsWith(baseUri.getScheme() + "://")) {
167+
authority = "";
168+
}
153169
try {
154170
return new URI(
155171
baseUri.getScheme(),
156-
baseUri.getAuthority(),
172+
authority,
157173
joinedPath, //path passed to constructor can't be encoded
158174
baseUri.getQuery(),
159175
baseUri.getFragment()

shellfx-storage/src/test/java/com/techsenger/shellfx/storage/DefaultGenericFileTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ void getParent_immediateParent_returned() {
7171
var parent = child.getParent();
7272

7373
URI expectedUri = IS_WINDOWS
74-
? URI.create("file:///C:/home/user/foo")
75-
: URI.create("file:///home/user/foo");
74+
? URI.create("file:///C:/home/user/foo/")
75+
: URI.create("file:///home/user/foo/");
7676

7777
assertThat(parent.getUri()).isEqualTo(expectedUri);
7878
assertThat(parent.getName()).isEqualTo("foo");
@@ -103,14 +103,14 @@ void getParents_deeplyNestedFile_allParentsReturnedFromImmediateToRoot() {
103103
var parents = child.getParents();
104104

105105
URI fooUri = IS_WINDOWS
106-
? URI.create("file:///C:/home/user/foo")
107-
: URI.create("file:///home/user/foo");
106+
? URI.create("file:///C:/home/user/foo/")
107+
: URI.create("file:///home/user/foo/");
108108
URI userUri = IS_WINDOWS
109-
? URI.create("file:///C:/home/user")
110-
: URI.create("file:///home/user");
109+
? URI.create("file:///C:/home/user/")
110+
: URI.create("file:///home/user/");
111111
URI homeUri = IS_WINDOWS
112-
? URI.create("file:///C:/home")
113-
: URI.create("file:///home");
112+
? URI.create("file:///C:/home/")
113+
: URI.create("file:///home/");
114114

115115
assertThat(parents).hasSize(4);
116116
assertThat(parents.get(0).getUri()).isEqualTo(fooUri);

0 commit comments

Comments
 (0)