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 * @param base Base directory
39 * @param target Target file
40 */
41 public Relative(final File base, final File target) {
42 this.base = base;
43 this.target = target;
44 }
45
46 /**
47 * Path of the target file relative to the base directory.
48 * @return Relative path starting with a forward slash, or the
49 * absolute path of the file if it is not under the base directory
50 */
51 public String path() {
52 final Path root = this.base.toPath().toAbsolutePath().normalize();
53 final Path file = this.target.toPath().toAbsolutePath().normalize();
54 final String name;
55 if (file.startsWith(root)) {
56 name = "/".concat(
57 root.relativize(file).toString().replace(File.separatorChar, '/')
58 );
59 } else {
60 name = file.toString().replace(File.separatorChar, '/');
61 }
62 return name;
63 }
64 }