Skip to content

Commit 6b6b90c

Browse files
committed
Initial working implementation.
Several nice to haves are still missing. But I will add those later.
1 parent f81a31a commit 6b6b90c

14 files changed

Lines changed: 1394 additions & 6 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package cpw.mods.niofs.layzip;
2+
3+
import cpw.mods.niofs.pathfs.PathFileSystemProvider;
4+
import cpw.mods.niofs.pathfs.PathPath;
5+
6+
import java.io.IOException;
7+
import java.io.UncheckedIOException;
8+
import java.net.URI;
9+
import java.net.URISyntaxException;
10+
import java.nio.file.FileSystem;
11+
import java.nio.file.FileSystems;
12+
import java.nio.file.Path;
13+
import java.util.Locale;
14+
import java.util.Map;
15+
16+
public class LayeredZipFileSystemProvider extends PathFileSystemProvider
17+
{
18+
public static final String SCHEME = "jij";
19+
public static final String INDICATOR = "!";
20+
public static final String SEPARATOR = INDICATOR + "/";
21+
22+
23+
@Override
24+
public String getScheme() {
25+
return SCHEME;
26+
}
27+
28+
@Override
29+
public FileSystem newFileSystem(final URI uri, final Map<String, ?> env) throws IOException
30+
{
31+
final String[] sections = uri.getRawSchemeSpecificPart().split(SEPARATOR);
32+
FileSystem workingSystem = FileSystems.getDefault(); //Grab the normal disk FS.
33+
34+
String keyPrefix = "";
35+
36+
if (sections.length > 1) {
37+
for (int i = 0; i < sections.length - 1; i++)
38+
{
39+
String section = sections[i];
40+
if (section.startsWith("//"))
41+
section = section.substring(2);
42+
43+
section = handleAbsolutePrefixOnWindows(workingSystem, section);
44+
final Path path = workingSystem.getPath(section).toAbsolutePath();
45+
workingSystem = getOrCreateNewSystem(keyPrefix, path);
46+
keyPrefix += path.toString().replace("\\", "/") + INDICATOR;
47+
}
48+
}
49+
50+
String lastSection = sections[sections.length - 1];
51+
if (lastSection.startsWith("//"))
52+
lastSection = lastSection.substring(2);
53+
54+
55+
final Path lastPath = workingSystem.getPath(lastSection).toAbsolutePath();
56+
return getOrCreateNewSystem(keyPrefix, lastPath);
57+
}
58+
59+
private String handleAbsolutePrefixOnWindows(final FileSystem workingSystem, String section)
60+
{
61+
if (workingSystem.getClass().getName().toLowerCase(Locale.ROOT).contains("windows")) {
62+
//This special casing is needed, since else the rooted paths crash on Windows system because:
63+
// /D:/something is not a valid path on Windows.
64+
//However, the JDK does not expose the Windows FS types and there are no marker classes, so we use the classname.
65+
//Because we are fancy like that.
66+
if (section.startsWith("/"))
67+
section = section.substring(1); //Turns /D:/something into D:/Something which is a valid windows path.
68+
}
69+
return section;
70+
}
71+
72+
private FileSystem getOrCreateNewSystem(final Path path)
73+
{
74+
return getOrCreateNewSystem("", path);
75+
}
76+
77+
private FileSystem getOrCreateNewSystem(String keyPrefix, final Path path)
78+
{
79+
final Map<String, ?> args = Map.of("packagePath", path.toAbsolutePath());
80+
try
81+
{
82+
return super.newFileSystem(new URI(super.getScheme() + ":" + keyPrefix + path.toString().replace("\\", "/")), args);
83+
}
84+
catch (Exception e)
85+
{
86+
throw new UncheckedIOException("Failed to create intermediary FS.", new IOException("Failed to process data.", e));
87+
}
88+
}
89+
90+
@Override
91+
public Path getPath(final URI uri)
92+
{
93+
final String[] sections = uri.getRawSchemeSpecificPart().split("~");
94+
FileSystem workingSystem = FileSystems.getDefault(); //Grab the normal disk FS.
95+
if (sections.length > 1) {
96+
for (int i = 0; i < sections.length - 1; i++)
97+
{
98+
final String section = sections[i];
99+
final Path path = workingSystem.getPath(section);
100+
workingSystem = getOrCreateNewSystem(path);
101+
}
102+
}
103+
104+
final String lastSection = sections[sections.length - 1];
105+
return workingSystem.getPath(lastSection);
106+
}
107+
108+
@Override
109+
public FileSystem getFileSystem(final URI uri)
110+
{
111+
final String[] sections = uri.getRawSchemeSpecificPart().split("~");
112+
FileSystem workingSystem = FileSystems.getDefault(); //Grab the normal disk FS.
113+
if (sections.length > 1) {
114+
for (int i = 0; i < sections.length - 1; i++)
115+
{
116+
final String section = sections[i];
117+
final Path path = workingSystem.getPath(section);
118+
workingSystem = getOrCreateNewSystem(path);
119+
}
120+
}
121+
122+
final String lastSection = sections[sections.length - 1];
123+
final Path lastPath = workingSystem.getPath(lastSection);
124+
return getOrCreateNewSystem(lastPath);
125+
}
126+
127+
@Override
128+
protected URI buildUriFor(final PathPath path) throws URISyntaxException, IllegalArgumentException
129+
{
130+
String prefix = "";
131+
132+
final URI outerUri = path.getFileSystem().getTarget().toUri();
133+
prefix = outerUri.getRawSchemeSpecificPart() + SEPARATOR;
134+
135+
return URI.create("%s:%s%s".formatted(SCHEME, prefix, path).replace("%s/".formatted(SEPARATOR), SEPARATOR));
136+
}
137+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package cpw.mods.niofs.pathfs;
2+
3+
import java.io.IOException;
4+
import java.nio.file.DirectoryStream;
5+
import java.nio.file.Path;
6+
import java.util.Iterator;
7+
import java.util.function.Function;
8+
9+
class PathFSUtils
10+
{
11+
12+
private PathFSUtils()
13+
{
14+
throw new IllegalStateException("Can not instantiate an instance of: PathFSUtils. This is a utility class");
15+
}
16+
17+
public static final DirectoryStream<Path> NULL_STREAM = new DirectoryStream<>()
18+
{
19+
@Override
20+
public Iterator<Path> iterator()
21+
{
22+
return new Iterator<>()
23+
{
24+
@Override
25+
public boolean hasNext()
26+
{
27+
return false;
28+
}
29+
30+
@Override
31+
public Path next()
32+
{
33+
return null;
34+
}
35+
};
36+
}
37+
38+
@Override
39+
public void close() throws IOException
40+
{
41+
42+
}
43+
};
44+
45+
public static DirectoryStream<Path> adapt(final DirectoryStream<Path> inner, final Function<Path, Path> adapter) {
46+
return new DirectoryStream<Path>() {
47+
@Override
48+
public Iterator<Path> iterator()
49+
{
50+
final Iterator<Path> targetIterator = inner.iterator();
51+
52+
return new Iterator<Path>() {
53+
@Override
54+
public boolean hasNext()
55+
{
56+
return targetIterator.hasNext();
57+
}
58+
59+
@Override
60+
public Path next()
61+
{
62+
final Path targetPath = targetIterator.next();
63+
return adapter.apply(targetPath);
64+
}
65+
};
66+
}
67+
68+
@Override
69+
public void close() throws IOException
70+
{
71+
inner.close();
72+
}
73+
};
74+
}
75+
}

0 commit comments

Comments
 (0)