-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Hoist some ArchiveMount logic into a new superclass
- Add AbstractInMemoryMount, which contains all of ArchiveMount's file tree logic, but not the caching functionality. - Convert MemoryMount to inherit from AbstractInMemoryMount. - Add a helper method to add a file to an AbstractInMemoryMount, and use that within {Resource,Jar}Mount. There's definitely more work to be done here - it might be nice to split FileEntry into separate Directory and File interfaces, or at least make them slightly more immutable, but that's definitely a future job.
- Loading branch information
Showing
7 changed files
with
235 additions
and
287 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
152 changes: 152 additions & 0 deletions
152
projects/core/src/main/java/dan200/computercraft/core/filesystem/AbstractInMemoryMount.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,152 @@ | ||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers | ||
// | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package dan200.computercraft.core.filesystem; | ||
|
||
import dan200.computercraft.api.filesystem.FileAttributes; | ||
import dan200.computercraft.api.filesystem.FileOperationException; | ||
import dan200.computercraft.api.filesystem.Mount; | ||
|
||
import javax.annotation.Nullable; | ||
import java.io.IOException; | ||
import java.nio.channels.SeekableByteChannel; | ||
import java.nio.file.attribute.BasicFileAttributes; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* An abstract mount which stores its file tree in memory. | ||
* | ||
* @param <T> The type of file. | ||
*/ | ||
public abstract class AbstractInMemoryMount<T extends AbstractInMemoryMount.FileEntry<T>> implements Mount { | ||
protected static final String NO_SUCH_FILE = "No such file"; | ||
|
||
@Nullable | ||
protected T root; | ||
|
||
private @Nullable T get(String path) { | ||
var lastEntry = root; | ||
var lastIndex = 0; | ||
|
||
while (lastEntry != null && lastIndex < path.length()) { | ||
var nextIndex = path.indexOf('/', lastIndex); | ||
if (nextIndex < 0) nextIndex = path.length(); | ||
|
||
lastEntry = lastEntry.children == null ? null : lastEntry.children.get(path.substring(lastIndex, nextIndex)); | ||
lastIndex = nextIndex + 1; | ||
} | ||
|
||
return lastEntry; | ||
} | ||
|
||
@Override | ||
public final boolean exists(String path) { | ||
return get(path) != null; | ||
} | ||
|
||
@Override | ||
public final boolean isDirectory(String path) { | ||
var file = get(path); | ||
return file != null && file.isDirectory(); | ||
} | ||
|
||
@Override | ||
public final void list(String path, List<String> contents) throws IOException { | ||
var file = get(path); | ||
if (file == null || !file.isDirectory()) throw new FileOperationException(path, "Not a directory"); | ||
|
||
file.list(contents); | ||
} | ||
|
||
@Override | ||
public final long getSize(String path) throws IOException { | ||
var file = get(path); | ||
if (file == null) throw new FileOperationException(path, NO_SUCH_FILE); | ||
return getSize(file); | ||
} | ||
|
||
/** | ||
* Get the size of a file. | ||
* | ||
* @param file The file to get the size of. | ||
* @return The size of the file. This should be 0 for directories, and equal to {@code openForRead(_).size()} for files. | ||
* @throws IOException If the size could not be read. | ||
*/ | ||
protected abstract long getSize(T file) throws IOException; | ||
|
||
@Override | ||
public final SeekableByteChannel openForRead(String path) throws IOException { | ||
var file = get(path); | ||
if (file == null || file.isDirectory()) throw new FileOperationException(path, NO_SUCH_FILE); | ||
return openForRead(file); | ||
} | ||
|
||
/** | ||
* Open a file for reading. | ||
* | ||
* @param file The file to read. This will not be a directory. | ||
* @return The channel for this file. | ||
*/ | ||
protected abstract SeekableByteChannel openForRead(T file) throws IOException; | ||
|
||
@Override | ||
public final BasicFileAttributes getAttributes(String path) throws IOException { | ||
var file = get(path); | ||
if (file == null) throw new FileOperationException(path, NO_SUCH_FILE); | ||
return getAttributes(file); | ||
} | ||
|
||
/** | ||
* Get all attributes of the file. | ||
* | ||
* @param file The file to compute attributes for. | ||
* @return The file's attributes. | ||
* @throws IOException If the attributes could not be read. | ||
*/ | ||
protected BasicFileAttributes getAttributes(T file) throws IOException { | ||
return new FileAttributes(file.isDirectory(), getSize(file)); | ||
} | ||
|
||
protected T getOrCreateChild(T lastEntry, String localPath, Function<String, T> factory) { | ||
var lastIndex = 0; | ||
while (lastIndex < localPath.length()) { | ||
var nextIndex = localPath.indexOf('/', lastIndex); | ||
if (nextIndex < 0) nextIndex = localPath.length(); | ||
|
||
var part = localPath.substring(lastIndex, nextIndex); | ||
if (lastEntry.children == null) lastEntry.children = new HashMap<>(0); | ||
|
||
var nextEntry = lastEntry.children.get(part); | ||
if (nextEntry == null || !nextEntry.isDirectory()) { | ||
lastEntry.children.put(part, nextEntry = factory.apply(localPath.substring(0, nextIndex))); | ||
} | ||
|
||
lastEntry = nextEntry; | ||
lastIndex = nextIndex + 1; | ||
} | ||
|
||
return lastEntry; | ||
} | ||
|
||
protected static class FileEntry<T extends FileEntry<T>> { | ||
public final String path; | ||
@Nullable | ||
public Map<String, T> children; | ||
|
||
protected FileEntry(String path) { | ||
this.path = path; | ||
} | ||
|
||
public boolean isDirectory() { | ||
return children != null; | ||
} | ||
|
||
protected void list(List<String> contents) { | ||
if (children != null) contents.addAll(children.keySet()); | ||
} | ||
} | ||
} |
Oops, something went wrong.