Skip to content
4 changes: 2 additions & 2 deletions src/main/java/cpw/mods/jarhandling/impl/JarContentsImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private Map<Path, Integer> readMultiReleaseInfo() {
return Map.of();
}

var vers = filesystem.getRoot().resolve("META-INF/versions");
var vers = filesystem.getPath("").resolve("META-INF/versions");
try (var walk = Files.walk(vers)) {
Map<Path, Integer> pathToJavaVersion = new HashMap<>();
walk
Expand Down Expand Up @@ -161,7 +161,7 @@ public Set<String> getPackagesExcluding(String... excludedRootPackages) {

Set<String> packages = new HashSet<>();
try {
Files.walkFileTree(this.filesystem.getRoot(), new SimpleFileVisitor<>() {
Files.walkFileTree(this.filesystem.getPath(""), new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.getFileName().toString().endsWith(".class") && attrs.isRegularFile()) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/cpw/mods/niofs/union/UnionFileSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ public DirectoryStream<Path> newDirStream(final UnionPath path, final DirectoryS
closeables.add(ds);
final var currentPaths = StreamSupport.stream(ds.spliterator(), false)
.filter(p -> testFilter(p, bp))
.map(other -> fastPath(isSimple ? other : bp.relativize(other)));
.map(other -> fastPath(isSimple ? other : bp.toAbsolutePath().relativize(other.toAbsolutePath())));
stream = Stream.concat(stream, currentPaths);
}
final Stream<Path> realStream = stream.distinct();
Expand Down Expand Up @@ -452,7 +452,7 @@ private boolean testFilter(final Path path, final Path basePath) {

var sPath = path.toString();
if (path.getFileSystem() == basePath.getFileSystem()) // Directories, zips will be different file systems.
sPath = basePath.relativize(path).toString().replace('\\', '/');
sPath = basePath.toAbsolutePath().relativize(path.toAbsolutePath()).toString().replace('\\', '/');
if (Files.isDirectory(path))
sPath += '/';
if (sPath.length() > 1 && sPath.startsWith("/"))
Expand Down
69 changes: 39 additions & 30 deletions src/main/java/cpw/mods/niofs/union/UnionPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
import java.util.Deque;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.function.IntBinaryOperator;
import java.util.stream.IntStream;

public class UnionPath implements Path {
private final UnionFileSystem fileSystem;
private final boolean absolute;
private final boolean empty;
private final String[] pathParts;

// Store the normalized path after it has been created first
Expand All @@ -31,7 +31,8 @@ public class UnionPath implements Path {
this.fileSystem = fileSystem;
if (pathParts.length == 0) {
this.absolute = false;
this.pathParts = new String[0];
this.pathParts = new String[]{ "" };
this.empty = true;
} else {
StringBuilder joiner = new StringBuilder();
for (int i = 0; i < pathParts.length; i++) {
Expand All @@ -43,7 +44,8 @@ public class UnionPath implements Path {
}
final var longstring = joiner.toString();
this.absolute = longstring.startsWith(UnionFileSystem.SEP_STRING);
this.pathParts = getPathParts(longstring);
this.pathParts = getPathParts(this.absolute, longstring);
this.empty = !this.absolute && this.pathParts.length == 1 && this.pathParts[0].isEmpty();
}
this.normalized = null;
}
Expand All @@ -55,15 +57,22 @@ public class UnionPath implements Path {

private UnionPath(final UnionFileSystem fileSystem, boolean absolute, boolean isNormalized, final String... pathParts) {
this.fileSystem = fileSystem;
this.absolute = absolute;
this.pathParts = pathParts;
if (isNormalized)
this.normalized = this;
else
this.normalized = null;
if (!absolute && (pathParts.length == 0 || (pathParts.length == 1 && pathParts[0].isEmpty()))) {
this.absolute = false;
this.pathParts = new String[]{ "" };
this.empty = true;
} else {
this.absolute = absolute;
this.empty = false;
this.pathParts = pathParts;
if (isNormalized)
this.normalized = this;
else
this.normalized = null;
}
}

private String[] getPathParts(final String longstring) {
private String[] getPathParts(final boolean isAbsolute, final String longstring) {
var clean = longstring.replace('\\', '/');
int startIndex = 0;
List<String> parts = new ArrayList<>();
Expand All @@ -79,7 +88,11 @@ private String[] getPathParts(final String longstring) {
}
startIndex = (index + 1);
}
return parts.toArray(String[]::new);
if (parts.isEmpty() && !isAbsolute) {
return new String[]{ "" };
} else {
return parts.toArray(String[]::new);
}
}

@Override
Expand All @@ -94,28 +107,25 @@ public boolean isAbsolute() {

@Override
public Path getRoot() {
// Found nothing in the docs that say a non-absolute path can't have a root
// although this is uncommon. However, other stuff relies on it so leave it
//if (!this.absolute)
// return null;
if (!this.absolute)
return null;
return this.fileSystem.getRoot();
}

@Override
public Path getFileName() {
if (this.pathParts.length > 0) {
if (this.empty) {
return null;
} else if (this.pathParts.length > 0) {
return new UnionPath(this.getFileSystem(), false, this.pathParts[this.pathParts.length - 1]);
} else {
// normally would be null for the empty absolute path and empty string for the empty relative
// path. But again, very much stuff relies on it and there's no current directory for union
// paths, so it does not really matter.
return new UnionPath(this.fileSystem, false);
return this.absolute ? null : new UnionPath(this.fileSystem, false);
}
}

@Override
public Path getParent() {
if (this.pathParts.length > 0) {
if (this.pathParts.length > 1 || (this.absolute && this.pathParts.length == 1)) {
return new UnionPath(this.fileSystem, this.absolute, Arrays.copyOf(this.pathParts,this.pathParts.length - 1));
} else {
return null;
Expand Down Expand Up @@ -193,10 +203,10 @@ public Path normalize() {
case ".":
break;
case "..":
if (normpath.isEmpty() || normpath.getLast().equals("..")) {
// .. on an empty path is allowed, so keep it
if (!this.absolute && (normpath.isEmpty() || normpath.getLast().equals(".."))) {
// .. on an empty path is allowed as long as it is not absolute, so keep it
normpath.addLast(pathPart);
} else {
} else if (!normpath.isEmpty()) {
normpath.removeLast();
}
break;
Expand All @@ -212,9 +222,12 @@ public Path normalize() {
@Override
public Path resolve(final Path other) {
if (other instanceof UnionPath path) {
if (path.isAbsolute()) {
if (path.isAbsolute() || this.empty) {
return path;
}
if (path.empty) {
return this;
}
String[] mergedParts = new String[this.pathParts.length + path.pathParts.length];
System.arraycopy(this.pathParts, 0, mergedParts, 0, this.pathParts.length);
System.arraycopy(path.pathParts, 0, mergedParts, this.pathParts.length, path.pathParts.length);
Expand All @@ -228,11 +241,7 @@ public Path relativize(final Path other) {
if (other.getFileSystem()!=this.getFileSystem()) throw new IllegalArgumentException("Wrong filesystem");
if (other instanceof UnionPath p) {
if (this.absolute != p.absolute) {
// Should not be allowed but union fs relies on it
// also there is no such concept of a current directory for union paths
// meaning absolute and relative paths should have the same effect,
// so we just allow this.
//throw new IllegalArgumentException("Different types of path");
throw new IllegalArgumentException("Different types of path");
}
var length = Math.min(this.pathParts.length, p.pathParts.length);
int i = 0;
Expand Down
56 changes: 3 additions & 53 deletions src/test/java/cpw/mods/niofs/union/TestUnionFS.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

public class TestUnionFS {

private static final UnionFileSystemProvider UFSP = (UnionFileSystemProvider) FileSystemProvider.installedProviders().stream().filter(fsp->fsp.getScheme().equals("union")).findFirst().orElseThrow(()->new IllegalStateException("Couldn't find UnionFileSystemProvider"));

@Test
void testUnionFileSystem() throws IOException {
final var dir1 = Paths.get("src", "test", "resources", "dir1").toAbsolutePath().normalize();
Expand Down Expand Up @@ -72,65 +74,13 @@ void testUnionFileSystemJar() throws Throwable {
var doexist = List.of("cpw/mods/niofs/union/UnionPath.class", "net/minecraftforge/client/event/GuiOpenEvent.class", "cpw/mods/modlauncher/Launcher.class"); //jar 3
var dontexist = List.of("cpw/mods/modlauncher/api/NoIDontExist.class", "net/minecraftforge/client/nonexistent/Nope.class", "Missing.class");
assertAll(
doexist.stream().map(ufs::getPath).map(p->()->assertTrue(Files.exists(p)))
doexist.stream().map(ufs::getPath).map(p->()->assertTrue(Files.exists(p)))
);
assertAll(
dontexist.stream().map(ufs::getPath).map(p->()->assertTrue(Files.notExists(p)))
);
}

@Test
void testRelativize() {
final var dir1 = Paths.get("src", "test", "resources", "dir1").toAbsolutePath().normalize();
final var dir2 = Paths.get("src", "test", "resources", "dir2").toAbsolutePath().normalize();

var fsp = (UnionFileSystemProvider)FileSystemProvider.installedProviders().stream().filter(fs-> fs.getScheme().equals("union")).findFirst().orElseThrow();
var ufs = fsp.newFileSystem((path, base) -> true, dir1, dir2);
var p1 = ufs.getPath("path1");
var p123 = ufs.getPath("path1/path2/path3");
var p11 = ufs.getPath("path1/path1");
var p12 = ufs.getPath("path1/path2");
var p13 = ufs.getPath("path1/path3");
var p23 = ufs.getPath("path2/path3");
var p13plus = ufs.getPath("path1/path3");
assertAll(
()->assertEquals("path2/path3", p1.relativize(p123).toString()),
()->assertEquals("../..", p123.relativize(p1).toString()),
()->assertEquals("path1", p1.relativize(p11).toString()),
()->assertEquals("path2", p1.relativize(p12).toString()),
()->assertEquals("path3", p1.relativize(p13).toString()),
()->assertEquals("../../path1/path1", p23.relativize(p11).toString()),
()->assertEquals("../../path1", p123.relativize(p11).toString()),
()->assertEquals(0, p13.relativize(p13plus).getNameCount())
);
}

@Test
void testRelativizeAbsolute() {
final var dir1 = Paths.get("src", "test", "resources", "dir1").toAbsolutePath().normalize();
final var dir2 = Paths.get("src", "test", "resources", "dir2").toAbsolutePath().normalize();

var fsp = (UnionFileSystemProvider)FileSystemProvider.installedProviders().stream().filter(fs-> fs.getScheme().equals("union")).findFirst().orElseThrow();
var ufs = fsp.newFileSystem((path, base) -> true, dir1, dir2);
var p1 = ufs.getPath("/path1");
var p123 = ufs.getPath("/path1/path2/path3");
var p11 = ufs.getPath("/path1/path1");
var p12 = ufs.getPath("/path1/path2");
var p13 = ufs.getPath("/path1/path3");
var p23 = ufs.getPath("/path2/path3");
var p13plus = ufs.getPath("/path1/path3");
assertAll(
()->assertEquals("path2/path3", p1.relativize(p123).toString()),
()->assertEquals("../..", p123.relativize(p1).toString()),
()->assertEquals("path1", p1.relativize(p11).toString()),
()->assertEquals("path2", p1.relativize(p12).toString()),
()->assertEquals("path3", p1.relativize(p13).toString()),
()->assertEquals("../../path1/path1", p23.relativize(p11).toString()),
()->assertEquals("../../path1", p123.relativize(p11).toString()),
()->assertEquals(0, p13.relativize(p13plus).getNameCount())
);
}

@Test
void testPathFiltering() {
final var dir1 = Paths.get("src", "test", "resources", "dir1").toAbsolutePath().normalize();
Expand Down
Loading