From 9add2387b9c60d2e1f55996e8b14f3d5182f2eea Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 23 Feb 2019 23:42:05 +0800 Subject: [PATCH] Add comments to FileUtils and IOUtils --- .../download/game/LibraryDownloadTask.java | 12 ++-- .../download/game/VersionJsonSaveTask.java | 2 - .../hmcl/game/DefaultGameRepository.java | 4 +- .../jackhuang/hmcl/mod/ForgeModMetadata.java | 4 +- .../jackhuang/hmcl/task/FileDownloadTask.java | 18 ++--- .../java/org/jackhuang/hmcl/task/Task.java | 5 ++ .../org/jackhuang/hmcl/util/io/FileUtils.java | 67 ++++++++++++++----- .../org/jackhuang/hmcl/util/io/IOUtils.java | 17 ++++- 8 files changed, 91 insertions(+), 38 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java index fecb40e3f..311ffaa1a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java @@ -225,16 +225,16 @@ public class LibraryDownloadTask extends Task { int x = decompressed.length; int len = decompressed[(x - 8)] & 0xFF | (decompressed[(x - 7)] & 0xFF) << 8 | (decompressed[(x - 6)] & 0xFF) << 16 | (decompressed[(x - 5)] & 0xFF) << 24; - File temp = FileUtils.createTempFile("minecraft", ".pack"); + Path temp = Files.createTempFile("minecraft", ".pack"); byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8); - OutputStream out = new FileOutputStream(temp); - out.write(decompressed, 0, decompressed.length - len - 8); - out.close(); + try (OutputStream out = Files.newOutputStream(temp)) { + out.write(decompressed, 0, decompressed.length - len - 8); + } try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) { - Pack200.newUnpacker().unpack(temp, jos); + Pack200.newUnpacker().unpack(temp.toFile(), jos); JarEntry checksumsFile = new JarEntry("checksums.sha1"); checksumsFile.setTime(0L); @@ -243,6 +243,6 @@ public class LibraryDownloadTask extends Task { jos.closeEntry(); } - temp.delete(); + Files.delete(temp); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java index d404994b5..00a14543e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java @@ -53,8 +53,6 @@ public final class VersionJsonSaveTask extends TaskResult { @Override public void execute() throws Exception { File json = repository.getVersionJson(version.getId()).getAbsoluteFile(); - if (!FileUtils.makeFile(json)) - throw new IOException("Cannot create file " + json); FileUtils.writeText(json, JsonUtils.GSON.toJson(version)); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index f20eda3cf..39ee352e3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -168,8 +168,8 @@ public class DefaultGameRepository implements GameRepository { versions.remove(id); - if (FileUtils.isMovingToTrashSupported()) { - return FileUtils.moveToTrash(removedFile); + if (FileUtils.isMovingToTrashSupported() && FileUtils.moveToTrash(removedFile)) { + return true; } // remove json files first to ensure HMCL will not recognize this folder as a valid version. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java index 7880b47ff..e0f4105ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java @@ -23,7 +23,7 @@ import com.google.gson.reflect.TypeToken; import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; -import org.jackhuang.hmcl.util.io.IOUtils; +import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; import java.io.IOException; @@ -119,7 +119,7 @@ public final class ForgeModMetadata { Path mcmod = fs.getPath("mcmod.info"); if (Files.notExists(mcmod)) throw new IOException("File " + modFile + " is not a Forge mod."); - List modList = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(Files.newInputStream(mcmod)), + List modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), new TypeToken>() { }.getType()); if (modList == null || modList.isEmpty()) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index 92ecbe580..7829d02b0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -33,6 +33,7 @@ import java.io.RandomAccessFile; import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; import java.util.Optional; @@ -212,7 +213,7 @@ public class FileDownloadTask extends Task { break; } - File temp = null; + Path temp = null; try { updateProgress(0); @@ -242,15 +243,15 @@ public class FileDownloadTask extends Task { if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) throw new IOException("Could not make directory " + file.getAbsoluteFile().getParent()); - temp = FileUtils.createTempFile(); - rFile = new RandomAccessFile(temp, "rw"); + temp = Files.createTempFile(null, null); + rFile = new RandomAccessFile(temp.toFile(), "rw"); MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest(); stream = con.getInputStream(); int lastDownloaded = 0, downloaded = 0; long lastTime = System.currentTimeMillis(); - byte buffer[] = new byte[IOUtils.DEFAULT_BUFFER_SIZE]; + byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE]; while (true) { if (Thread.interrupted()) { Thread.currentThread().interrupt(); @@ -283,16 +284,15 @@ public class FileDownloadTask extends Task { // Restore temp file to original name. if (Thread.interrupted()) { - temp.delete(); + temp.toFile().delete(); Thread.currentThread().interrupt(); break; } else { - if (file.exists() && !file.delete()) - throw new IOException("Unable to delete existent file " + file); + Files.deleteIfExists(file.toPath()); if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) throw new IOException("Unable to make parent directory " + file); try { - FileUtils.moveFile(temp, file); + FileUtils.moveFile(temp.toFile(), file); } catch (Exception e) { throw new IOException("Unable to move temp file from " + temp + " to " + file, e); } @@ -321,7 +321,7 @@ public class FileDownloadTask extends Task { return; } catch (IOException | IllegalStateException e) { if (temp != null) - temp.delete(); + temp.toFile().delete(); exception = e; } finally { closeFiles(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index c43ab0d3b..cede8eebc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -293,6 +293,11 @@ public abstract class Task { return new TaskResult() { TaskResult then; + @Override + public Collection getDependents() { + return Collections.singleton(Task.this); + } + @Override public void execute() throws Exception { then = taskSupplier.get().storeTo(this::setResult); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java index 52fe65a8f..a38e53f2f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java @@ -116,14 +116,43 @@ public final class FileUtils { return new String(Files.readAllBytes(file), charset); } + /** + * Write plain text to file. Characters are encoded into bytes using UTF-8. + * + * We don't care about platform difference of line separator. Because readText accept all possibilities of line separator. + * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. + * All characters in text will be written into the file in binary format. Existing data will be erased. + * @param file the path to the file + * @param text the text being written to file + * @throws IOException if an I/O error occurs + */ public static void writeText(File file, String text) throws IOException { writeText(file, text, UTF_8); } + /** + * Write plain text to file. + * + * We don't care about platform difference of line separator. Because readText accept all possibilities of line separator. + * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. + * All characters in text will be written into the file in binary format. Existing data will be erased. + * @param file the path to the file + * @param text the text being written to file + * @param charset the charset to use for encoding + * @throws IOException if an I/O error occurs + */ public static void writeText(File file, String text, Charset charset) throws IOException { writeBytes(file, text.getBytes(charset)); } + /** + * Write byte array to file. + * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. + * All bytes in byte array will be written into the file in binary format. Existing data will be erased. + * @param file the path to the file + * @param array the data being written to file + * @throws IOException if an I/O error occurs + */ public static void writeBytes(File file, byte[] array) throws IOException { Files.createDirectories(file.toPath().getParent()); Files.write(file.toPath(), array); @@ -153,6 +182,14 @@ public final class FileUtils { } } + /** + * Copy directory. + * Paths of all files relative to source directory will be the same as the ones relative to destination directory. + * + * @param src the source directory. + * @param dest the destination directory, which will be created if not existing. + * @throws IOException if an I/O error occurs. + */ public static void copyDirectory(Path src, Path dest) throws IOException { Files.walkFileTree(src, new SimpleFileVisitor(){ @Override @@ -173,6 +210,20 @@ public final class FileUtils { }); } + /** + * Move file to trash. + * + * This method is only implemented in Java 9. Please check we are using Java 9 by invoking isMovingToTrashSupported. + * Example: + *
{@code
+     * if (FileUtils.isMovingToTrashSupported()) {
+     *     FileUtils.moveToTrash(file);
+     * }
+     * }
+ * @param file the file being moved to trash. + * @see FileUtils#isMovingToTrashSupported() + * @return false if moveToTrash does not exist, or platform does not support Desktop.Action.MOVE_TO_TRASH + */ public static boolean moveToTrash(File file) { try { java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); @@ -314,20 +365,4 @@ public final class FileUtils { result.add(it); return result; } - - public static File createTempFile() throws IOException { - return createTempFile("tmp"); - } - - public static File createTempFile(String prefix) throws IOException { - return createTempFile(prefix, null); - } - - public static File createTempFile(String prefix, String suffix) throws IOException { - return createTempFile(prefix, suffix, null); - } - - public static File createTempFile(String prefix, String suffix, File directory) throws IOException { - return File.createTempFile(prefix, suffix, directory); - } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java index e5421a2c3..25d085c0e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java @@ -21,8 +21,9 @@ import java.io.*; import java.nio.charset.Charset; /** + * This utility class consists of some util methods operating on InputStream/OutputStream. * - * @author huang + * @author huangyuhui */ public final class IOUtils { @@ -31,12 +32,26 @@ public final class IOUtils { public static final int DEFAULT_BUFFER_SIZE = 8 * 1024; + /** + * Read all bytes to a buffer from given input stream. The stream will not be closed. + * + * @param stream the InputStream being read. + * @return all bytes read from the stream + * @throws IOException if an I/O error occurs. + */ public static byte[] readFullyWithoutClosing(InputStream stream) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); copyTo(stream, result); return result.toByteArray(); } + /** + * Read all bytes to a buffer from given input stream, and close the input stream finally. + * + * @param stream the InputStream being read, closed finally. + * @return all bytes read from the stream + * @throws IOException if an I/O error occurs. + */ public static ByteArrayOutputStream readFully(InputStream stream) throws IOException { try (InputStream is = stream) { ByteArrayOutputStream result = new ByteArrayOutputStream();