-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
59 changed files
with
1,649 additions
and
1,763 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
loader/src/main/java/cpw/mods/jarhandling/CompositeModContainer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package cpw.mods.jarhandling; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.net.URI; | ||
import java.net.URL; | ||
import java.nio.file.Path; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.jar.Manifest; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
public final class CompositeModContainer implements JarContents { | ||
private final JarContents[] delegates; | ||
@Nullable | ||
private final PathFilter[] filters; | ||
|
||
public CompositeModContainer(List<JarContents> delegates) { | ||
this(delegates, null); | ||
} | ||
|
||
public CompositeModContainer(List<JarContents> delegates, @Nullable List<PathFilter> filters) { | ||
this.delegates = delegates.toArray(JarContents[]::new); | ||
this.filters = filters != null ? filters.toArray(PathFilter[]::new) : null; | ||
if (delegates.isEmpty()) { | ||
throw new IllegalArgumentException("Cannot construct an empty mod container"); | ||
} | ||
if (filters != null && delegates.size() != filters.size()) { | ||
throw new IllegalArgumentException("The number of delegates and filters must match."); | ||
} | ||
} | ||
|
||
@Override | ||
public Path getPrimaryPath() { | ||
return delegates[0].getPrimaryPath(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "[" + Arrays.stream(delegates).map(Object::toString).collect(Collectors.joining(", ")) + "]"; | ||
} | ||
|
||
@Override | ||
public Optional<URI> findFile(String relativePath) { | ||
for (var delegate : delegates) { | ||
var result = delegate.findFile(relativePath); | ||
if (result.isPresent()) { | ||
return result; | ||
} | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public Manifest getJarManifest() { | ||
for (var delegate : delegates) { | ||
var manifest = delegate.getJarManifest(); | ||
if (manifest != null) { | ||
return manifest; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public InputStream openFile(String relativePath) throws IOException { | ||
for (var delegate : delegates) { | ||
var stream = delegate.openFile(relativePath); | ||
if (stream != null) { | ||
return stream; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public byte[] readFile(String relativePath) throws IOException { | ||
for (var delegate : delegates) { | ||
var content = delegate.readFile(relativePath); | ||
if (content != null) { | ||
return content; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public boolean hasContentRoot(Path path) { | ||
for (var delegate : delegates) { | ||
if (delegate.hasContentRoot(path)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public Stream<URL> getClasspathRoots() { | ||
return Arrays.stream(delegates).flatMap(JarContents::getClasspathRoots); | ||
} | ||
|
||
@Override | ||
public void visitContent(ModContentVisitor visitor) { | ||
// This is based on the logic that openResource will return the file from the *first* delegate | ||
// Every relative path we return will not be returned again | ||
var distinctVisitor = new ModContentVisitor() { | ||
final Set<String> pathsVisited = new HashSet<>(); | ||
|
||
@Override | ||
public void visit(String relativePath, IOSupplier<InputStream> contentSupplier, IOSupplier<ModContentAttributes> attributesSupplier) { | ||
if (pathsVisited.add(relativePath)) { | ||
visitor.visit(relativePath, contentSupplier, attributesSupplier); | ||
} | ||
} | ||
}; | ||
|
||
for (var delegate : delegates) { | ||
delegate.visitContent(distinctVisitor); | ||
} | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
IOException error = null; | ||
for (var delegate : delegates) { | ||
try { | ||
delegate.close(); | ||
} catch (IOException e) { | ||
if (error == null) { | ||
error = new IOException("Failed to close one ore more delegates of " + this); | ||
} | ||
error.addSuppressed(e); | ||
} | ||
} | ||
if (error != null) { | ||
throw error; | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
loader/src/main/java/cpw/mods/jarhandling/EmptyModContainer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package cpw.mods.jarhandling; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.net.URI; | ||
import java.net.URL; | ||
import java.nio.file.Path; | ||
import java.util.Optional; | ||
import java.util.stream.Stream; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
public final class EmptyModContainer implements JarContents { | ||
private final Path path; | ||
|
||
public EmptyModContainer(Path path) { | ||
this.path = path; | ||
} | ||
|
||
@Override | ||
public Optional<URI> findFile(String relativePath) { | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Path getPrimaryPath() { | ||
return path; | ||
} | ||
|
||
@Override | ||
public @Nullable InputStream openFile(String relativePath) throws IOException { | ||
return null; | ||
} | ||
|
||
@Override | ||
public byte @Nullable [] readFile(String relativePath) throws IOException { | ||
return null; | ||
} | ||
|
||
@Override | ||
public boolean hasContentRoot(Path path) { | ||
return false; | ||
} | ||
|
||
@Override | ||
public Stream<URL> getClasspathRoots() { | ||
return Stream.empty(); | ||
} | ||
|
||
@Override | ||
public void visitContent(ModContentVisitor visitor) {} | ||
|
||
@Override | ||
public void close() throws IOException {} | ||
} |
94 changes: 94 additions & 0 deletions
94
loader/src/main/java/cpw/mods/jarhandling/FolderModContainer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package cpw.mods.jarhandling; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.UncheckedIOException; | ||
import java.net.MalformedURLException; | ||
import java.net.URI; | ||
import java.net.URL; | ||
import java.nio.file.Files; | ||
import java.nio.file.NoSuchFileException; | ||
import java.nio.file.Path; | ||
import java.nio.file.attribute.BasicFileAttributeView; | ||
import java.util.Optional; | ||
import java.util.stream.Stream; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public record FolderModContainer(Path path) implements JarContents { | ||
private static final Logger LOG = LoggerFactory.getLogger(FolderModContainer.class); | ||
|
||
@Override | ||
public Path getPrimaryPath() { | ||
return path; | ||
} | ||
|
||
@Override | ||
public InputStream openFile(String relativePath) throws IOException { | ||
try { | ||
return Files.newInputStream(path.resolve(relativePath)); | ||
} catch (NoSuchFileException e) { | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
public byte[] readFile(String relativePath) throws IOException { | ||
try { | ||
return Files.readAllBytes(path.resolve(relativePath)); | ||
} catch (NoSuchFileException e) { | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
public boolean hasContentRoot(Path path) { | ||
return this.path.equals(path); | ||
} | ||
|
||
@Override | ||
public Stream<URL> getClasspathRoots() { | ||
try { | ||
return Stream.of(path.toUri().toURL()); | ||
} catch (MalformedURLException e) { | ||
LOG.error("Failed to convert path to URL: {}", path, e); | ||
return Stream.of(); | ||
} | ||
} | ||
|
||
@Override | ||
public void visitContent(ModContentVisitor visitor) { | ||
var startingPoint = path; | ||
try (var stream = Files.walk(startingPoint)) { | ||
stream.forEach(path -> { | ||
var file = path.toFile(); | ||
if (file.isFile()) { | ||
var relativePath = PathNormalization.normalize(startingPoint.relativize(path).toString()); | ||
visitor.visit(relativePath, () -> Files.newInputStream(path), () -> { | ||
var attributes = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes(); | ||
return new ModContentAttributes(attributes.lastModifiedTime(), attributes.size()); | ||
}); | ||
} | ||
}); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException("Failed to walk contents of " + path, e); | ||
} | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return path.toString(); | ||
} | ||
|
||
@Override | ||
public Optional<URI> findFile(String relativePath) { | ||
if (relativePath.startsWith("/")) { | ||
throw new IllegalArgumentException("Must be a relative path: " + relativePath); | ||
} | ||
var pathToFile = path.resolve(relativePath); | ||
return pathToFile.toFile().isFile() ? Optional.of(pathToFile.toUri()) : Optional.empty(); | ||
} | ||
|
||
@Override | ||
public void close() {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package cpw.mods.jarhandling; | ||
|
||
import java.io.IOException; | ||
|
||
@FunctionalInterface | ||
public interface IOSupplier<T> { | ||
T get() throws IOException; | ||
} |
Oops, something went wrong.