semi-auto cleanup

This commit is contained in:
gnembon committed 2023-02-04 13:26:21 +01:00
1 parent cad80f82c6
commit 8f61271606
99 files changed
+11463 -8007

No files matched your search

@@ -59,14 +59,19 @@ public class FileArgument
private FileSystem zfs;
private Path zipPath;
public final static Object writeIOSync = new Object();
public static final Object writeIOSync = new Object();
public void close() {
if (zfs != null && zfs.isOpen()) {
try {
public void close()
{
if (zfs != null && zfs.isOpen())
{
try
{
zfs.close();
} catch (IOException e) {
throw new InternalExpressionException("Unable to close zip container: "+zipContainer);
}
catch (final IOException e)
{
throw new InternalExpressionException("Unable to close zip container: " + zipContainer);
}
zfs = null;
}
@@ -86,10 +91,10 @@ public class FileArgument
public static Map<String, Type> of = Arrays.stream(values()).collect(Collectors.toMap(t -> t.id, t -> t));
Type(String id, String extension)
Type(final String id, final String extension)
{
this.id = id;
this.extension = extension;
this.id = id;
this.extension = extension;
}
}
@@ -98,7 +103,7 @@ public class FileArgument
READ, CREATE, DELETE
}
public FileArgument(String resource, Type type, String zipContainer, boolean isFolder, boolean isShared, Reason reason)
public FileArgument(final String resource, final Type type, final String zipContainer, final boolean isFolder, final boolean isShared, final Reason reason)
{
this.resource = resource;
this.type = type;
@@ -111,48 +116,68 @@ public class FileArgument
}
@Override
public String toString() {
return "path: "+resource+" zip: "+zipContainer+" type: "+type.id+" folder: "+isFolder+" shared: "+isShared+" reason: "+reason.toString();
public String toString()
{
return "path: " + resource + " zip: " + zipContainer + " type: " + type.id + " folder: " + isFolder + " shared: " + isShared + " reason: " + reason.toString();
}
public static FileArgument from(List<Value> lv, boolean isFolder, Reason reason)
public static FileArgument from(final List<Value> lv, final boolean isFolder, final Reason reason)
{
if (lv.size() < 2) throw new InternalExpressionException("File functions require path and type as first two arguments");
String origtype = lv.get(1).getString().toLowerCase(Locale.ROOT);
boolean shared = origtype.startsWith("shared_");
String typeString = shared ? origtype.substring(7) : origtype; //len(shared_)
Type type = Type.of.get(typeString);
Pair<String, String> resource = recognizeResource(lv.get(0).getString(), isFolder, type);
if (type==null)
throw new InternalExpressionException("Unsupported file type: "+origtype);
if (type==Type.FOLDER && !isFolder)
if (lv.size() < 2)
{
throw new InternalExpressionException("File functions require path and type as first two arguments");
}
final String origtype = lv.get(1).getString().toLowerCase(Locale.ROOT);
final boolean shared = origtype.startsWith("shared_");
final String typeString = shared ? origtype.substring(7) : origtype; //len(shared_)
final Type type = Type.of.get(typeString);
final Pair<String, String> resource = recognizeResource(lv.get(0).getString(), isFolder, type);
if (type == null)
{
throw new InternalExpressionException("Unsupported file type: " + origtype);
}
if (type == Type.FOLDER && !isFolder)
{
throw new InternalExpressionException("Folder types are no supported for this IO function");
return new FileArgument(resource.getLeft(), type,resource.getRight(), isFolder, shared, reason);
}
return new FileArgument(resource.getLeft(), type, resource.getRight(), isFolder, shared, reason);
}
public static FileArgument resourceFromPath(String path, Reason reason, boolean shared)
public static FileArgument resourceFromPath(final String path, final Reason reason, final boolean shared)
{
Pair<String, String> resource = recognizeResource(path, false, Type.ANY);
return new FileArgument(resource.getLeft(), Type.ANY,resource.getRight(), false, shared, reason);
final Pair<String, String> resource = recognizeResource(path, false, Type.ANY);
return new FileArgument(resource.getLeft(), Type.ANY, resource.getRight(), false, shared, reason);
}
public static Pair<String,String> recognizeResource(String origfile, boolean isFolder, Type type)
public static Pair<String, String> recognizeResource(final String origfile, final boolean isFolder, final Type type)
{
String[] pathElements = origfile.split("[/\\\\]+");
List<String> path = new ArrayList<>();
final String[] pathElements = origfile.split("[/\\\\]+");
final List<String> path = new ArrayList<>();
String zipPath = null;
for (int i =0; i < pathElements.length; i++ )
for (int i = 0; i < pathElements.length; i++)
{
String token = pathElements[i];
boolean isZip = token.endsWith(".zip") && (isFolder || (i < pathElements.length-1));
if (zipPath != null && isZip) throw new InternalExpressionException(token+" indicates zip access in an already zipped location "+zipPath);
if (isZip) token = token.substring(0, token.length()-4);
token = (type==Type.ANY && i == pathElements.length-1)? // sloppy really, but should work
token.replaceAll("[^A-Za-z0-9\\-+_.]", ""):
final boolean isZip = token.endsWith(".zip") && (isFolder || (i < pathElements.length - 1));
if (zipPath != null && isZip)
{
throw new InternalExpressionException(token + " indicates zip access in an already zipped location " + zipPath);
}
if (isZip)
{
token = token.substring(0, token.length() - 4);
}
token = (type == Type.ANY && i == pathElements.length - 1) ? // sloppy really, but should work
token.replaceAll("[^A-Za-z0-9\\-+_.]", "") :
token.replaceAll("[^A-Za-z0-9\\-+_]", "");
if (token.isEmpty()) continue;
if (isZip) token = token + ".zip";
if (token.isEmpty())
{
continue;
}
if (isZip)
{
token = token + ".zip";
}
path.add(token);
if (isZip)
{
@@ -161,80 +186,106 @@ public class FileArgument
}
}
if (path.isEmpty() && !isFolder)
{
throw new InternalExpressionException(
"Cannot use "+origfile+" as resource name: indicated path is empty"+((zipPath==null)?"":" in zip container "+zipPath)
"Cannot use " + origfile + " as resource name: indicated path is empty" + ((zipPath == null) ? "" : " in zip container " + zipPath)
);
}
return Pair.of(String.join("/", path), zipPath);
}
private Path resolve(String suffix)
private Path resolve(final String suffix)
{
if (CarpetServer.minecraft_server == null)
{
throw new InternalExpressionException("Accessing world files without server running");
return CarpetServer.minecraft_server.getWorldPath(LevelResource.ROOT).resolve("scripts/"+suffix);
}
return CarpetServer.minecraft_server.getWorldPath(LevelResource.ROOT).resolve("scripts/" + suffix);
}
private Path toPath(Module module)
private Path toPath(final Module module)
{
if (!isShared && module == null) return null;
if (!isShared && module == null)
{
return null;
}
if (zipContainer == null)
return resolve(getDescriptor(module, resource)+(isFolder?"":type.extension));
{
return resolve(getDescriptor(module, resource) + (isFolder ? "" : type.extension));
}
else
{
if (zfs == null)
{
Map<String, String> env = new HashMap<>();
if (reason == Reason.CREATE) env.put("create", "true");
zipPath = resolve(getDescriptor(module, zipContainer));
if (!Files.exists(zipPath) && reason != Reason.CREATE) return null; // no zip file
try {
if (!Files.exists(zipPath.getParent())) Files.createDirectories(zipPath.getParent());
zfs = FileSystems.newFileSystem(URI.create("jar:"+ zipPath.toUri().toString()), env);
final Map<String, String> env = new HashMap<>();
if (reason == Reason.CREATE)
{
env.put("create", "true");
}
catch (FileSystemNotFoundException | IOException e)
zipPath = resolve(getDescriptor(module, zipContainer));
if (!Files.exists(zipPath) && reason != Reason.CREATE)
{
return null; // no zip file
}
try
{
if (!Files.exists(zipPath.getParent()))
{
Files.createDirectories(zipPath.getParent());
}
zfs = FileSystems.newFileSystem(URI.create("jar:" + zipPath.toUri()), env);
}
catch (final FileSystemNotFoundException | IOException e)
{
CarpetScriptServer.LOG.warn("Exception when opening zip file", e);
throw new ThrowStatement("Unable to open zip file: "+zipContainer, Throwables.IO_EXCEPTION);
throw new ThrowStatement("Unable to open zip file: " + zipContainer, Throwables.IO_EXCEPTION);
}
}
return zfs.getPath(resource+(isFolder?"/":type.extension));
return zfs.getPath(resource + (isFolder ? "/" : type.extension));
}
}
private Path moduleRootPath(Module module)
private Path moduleRootPath(final Module module)
{
if (!isShared && module == null) return null;
return resolve(isShared?"shared":module.name()+".data");
return !isShared && module == null
? null
: resolve(isShared ? "shared" : module.name() + ".data");
}
public String getDisplayPath()
{
return (isShared?"shared/":"")+(zipContainer!=null?zipContainer+"/":"")+resource+type.extension;
return (isShared ? "shared/" : "") + (zipContainer != null ? zipContainer + "/" : "") + resource + type.extension;
}
private String getDescriptor(Module module, String res)
private String getDescriptor(final Module module, final String res)
{
if (isShared)
{
return res.isEmpty()?"shared":"shared/"+res;
return res.isEmpty() ? "shared" : "shared/" + res;
}
if (module != null) // appdata
{
return module.name()+".data"+(res==null || res.isEmpty()?"":"/"+res);
return module.name() + ".data" + (res == null || res.isEmpty() ? "" : "/" + res);
}
throw new InternalExpressionException("Invalid file descriptor: "+res);
throw new InternalExpressionException("Invalid file descriptor: " + res);
}
public boolean findPathAndApply(Module module, Consumer<Path> action)
public boolean findPathAndApply(final Module module, final Consumer<Path> action)
{
try { synchronized (writeIOSync)
try
{
Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
if (dataFile == null) return false;
createPaths(dataFile);
action.accept(dataFile);
} }
synchronized (writeIOSync)
{
final Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
if (dataFile == null)
{
return false;
}
createPaths(dataFile);
action.accept(dataFile);
}
}
finally
{
close();
@@ -242,83 +293,109 @@ public class FileArgument
return true;
}
public Stream<Path> listFiles(Module module)
public Stream<Path> listFiles(final Module module)
{
Path dir = toPath(module);
if (dir == null || !Files.exists(dir)) return null;
String ext = type.extension;
final Path dir = toPath(module);
if (dir == null || !Files.exists(dir))
{
return null;
}
final String ext = type.extension;
try
{
return Files.list(dir).filter(path -> (type==Type.FOLDER)
?Files.isDirectory(path)
:(Files.isRegularFile(path) && path.toString().endsWith(ext))
return Files.list(dir).filter(path -> (type == Type.FOLDER)
? Files.isDirectory(path)
: (Files.isRegularFile(path) && path.toString().endsWith(ext))
);
}
catch (IOException ignored)
catch (final IOException ignored)
{
return null;
}
}
public Stream<String> listFolder(Module module)
public Stream<String> listFolder(final Module module)
{
Stream<String> strings;
try (Stream<Path> result = listFiles(module)) { synchronized (writeIOSync)
try (final Stream<Path> result = listFiles(module))
{
if (result == null) return null;
Path rootPath = moduleRootPath(module);
if (rootPath == null) return null;
String zipComponent = (zipContainer != null) ? rootPath.relativize(zipPath).toString() : null;
// need to evaluate the stream before exiting try-with-resources else there'll be no data to stream
strings = (zipContainer == null)
? result.map(p -> rootPath.relativize(p).toString().replaceAll("[\\\\/]+", "/")).toList().stream()
: result.map(p -> (zipComponent + '/'+ p.toString()).replaceAll("[\\\\/]+", "/")).toList().stream();
} }
synchronized (writeIOSync)
{
if (result == null)
{
return null;
}
final Path rootPath = moduleRootPath(module);
if (rootPath == null)
{
return null;
}
final String zipComponent = (zipContainer != null) ? rootPath.relativize(zipPath).toString() : null;
// need to evaluate the stream before exiting try-with-resources else there'll be no data to stream
strings = (zipContainer == null)
? result.map(p -> rootPath.relativize(p).toString().replaceAll("[\\\\/]+", "/")).toList().stream()
: result.map(p -> (zipComponent + '/' + p.toString()).replaceAll("[\\\\/]+", "/")).toList().stream();
}
}
finally
{
close();
}
if (type == Type.FOLDER)
// java 8 paths are inconsistent. in java 16 they all should not have trailing slashes
return strings.map(s -> s.endsWith("/")?s.substring(0, s.length()-1):s);
return strings.map(FilenameUtils::removeExtension);
// java 8 paths are inconsistent. in java 16 they all should not have trailing slashes
return type == Type.FOLDER
? strings.map(s -> s.endsWith("/") ? s.substring(0, s.length() - 1) : s)
: strings.map(FilenameUtils::removeExtension);
}
private void createPaths(Path file)
private void createPaths(final Path file)
{
try {
try
{
if ((zipContainer == null || file.getParent() != null) &&
!Files.exists(file.getParent()) &&
Files.createDirectories(file.getParent()) == null
) throw new IOException();
} catch (IOException e) {
)
{
throw new IOException();
}
}
catch (final IOException e)
{
CarpetScriptServer.LOG.warn("IOException when creating paths", e);
throw new ThrowStatement("Unable to create paths for "+file.toString(), Throwables.IO_EXCEPTION);
throw new ThrowStatement("Unable to create paths for " + file, Throwables.IO_EXCEPTION);
}
}
public boolean appendToTextFile(Module module, List<String> message)
public boolean appendToTextFile(final Module module, final List<String> message)
{
try { synchronized (writeIOSync)
try
{
Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
if (dataFile == null) return false;
createPaths(dataFile);
OutputStream out = Files.newOutputStream(dataFile, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
synchronized (writeIOSync)
{
for (String line: message)
final Path dataFile = toPath(module);
if (dataFile == null)
{
writer.append(line);
if (type == Type.TEXT) writer.newLine();
return false;
}
createPaths(dataFile);
final OutputStream out = Files.newOutputStream(dataFile, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
try (final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
{
for (final String line : message)
{
writer.append(line);
if (type == Type.TEXT)
{
writer.newLine();
}
}
}
}
} }
catch (IOException e)
}
catch (final IOException e)
{
CarpetScriptServer.LOG.warn("IOException when appending to text file", e);
throw new ThrowStatement("Error when writing to the file: "+e, Throwables.IO_EXCEPTION);
throw new ThrowStatement("Error when writing to the file: " + e, Throwables.IO_EXCEPTION);
}
finally
{
@@ -327,76 +404,91 @@ public class FileArgument
return true;
}
public Tag getNbtData(Module module) // aka getData
public Tag getNbtData(final Module module) // aka getData
{
try { synchronized (writeIOSync) {
Path dataFile = toPath(module);
if (dataFile == null || !Files.exists(dataFile)) return null;
return readTag(dataFile);
} }
finally {
try
{
synchronized (writeIOSync)
{
final Path dataFile = toPath(module);
if (dataFile == null || !Files.exists(dataFile))
{
return null;
}
return readTag(dataFile);
}
}
finally
{
close();
}
}
//copied private method from net.minecraft.nbt.NbtIo.read()
// to read non-compound tags - these won't be compressed
public static Tag readTag(Path path)
public static Tag readTag(final Path path)
{
try
{
return NbtIo.readCompressed(Files.newInputStream(path));
}
catch (IOException e)
catch (final IOException e)
{
// Copy of NbtIo.read(File) because that's now client-side only
if (!Files.exists(path))
{
return null;
}
try (DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
try (final DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
{
return NbtIo.read(in);
}
catch (IOException ioException)
catch (final IOException ioException)
{
// not compressed compound tag neither uncompressed compound tag - trying any type of a tag
try (DataInputStream dataInput_1 = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
try (final DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
{
byte byte_1 = dataInput_1.readByte();
if (byte_1 == 0)
final byte b = dataInputStream.readByte();
if (b == 0)
{
return null;
}
else
{
dataInput_1.readUTF();
return TagTypes.getType(byte_1).load(dataInput_1, 0, NbtAccounter.UNLIMITED);
dataInputStream.readUTF();
return TagTypes.getType(b).load(dataInputStream, 0, NbtAccounter.UNLIMITED);
}
}
catch (IOException secondIO)
catch (final IOException secondIO)
{
CarpetScriptServer.LOG.warn("IOException when trying to read nbt file, something may have gone wrong with the fs", e);
CarpetScriptServer.LOG.warn("", ioException);
CarpetScriptServer.LOG.warn("", secondIO);
throw new ThrowStatement("Not a valid NBT tag in "+path.toString(), Throwables.NBT_ERROR);
throw new ThrowStatement("Not a valid NBT tag in " + path, Throwables.NBT_ERROR);
}
}
}
catch (ReportedException e)
catch (final ReportedException e)
{
throw new ThrowStatement("Error when reading NBT file "+path.toString(), Throwables.NBT_ERROR);
throw new ThrowStatement("Error when reading NBT file " + path.toString(), Throwables.NBT_ERROR);
}
}
public boolean saveNbtData(Module module, Tag tag) // aka saveData
public boolean saveNbtData(final Module module, final Tag tag) // aka saveData
{
try { synchronized (writeIOSync) {
Path dataFile = toPath(module);
if (dataFile == null) return false;
createPaths(dataFile);
return writeTagDisk(tag, dataFile, zipContainer != null);
} }
try
{
synchronized (writeIOSync)
{
final Path dataFile = toPath(module);
if (dataFile == null)
{
return false;
}
createPaths(dataFile);
return writeTagDisk(tag, dataFile, zipContainer != null);
}
}
finally
{
close();
@@ -404,9 +496,9 @@ public class FileArgument
}
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
public static boolean writeTagDisk(Tag tag_1, Path path, boolean zipped)
public static boolean writeTagDisk(final Tag tag, Path path, final boolean zipped)
{
Path original = path;
final Path original = path;
try
{
if (!zipped)
@@ -415,19 +507,19 @@ public class FileArgument
Files.deleteIfExists(path);
}
if (tag_1 instanceof CompoundTag)
if (tag instanceof final CompoundTag cTag)
{
NbtIo.writeCompressed((CompoundTag) tag_1, Files.newOutputStream(path));
NbtIo.writeCompressed(cTag, Files.newOutputStream(path));
}
else
{
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(Files.newOutputStream(path)))
try (final DataOutputStream dataOutputStream = new DataOutputStream(Files.newOutputStream(path)))
{
dataOutputStream_1.writeByte(tag_1.getId());
if (tag_1.getId() != 0)
dataOutputStream.writeByte(tag.getId());
if (tag.getId() != 0)
{
dataOutputStream_1.writeUTF("");
tag_1.write(dataOutputStream_1);
dataOutputStream.writeUTF("");
tag.write(dataOutputStream);
}
}
}
@@ -438,26 +530,31 @@ public class FileArgument
}
return true;
}
catch (IOException e)
catch (final IOException e)
{
CarpetScriptServer.LOG.warn("IO Exception when writing nbt file", e);
throw new ThrowStatement("Unable to write tag to "+original.toString(), Throwables.IO_EXCEPTION);
throw new ThrowStatement("Unable to write tag to " + original, Throwables.IO_EXCEPTION);
}
}
public boolean dropExistingFile(Module module)
public boolean dropExistingFile(final Module module)
{
try { synchronized (writeIOSync)
try
{
Path dataFile = toPath(module);
if (dataFile == null) return false;
return Files.deleteIfExists(dataFile);
} }
catch (IOException e)
synchronized (writeIOSync)
{
final Path dataFile = toPath(module);
if (dataFile == null)
{
return false;
}
return Files.deleteIfExists(dataFile);
}
}
catch (final IOException e)
{
CarpetScriptServer.LOG.warn("IOException when removing file", e);
throw new ThrowStatement("Error while removing file: "+getDisplayPath(), Throwables.IO_EXCEPTION);
throw new ThrowStatement("Error while removing file: " + getDisplayPath(), Throwables.IO_EXCEPTION);
}
finally
{
@@ -465,69 +562,95 @@ public class FileArgument
}
}
public List<String> listFile(Module module)
public List<String> listFile(final Module module)
{
try { synchronized (writeIOSync) {
Path dataFile = toPath(module);
if (dataFile == null) return null;
if (!Files.exists(dataFile)) return null;
return listFileContent(dataFile);
} }
finally {
try
{
synchronized (writeIOSync)
{
final Path dataFile = toPath(module);
if (dataFile == null)
{
return null;
}
if (!Files.exists(dataFile))
{
return null;
}
return listFileContent(dataFile);
}
}
finally
{
close();
}
}
public static List<String> listFileContent(Path filePath)
public static List<String> listFileContent(final Path filePath)
{
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
List<String> result = new ArrayList<>();
for (;;) {
String line = reader.readLine();
try (final BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
{
final List<String> result = new ArrayList<>();
for (; ; )
{
final String line = reader.readLine();
if (line == null)
{
break;
result.add(line.replaceAll("[\n\r]+",""));
}
result.add(line.replaceAll("[\n\r]+", ""));
}
return result;
}
catch (IOException e)
catch (final IOException e)
{
CarpetScriptServer.LOG.warn("IOException when reading text file", e);
throw new ThrowStatement("Failed to read text file "+filePath.toString(), Throwables.IO_EXCEPTION);
throw new ThrowStatement("Failed to read text file " + filePath, Throwables.IO_EXCEPTION);
}
}
public JsonElement readJsonFile(Module module) {
try { synchronized (writeIOSync) {
Path dataFile = toPath(module);
if (dataFile == null || !Files.exists(dataFile)) return null;
return readJsonContent(dataFile);
} }
finally {
public JsonElement readJsonFile(final Module module)
{
try
{
synchronized (writeIOSync)
{
final Path dataFile = toPath(module);
if (dataFile == null || !Files.exists(dataFile))
{
return null;
}
return readJsonContent(dataFile);
}
}
finally
{
close();
}
}
public static JsonElement readJsonContent(Path filePath)
public static JsonElement readJsonContent(final Path filePath)
{
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
try (final BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
{
return JsonParser.parseReader(reader);
}
catch (JsonParseException e)
catch (final JsonParseException e)
{
Throwable exc = e;
if(e.getCause() != null)
if (e.getCause() != null)
{
exc = e.getCause();
}
throw new ThrowStatement(MapValue.wrap(Map.of(
StringValue.of("error"), StringValue.of(exc.getMessage()),
StringValue.of("path"), StringValue.of(filePath.toString())
)), Throwables.JSON_ERROR);
}
catch (IOException e)
catch (final IOException e)
{
CarpetScriptServer.LOG.warn("IOException when reading JSON file", e);
throw new ThrowStatement("Failed to read json file content "+filePath.toString(), Throwables.IO_EXCEPTION);
throw new ThrowStatement("Failed to read json file content " + filePath, Throwables.IO_EXCEPTION);
}
}