4

Why does the method relativize behave differently on and ?

Path path1 = Paths.get("/a/./b/../image.png");
Path path2 = Paths.get("/a/file.txt");
Path path = path1.relativize(path2);
System.out.println(path);

The JavaDoc description of both versions is equal. I feel the 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 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?

Boann
  • 48,794
  • 16
  • 117
  • 146
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183

2 Answers2

6

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.

Hitobat
  • 2,847
  • 1
  • 16
  • 12
  • Interestingly this was already reported in 2010 as [JDK-6925169](https://bugs.openjdk.java.net/browse/JDK-6925169), but was apparently never fixed (and only resolved as "Duplicate" in 2018). – Marcono1234 Sep 13 '20 at 16:23
2

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 is has additional code including normalization compared to .

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 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());
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183