Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Path.relativize behave differently on Java 8 and Java 11?

Why does the method relativize behave differently on java-8 and java-11?

Path path1 = Paths.get("/a/./b/../image.png");
Path path2 = Paths.get("/a/file.txt");
Path path = path1.relativize(path2);
System.out.println(path);
  • java-8 (1.8.0_66 to be exact) prints ../../../../file.txt. JavaDoc.
  • java-11 (11.0.4 to be exact) prints ../file.txt. JavaDoc.

The JavaDoc description of both versions is equal. I feel the java-11 way looks like a correct behavior to me:

  • path1: /a/./b/../image.png normalizes to /a/b/../image.png which normalizes to /a/image.png
  • path2: /a/file.txt
  • the way to navigate from /a/image.png and /a/file.txt is ../file.txt

Questions

  1. How is the java-8 way supposed to be calculated? Doesn't it normalize the path? I don't understand how to get the result from head.

  2. Why is there a difference between these two versions that is not documented at all?

like image 494
Nikolas Charalambidis Avatar asked Oct 19 '25 22:10

Nikolas Charalambidis


2 Answers

Maybe this bug will answer your question: https://bugs.openjdk.java.net/browse/JDK-8066943

This affected relativizing paths containing . or .. and was fixed for Java 9. So that's why you see a difference between 8 and 11.

like image 179
Hitobat Avatar answered Oct 21 '25 13:10

Hitobat


Windows based source-code answer here.

From the observation of the source codes (let's take a look at sun.nio.fs.WindowsPath, one of the implementations of Path) in java-11 is has additional code including normalization compared to java-8.

  • java-8 sun.nio.fs.WindowsPath source code at GitHub
  • java-11 sun.nio.fs.WindowsPath source code at GitHub

The key line of the latter implementation starts at the line 411, so basically, the latter implementation normalizes the paths before taking into calculation of the relative path:

WindowsPath base = this;
if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) {
    base = base.normalize();
    child = child.normalize();
}

Digging further, the implementation changes between jdk8-b120 (source) and jdk-9+95 (source). Since the modular system was introduced, both the classes implementation and location differ:

  • Java 8 and below: /jdk/src/windows/classes/sun/nio/fs/WindowsPath.java
  • Java 9 and above: /jdk/src/java.base/windows/classes/sun/nio/fs/WindowsPath.java

How is the java-8 way supposed to be calculated? Doesn't it normalize the path? I don't understand how to get the result from head.

The most straightforward way to go is to normalize both paths first before relativizing them. But I have no idea whether it completely covers all the java.nio.file.Path implementations and is safe to do so.

Path path = path1.normalize().relativize(path2.normalize());
like image 20
Nikolas Charalambidis Avatar answered Oct 21 '25 11:10

Nikolas Charalambidis