1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3 * SPDX-License-Identifier: MIT
4 */
5 package com.qulice.spi;
6
7 import java.io.File;
8 import java.nio.file.Path;
9
10 /**
11 * Path of a file relative to a base directory.
12 *
13 * <p>Given a base directory and a target file, computes the file path
14 * relative to the base, prefixed with a forward slash and using forward
15 * slashes as separators. Uses {@link Path#relativize(Path)} after
16 * normalising both paths to absolute form, which correctly handles cases
17 * where the file and base dir share a canonical location but differ in
18 * string form (for example, on macOS the {@code /var} symlink to
19 * {@code /private/var}). When the file lies outside the base directory,
20 * returns the file's absolute path unchanged.</p>
21 *
22 * @since 0.24
23 */
24 public final class Relative {
25
26 /**
27 * Base directory.
28 */
29 private final File base;
30
31 /**
32 * Target file.
33 */
34 private final File target;
35
36 /**
37 * Ctor.
38 *
39 * @param base Base directory
40 * @param target Target file
41 */
42 public Relative(final File base, final File target) {
43 this.base = base;
44 this.target = target;
45 }
46
47 /**
48 * Path of the target file relative to the base directory.
49 *
50 * @return Relative path starting with a forward slash, or the
51 * absolute path of the file if it is not under the base directory
52 */
53 public String path() {
54 final Path root = this.base.toPath().toAbsolutePath().normalize();
55 final Path file = this.target.toPath().toAbsolutePath().normalize();
56 final String name;
57 if (file.startsWith(root)) {
58 name = "/".concat(
59 root.relativize(file).toString().replace(File.separatorChar, '/')
60 );
61 } else {
62 name = file.toString().replace(File.separatorChar, '/');
63 }
64 return name;
65 }
66 }