From d9c2af035ac2edb68bc8b6a1f94b7c772c3509c8 Mon Sep 17 00:00:00 2001 From: Keuin Date: Sun, 2 Aug 2020 18:31:50 +0800 Subject: Refactor & object tree impl. stage 1 --- .../com/keuin/kbackupfabric/KBCommandRegister.java | 2 +- .../java/com/keuin/kbackupfabric/KBCommands.java | 24 +++--- .../com/keuin/kbackupfabric/KBPluginEvents.java | 4 +- src/main/java/com/keuin/kbackupfabric/Random.java | 97 ++++++++++++++++++++++ .../kbackupfabric/operation/BackupOperation.java | 23 ++--- .../kbackupfabric/operation/DeleteOperation.java | 6 +- .../kbackupfabric/operation/RestoreOperation.java | 4 +- .../operation/backup/BackupMethod.java | 22 +++++ .../operation/backup/PrimitiveBackupMehtod.java | 42 ++++++++++ .../kbackupfabric/util/BackupFilesystemUtil.java | 78 ----------------- .../util/BackupNameSuggestionProvider.java | 77 ----------------- .../util/BackupNameTimeFormatter.java | 22 ----- .../com/keuin/kbackupfabric/util/PrintUtil.java | 16 ++++ .../java/com/keuin/kbackupfabric/util/ZipUtil.java | 6 +- .../util/backup/BackupFilesystemUtil.java | 82 ++++++++++++++++++ .../util/backup/BackupNameSuggestionProvider.java | 77 +++++++++++++++++ .../util/backup/BackupNameTimeFormatter.java | 32 +++++++ .../kbackupfabric/util/backup/BackupType.java | 21 +++++ .../util/backup/builder/BackupFileNameBuilder.java | 25 ++++++ .../builder/ObjectTreeBackupFileNameBuilder.java | 20 +++++ .../builder/PrimitiveZipBackupFileNameBuilder.java | 22 +++++ .../backup/formatter/BackupFileNameFormatter.java | 32 +++++++ .../ObjectTreeBackupFileNameFormatter.java | 47 +++++++++++ .../PrimitiveZipBackupFileNameFormatter.java | 46 ++++++++++ 24 files changed, 615 insertions(+), 212 deletions(-) create mode 100644 src/main/java/com/keuin/kbackupfabric/Random.java create mode 100644 src/main/java/com/keuin/kbackupfabric/operation/backup/BackupMethod.java create mode 100644 src/main/java/com/keuin/kbackupfabric/operation/backup/PrimitiveBackupMehtod.java delete mode 100644 src/main/java/com/keuin/kbackupfabric/util/BackupFilesystemUtil.java delete mode 100644 src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java delete mode 100644 src/main/java/com/keuin/kbackupfabric/util/BackupNameTimeFormatter.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/BackupFilesystemUtil.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameSuggestionProvider.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameTimeFormatter.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/BackupType.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/builder/BackupFileNameBuilder.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/builder/ObjectTreeBackupFileNameBuilder.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/builder/PrimitiveZipBackupFileNameBuilder.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/formatter/BackupFileNameFormatter.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/formatter/ObjectTreeBackupFileNameFormatter.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/backup/formatter/PrimitiveZipBackupFileNameFormatter.java diff --git a/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java b/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java index b223e3f..bfbe3c1 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java +++ b/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java @@ -1,6 +1,6 @@ package com.keuin.kbackupfabric; -import com.keuin.kbackupfabric.util.BackupNameSuggestionProvider; +import com.keuin.kbackupfabric.util.backup.BackupNameSuggestionProvider; import com.keuin.kbackupfabric.util.PermissionValidator; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; diff --git a/src/main/java/com/keuin/kbackupfabric/KBCommands.java b/src/main/java/com/keuin/kbackupfabric/KBCommands.java index 651a73e..9568842 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBCommands.java +++ b/src/main/java/com/keuin/kbackupfabric/KBCommands.java @@ -6,9 +6,10 @@ import com.keuin.kbackupfabric.operation.BackupOperation; import com.keuin.kbackupfabric.operation.DeleteOperation; import com.keuin.kbackupfabric.operation.RestoreOperation; import com.keuin.kbackupfabric.operation.abstracts.i.Invokable; -import com.keuin.kbackupfabric.util.BackupFilesystemUtil; -import com.keuin.kbackupfabric.util.BackupNameSuggestionProvider; -import com.keuin.kbackupfabric.util.BackupNameTimeFormatter; +import com.keuin.kbackupfabric.operation.backup.BackupMethod; +import com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil; +import com.keuin.kbackupfabric.util.backup.BackupNameSuggestionProvider; +import com.keuin.kbackupfabric.util.backup.BackupNameTimeFormatter; import com.keuin.kbackupfabric.util.PrintUtil; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; @@ -16,12 +17,9 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; -import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.*; +import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.*; import static com.keuin.kbackupfabric.util.PrintUtil.*; public final class KBCommands { @@ -78,6 +76,7 @@ public final class KBCommands { File[] files = getBackupSaveDirectory(server).listFiles( (dir, name) -> dir.isDirectory() && name.toLowerCase().endsWith(".zip") && name.toLowerCase().startsWith(getBackupFileNamePrefix()) ); + backupNameList.clear(); if (files != null) { if (files.length != 0) { @@ -112,7 +111,7 @@ public final class KBCommands { backupName = String.format("a%s", backupName); msgWarn(context, String.format("Pure numeric name is not allowed. Renaming to %s", backupName)); } - return doBackup(context, backupName); + return doBackup(context, backupName, BackupMethod); } /** @@ -189,10 +188,10 @@ public final class KBCommands { return doBackup(context, "noname"); } - private static int doBackup(CommandContext context, String customName) { + private static int doBackup(CommandContext context, String customBackupName, BackupMethod backupMethod) { // Real backup name (compatible with legacy backup): date_name, such as 2020-04-23_21-03-00_test //KBMain.backup("name") - String backupName = BackupNameTimeFormatter.getTimeString() + "_" + customName; + String backupName = BackupNameTimeFormatter.getTimeString() + "_" + customBackupName; // Validate file name final char[] ILLEGAL_CHARACTERS = {'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'}; @@ -204,10 +203,9 @@ public final class KBCommands { } // Do backup - BackupMetadata metadata = new BackupMetadata(System.currentTimeMillis(), backupName); PrintUtil.info("Invoking backup worker ..."); //BackupWorker.invoke(context, backupName, metadata); - BackupOperation operation = new BackupOperation(context, backupName, metadata); + BackupOperation operation = new BackupOperation(context, backupName, backupMethod); if (operation.invoke()) { return SUCCESS; } else if (operation.isBlocked()) { diff --git a/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java index 9e90019..d67d85b 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java +++ b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java @@ -2,8 +2,8 @@ package com.keuin.kbackupfabric; import com.keuin.kbackupfabric.metadata.BackupMetadata; import com.keuin.kbackupfabric.metadata.MetadataHolder; -import com.keuin.kbackupfabric.util.BackupFilesystemUtil; -import com.keuin.kbackupfabric.util.BackupNameSuggestionProvider; +import com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil; +import com.keuin.kbackupfabric.util.backup.BackupNameSuggestionProvider; import com.keuin.kbackupfabric.util.PrintUtil; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.server.ServerStartCallback; diff --git a/src/main/java/com/keuin/kbackupfabric/Random.java b/src/main/java/com/keuin/kbackupfabric/Random.java new file mode 100644 index 0000000..e87e4c1 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/Random.java @@ -0,0 +1,97 @@ +package com.keuin.kbackupfabric; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class Random { + public int random(int A, int B) { + return (int) (A + (B - A + 1) * Math.random()); + } + + public double[] randomArray(int MaxN, int lowA, int highA) { + double[] array = new double[random(1,MaxN)]; + for (int i = 0; i < array.length; i++) { + array[i] = lowA + (highA - lowA) * Math.random(); + } + return array; + } + + public double[] generate(int MaxN, int lowA, int highA) { + double[] testData = randomArray(MaxN,lowA,highA); + double x = testData[random(0,testData.length-1)]; + } + + public int R(double[] a, double x) { + List list = new LinkedList<>(); + int k; + for (int i = 0; i < a.length; i++) { + if(Math.abs(a[i] - x) < 1e-6) + list.add(i); + } + assert !list.isEmpty(); + k = list.get(list.size() / 2); + return k; + } + + + public boolean domR(double[] a, double x) { + for (double v : a) { + if(Math.abs(v - x) < 1e-6) + return true; + } + return false; + } + + public boolean oracle(double[] a, double x, int k) { + if(!(Math.abs(a[k] - x) < 1e-6)) + return false; + int c1=0,c2=0; + for (int i = 0; i < k; i++) { + if(Math.abs(a[i] - x) < 1e-6) + ++c1; + } + for (int i = k; i < a.length; i++) { + if(Math.abs(a[i] - x) < 1e-6) + ++c2; + } + return c1 - c2 >= -1 && c1 - c2 <= 1; + } + + public boolean driver() { + int MaxN = 80, lowA = 20, highA = 50; + double[] a = randomArray(MaxN,lowA,highA); + double x = a[random(0,a.length-1)]; + return oracle(a,x,search(a,x)); + } + + + public int search(double[] a, double x) { + int counter = 0; + for (double i : a) { + if(Math.abs(i - x) < 1e-6) + ++counter; + } + counter /= 2; + for (int i = 0; i < a.length; i++) { + if(Math.abs(a[i] - x) < 1e-6) { + --counter; + if(counter == 0) + return i; + } + } + return -1; + } + + public static void Main() { + int pass = 0, fail = 0; + for (int i = 0; i < 10000; i++) { + if(driver()) + pass++; + else + fail++; + } + System.out.println(String.format("pass: %d, fail: %d.",pass,fail)); + } + +} diff --git a/src/main/java/com/keuin/kbackupfabric/operation/BackupOperation.java b/src/main/java/com/keuin/kbackupfabric/operation/BackupOperation.java index 53b05b8..4c08d14 100644 --- a/src/main/java/com/keuin/kbackupfabric/operation/BackupOperation.java +++ b/src/main/java/com/keuin/kbackupfabric/operation/BackupOperation.java @@ -3,9 +3,12 @@ package com.keuin.kbackupfabric.operation; import com.keuin.kbackupfabric.exception.ZipUtilException; import com.keuin.kbackupfabric.metadata.BackupMetadata; import com.keuin.kbackupfabric.operation.abstracts.InvokableAsyncBlockingOperation; +import com.keuin.kbackupfabric.operation.backup.BackupMethod; import com.keuin.kbackupfabric.util.PrintUtil; -import com.keuin.kbackupfabric.util.ZipUtil; +import com.keuin.kbackupfabric.util.backup.builder.BackupFileNameBuilder; +import com.keuin.kbackupfabric.util.backup.formatter.BackupFileNameFormatter; import com.mojang.brigadier.context.CommandContext; +import com.sun.istack.internal.NotNull; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.world.World; @@ -15,7 +18,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.*; +import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.*; import static com.keuin.kbackupfabric.util.PrintUtil.msgInfo; public class BackupOperation extends InvokableAsyncBlockingOperation { @@ -23,15 +26,15 @@ public class BackupOperation extends InvokableAsyncBlockingOperation { private final CommandContext context; private final String backupName; private final Map oldWorldsSavingDisabled = new HashMap<>(); - private final BackupMetadata backupMetadata; + private final BackupMethod backupMethod; private long startTime; - public BackupOperation(CommandContext context, String backupName, BackupMetadata backupMetadata) { + public BackupOperation(@NotNull CommandContext context, @NotNull String backupName, @NotNull BackupMethod backupMethod, @NotNull BackupFileNameBuilder backupFileNameBuilder, @NotNull BackupFileNameFormatter backupFileNameFormatter) { super("BackupWorker"); this.context = context; this.backupName = backupName; - this.backupMetadata = backupMetadata; + this.backupMethod = backupMethod; } @Override @@ -52,11 +55,8 @@ public class BackupOperation extends InvokableAsyncBlockingOperation { // Make zip String levelPath = getLevelPath(server); String backupFileName = getBackupFileName(backupName); - PrintUtil.info(String.format("zip(srcPath=%s, destPath=%s)", levelPath, backupSaveDirectoryFile.toString())); - msgInfo(context, "Compressing worlds ..."); - PrintUtil.info("Compressing level ..."); - ZipUtil.makeBackupZip(levelPath, backupSaveDirectoryFile.toString(), backupFileName, backupMetadata); - File backupZipFile = new File(backupSaveDirectoryFile, backupFileName); + + backupMethod.backup(backupName,levelPath,backupSaveDirectory); // Restore old autosave switch stat server.getWorlds().forEach(world -> world.savingDisabled = oldWorldsSavingDisabled.getOrDefault(world, true)); @@ -64,6 +64,7 @@ public class BackupOperation extends InvokableAsyncBlockingOperation { // Print finish message: time elapsed and file size long timeElapsedMillis = System.currentTimeMillis() - startTime; String msgText = String.format("Backup finished. Time elapsed: %.2fs.", timeElapsedMillis / 1000.0); + File backupZipFile = new File(backupSaveDirectory, backupFileName); try { msgText += String.format(" File size: %s.", humanFileSize(backupZipFile.length())); } catch (SecurityException ignored) { @@ -72,7 +73,7 @@ public class BackupOperation extends InvokableAsyncBlockingOperation { } catch (SecurityException e) { msgInfo(context, String.format("Failed to create backup saving directory: %s. Failed to backup.", backupSaveDirectory)); - } catch (IOException | ZipUtilException e) { + } catch (IOException e) { msgInfo(context, "Failed to make zip: " + e.getMessage()); } } diff --git a/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java b/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java index ee1a57e..30fdfc0 100644 --- a/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java +++ b/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java @@ -1,7 +1,7 @@ package com.keuin.kbackupfabric.operation; import com.keuin.kbackupfabric.operation.abstracts.InvokableAsyncBlockingOperation; -import com.keuin.kbackupfabric.util.BackupNameSuggestionProvider; +import com.keuin.kbackupfabric.util.backup.BackupNameSuggestionProvider; import com.keuin.kbackupfabric.util.PrintUtil; import com.mojang.brigadier.context.CommandContext; import net.minecraft.server.MinecraftServer; @@ -10,8 +10,8 @@ import net.minecraft.server.command.ServerCommandSource; import java.io.File; import java.io.IOException; -import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.getBackupFileName; -import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.getBackupSaveDirectory; +import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.getBackupFileName; +import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.getBackupSaveDirectory; import static com.keuin.kbackupfabric.util.PrintUtil.msgErr; import static com.keuin.kbackupfabric.util.PrintUtil.msgInfo; import static org.apache.commons.io.FileUtils.forceDelete; diff --git a/src/main/java/com/keuin/kbackupfabric/operation/RestoreOperation.java b/src/main/java/com/keuin/kbackupfabric/operation/RestoreOperation.java index 6754393..ef6ab2b 100644 --- a/src/main/java/com/keuin/kbackupfabric/operation/RestoreOperation.java +++ b/src/main/java/com/keuin/kbackupfabric/operation/RestoreOperation.java @@ -11,8 +11,8 @@ import net.minecraft.server.command.ServerCommandSource; import java.io.File; import java.io.IOException; -import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.getBackupFileName; -import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.getBackupSaveDirectory; +import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.getBackupFileName; +import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.getBackupSaveDirectory; import static org.apache.commons.io.FileUtils.forceDelete; public class RestoreOperation extends InvokableBlockingOperation { diff --git a/src/main/java/com/keuin/kbackupfabric/operation/backup/BackupMethod.java b/src/main/java/com/keuin/kbackupfabric/operation/backup/BackupMethod.java new file mode 100644 index 0000000..97c609d --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/operation/backup/BackupMethod.java @@ -0,0 +1,22 @@ +package com.keuin.kbackupfabric.operation.backup; + +import com.keuin.kbackupfabric.util.backup.builder.BackupFileNameBuilder; +import com.keuin.kbackupfabric.util.backup.formatter.BackupFileNameFormatter; +import com.sun.istack.internal.NotNull; + +import java.io.IOException; + +public interface BackupMethod { + + /** + * Perform a backup with given method. The backup will be saved as the given name. + * Note: real file name is depended by the backup type. + * @param backupName the backup name. + */ + void backup(@NotNull String backupName, @NotNull String levelPath, @NotNull String backupSaveDirectory) throws IOException; + + BackupFileNameBuilder getBackupFileNameBuilder(); + + BackupFileNameFormatter getBackupFileNameFormatter(); + +} diff --git a/src/main/java/com/keuin/kbackupfabric/operation/backup/PrimitiveBackupMehtod.java b/src/main/java/com/keuin/kbackupfabric/operation/backup/PrimitiveBackupMehtod.java new file mode 100644 index 0000000..3c2a608 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/operation/backup/PrimitiveBackupMehtod.java @@ -0,0 +1,42 @@ +package com.keuin.kbackupfabric.operation.backup; + +import com.keuin.kbackupfabric.exception.ZipUtilException; +import com.keuin.kbackupfabric.metadata.BackupMetadata; +import com.keuin.kbackupfabric.util.PrintUtil; +import com.keuin.kbackupfabric.util.ZipUtil; +import com.keuin.kbackupfabric.util.backup.builder.BackupFileNameBuilder; +import com.keuin.kbackupfabric.util.backup.formatter.BackupFileNameFormatter; + +import java.io.File; +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; + +public class PrimitiveBackupMehtod implements BackupMethod { + @Override + public boolean backup(String backupName, String levelPath, String backupSaveDirectory) throws IOException { + try { + String backupFileName = BackupFileNameBuilder.primitiveZipBackup().build(LocalDateTime.now(),backupName); + BackupMetadata backupMetadata = new BackupMetadata(System.currentTimeMillis(), backupName); + + PrintUtil.info(String.format("zip(srcPath=%s, destPath=%s)", levelPath, backupSaveDirectory)); + PrintUtil.info("Compressing level ..."); + ZipUtil.makeBackupZip(levelPath, backupSaveDirectory, backupFileName, backupMetadata); + + } catch (ZipUtilException exception) { + PrintUtil.info("Infinite recursive of directory tree detected, backup was aborted."); + return false; + } + return true; + } + + @Override + public BackupFileNameBuilder getBackupFileNameBuilder() { + return BackupFileNameBuilder.primitiveZipBackup(); + } + + @Override + public BackupFileNameFormatter getBackupFileNameFormatter() { + return BFNF; + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/BackupFilesystemUtil.java b/src/main/java/com/keuin/kbackupfabric/util/BackupFilesystemUtil.java deleted file mode 100644 index ce39615..0000000 --- a/src/main/java/com/keuin/kbackupfabric/util/BackupFilesystemUtil.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.keuin.kbackupfabric.util; - -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.world.ThreadedAnvilChunkStorage; -import net.minecraft.world.World; - -import java.io.File; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Functions deal with file name, directory name about Minecraft saves. - */ -public final class BackupFilesystemUtil { - - private static final String backupSaveDirectoryName = "backups"; - private static final String backupFileNamePrefix = "kbackup-"; - - public static String getBackupFileName(String backupName) { - return backupFileNamePrefix + backupName + ".zip"; - } - - public static String getBackupName(String backupFileName) { - try { - if (backupFileName.matches(backupFileNamePrefix + ".+\\.zip")) - return backupFileName.substring(backupFileNamePrefix.length(), backupFileName.length() - 4); - } catch (IndexOutOfBoundsException ignored) { - } - return backupFileName; - } - - public static boolean isBackupNameValid(String backupName, MinecraftServer server) { - File backupFile = new File(getBackupSaveDirectory(server), getBackupFileName(backupName)); - return backupFile.isFile(); - } - - public static File getBackupSaveDirectory(MinecraftServer server) { - return new File(server.getRunDirectory(), backupSaveDirectoryName); - } - - public static String getLevelPath(MinecraftServer server) { - return (new File(server.getRunDirectory(), server.getLevelName())).getAbsolutePath(); - } - - public static String getWorldDirectoryName(World world) throws NoSuchFieldException, IllegalAccessException { - File saveDir; - ThreadedAnvilChunkStorage threadedAnvilChunkStorage = (ThreadedAnvilChunkStorage) ReflectionUtils.getPrivateField(world.getChunkManager(), "threadedAnvilChunkStorage"); - saveDir = (File) ReflectionUtils.getPrivateField(threadedAnvilChunkStorage, "saveDir"); - return saveDir.getName(); - } - - public static String getBackupFileNamePrefix() { - return backupFileNamePrefix; - } - - public static long getBackupTimeFromBackupFileName(String backupFileName) { - Matcher matcher = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}").matcher(backupFileName); - if (matcher.find()) { - String timeString = matcher.group(0); - long timeStamp = BackupNameTimeFormatter.timeStringToEpochSeconds(timeString); - System.out.println(backupFileName + " -> " + timeStamp); - return timeStamp; - } else { - System.err.println("Failed to extract time from " + backupFileName); - } - return -1; - } - - public static String humanFileSize(long size) { - double fileSize = size * 1.0 / 1024 / 1024; // Default unit is MB - if (fileSize > 1000) - //msgInfo(context, String.format("File size: %.2fGB", fileSize / 1024)); - return String.format("%.2fGB", fileSize / 1024); - else - //msgInfo(context, String.format("File size: %.2fMB", fileSize)); - return String.format("%.2fMB", fileSize); - } -} diff --git a/src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java b/src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java deleted file mode 100644 index ff122b9..0000000 --- a/src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.keuin.kbackupfabric.util; - -import com.mojang.brigadier.suggestion.SuggestionProvider; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.server.command.ServerCommandSource; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.CompletableFuture; - -public class BackupNameSuggestionProvider { - - private static final List candidateCacheList = new ArrayList<>(); - private static final Object syncSetDirectory = new Object(); - private static final Object syncCache = new Object(); - private static final long CACHE_TTL = 8000; - private static String backupSaveDirectory; - private static long cacheUpdateTime = 0; - - public static void setBackupSaveDirectory(String backupSaveDirectory) { - synchronized (syncSetDirectory) { - BackupNameSuggestionProvider.backupSaveDirectory = backupSaveDirectory; - } - // Immediately perform an update - updateCandidateList(); - } - - public static void updateCandidateList() { - synchronized (syncCache) { - try { - File file = new File(backupSaveDirectory); - candidateCacheList.clear(); - File[] files = file.listFiles(); - if (files == null) - return; - for (File f : files) - candidateCacheList.add(BackupFilesystemUtil.getBackupName(f.getName())); - cacheUpdateTime = System.currentTimeMillis(); - } catch (NullPointerException ignored) { - } - } - } - -// private static void updateCandidateList(Collection stringCollection) { -// candidateList.clear(); -// candidateList.addAll(stringCollection); -// } - - public static SuggestionProvider getProvider() { - return (context, builder) -> getCompletableFuture(builder); - } - - private static CompletableFuture getCompletableFuture(SuggestionsBuilder builder) { - if (isCacheExpired()) - updateCandidateList(); - String remaining = builder.getRemaining().toLowerCase(Locale.ROOT); - synchronized (syncCache) { - if (candidateCacheList.isEmpty()) { // If the list is empty then return no suggestions - return Suggestions.empty(); // No suggestions - } - - for (String string : candidateCacheList) { // Iterate through the supplied list - if (string.toLowerCase(Locale.ROOT).startsWith(remaining)) { - builder.suggest(string); // Add every single entry to suggestions list. - } - } - } - return builder.buildFuture(); // Create the CompletableFuture containing all the suggestions - } - - private static boolean isCacheExpired() { - return System.currentTimeMillis() - cacheUpdateTime > CACHE_TTL || cacheUpdateTime == 0; - } -} diff --git a/src/main/java/com/keuin/kbackupfabric/util/BackupNameTimeFormatter.java b/src/main/java/com/keuin/kbackupfabric/util/BackupNameTimeFormatter.java deleted file mode 100644 index 5d00270..0000000 --- a/src/main/java/com/keuin/kbackupfabric/util/BackupNameTimeFormatter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.keuin.kbackupfabric.util; - -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; - -public class BackupNameTimeFormatter { - private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); - - public static String getTimeString() { - return LocalDateTime.now().format(formatter); - } - - public static long timeStringToEpochSeconds(String timeString) { - ZoneId systemZone = ZoneId.systemDefault(); // my timezone - LocalDateTime localDateTime = LocalDateTime.parse(timeString, formatter); - ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(localDateTime); - return localDateTime.toEpochSecond(currentOffsetForMyZone); - } - -} diff --git a/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java b/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java index 8839993..81c6a33 100644 --- a/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java +++ b/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java @@ -80,18 +80,34 @@ public final class PrintUtil { return context; } + /** + * Print debug message on the server console. + * @param string the message. + */ public static void debug(String string) { LOGGER.debug("[KBackup] " + string); } + /** + * Print informative message on the server console. + * @param string the message. + */ public static void info(String string) { LOGGER.info("[KBackup] " + string); } + /** + * Print warning message on the server console. + * @param string the message. + */ public static void warn(String string) { LOGGER.warn("[KBackup] " + string); } + /** + * Print error message on the server console. + * @param string the message. + */ public static void error(String string) { LOGGER.error("[KBackup] " + string); } diff --git a/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java b/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java index 11b912e..0ca8da3 100644 --- a/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java +++ b/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java @@ -68,11 +68,11 @@ public final class ZipUtil { * @param zipPath 压缩文件保存的路径。注意:zipPath不能是srcPath路径下的子文件夹 * @param zipFileName 压缩文件名 * @throws IOException IO Error - * @throws ZipUtilException General exception, such as loop recursion or invalid input. + * @throws ZipUtilException General exception, such as loop recursion. */ public static void makeBackupZip(String srcPath, String zipPath, String zipFileName, BackupMetadata backupMetadata) throws IOException, ZipUtilException { - if (srcPath.isEmpty() || zipPath.isEmpty() || zipFileName.isEmpty()) { - throw new ZipUtilException("Parameter for zip() contains null."); + if (srcPath == null || zipPath == null || zipFileName == null || backupMetadata == null || srcPath.isEmpty() || zipPath.isEmpty() || zipFileName.isEmpty()) { + throw new IllegalArgumentException("Parameter for zip() contains null."); } CheckedOutputStream checkedOutputStream; ZipOutputStream zipOutputStream = null; diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/BackupFilesystemUtil.java b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupFilesystemUtil.java new file mode 100644 index 0000000..54c2f58 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupFilesystemUtil.java @@ -0,0 +1,82 @@ +package com.keuin.kbackupfabric.util.backup; + +import com.keuin.kbackupfabric.util.ReflectionUtils; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ThreadedAnvilChunkStorage; +import net.minecraft.world.World; + +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Functions deal with file name, directory name about Minecraft saves. + */ +public final class BackupFilesystemUtil { + + private static final String backupSaveDirectoryName = "backups"; + private static final String backupFileNamePrefix = "kbackup-"; + + public static String getBackupFileNamePrefix() { + return backupFileNamePrefix; + } + + @Deprecated + public static String getBackupFileName(String backupName) { + return backupFileNamePrefix + backupName + ".zip"; + } + + @Deprecated + public static String getBackupName(String backupFileName) { + try { + if (backupFileName.matches(backupFileNamePrefix + ".+\\.zip")) + return backupFileName.substring(backupFileNamePrefix.length(), backupFileName.length() - 4); + } catch (IndexOutOfBoundsException ignored) { + } + return backupFileName; + } + + public static boolean isBackupNameValid(String backupName, MinecraftServer server) { + File backupFile = new File(getBackupSaveDirectory(server), getBackupFileName(backupName)); + return backupFile.isFile(); + } + + public static File getBackupSaveDirectory(MinecraftServer server) { + return new File(server.getRunDirectory(), backupSaveDirectoryName); + } + + public static String getLevelPath(MinecraftServer server) { + return (new File(server.getRunDirectory(), server.getLevelName())).getAbsolutePath(); + } + + public static String getWorldDirectoryName(World world) throws NoSuchFieldException, IllegalAccessException { + File saveDir; + ThreadedAnvilChunkStorage threadedAnvilChunkStorage = (ThreadedAnvilChunkStorage) ReflectionUtils.getPrivateField(world.getChunkManager(), "threadedAnvilChunkStorage"); + saveDir = (File) ReflectionUtils.getPrivateField(threadedAnvilChunkStorage, "saveDir"); + return saveDir.getName(); + } + + @Deprecated + public static long getBackupTimeFromBackupFileName(String backupFileName) { + Matcher matcher = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}").matcher(backupFileName); + if (matcher.find()) { + String timeString = matcher.group(0); + long timeStamp = BackupNameTimeFormatter.timeStringToEpochSeconds(timeString); + System.out.println(backupFileName + " -> " + timeStamp); + return timeStamp; + } else { + System.err.println("Failed to extract time from " + backupFileName); + } + return -1; + } + + public static String humanFileSize(long size) { + double fileSize = size * 1.0 / 1024 / 1024; // Default unit is MB + if (fileSize > 1000) + //msgInfo(context, String.format("File size: %.2fGB", fileSize / 1024)); + return String.format("%.2fGB", fileSize / 1024); + else + //msgInfo(context, String.format("File size: %.2fMB", fileSize)); + return String.format("%.2fMB", fileSize); + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameSuggestionProvider.java b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameSuggestionProvider.java new file mode 100644 index 0000000..4639e99 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameSuggestionProvider.java @@ -0,0 +1,77 @@ +package com.keuin.kbackupfabric.util.backup; + +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.server.command.ServerCommandSource; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; + +public class BackupNameSuggestionProvider { + + private static final List candidateCacheList = new ArrayList<>(); + private static final Object syncSetDirectory = new Object(); + private static final Object syncCache = new Object(); + private static final long CACHE_TTL = 8000; + private static String backupSaveDirectory; + private static long cacheUpdateTime = 0; + + public static void setBackupSaveDirectory(String backupSaveDirectory) { + synchronized (syncSetDirectory) { + BackupNameSuggestionProvider.backupSaveDirectory = backupSaveDirectory; + } + // Immediately perform an update + updateCandidateList(); + } + + public static void updateCandidateList() { + synchronized (syncCache) { + try { + File file = new File(backupSaveDirectory); + candidateCacheList.clear(); + File[] files = file.listFiles(); + if (files == null) + return; + for (File f : files) + candidateCacheList.add(BackupFilesystemUtil.getBackupName(f.getName())); + cacheUpdateTime = System.currentTimeMillis(); + } catch (NullPointerException ignored) { + } + } + } + +// private static void updateCandidateList(Collection stringCollection) { +// candidateList.clear(); +// candidateList.addAll(stringCollection); +// } + + public static SuggestionProvider getProvider() { + return (context, builder) -> getCompletableFuture(builder); + } + + private static CompletableFuture getCompletableFuture(SuggestionsBuilder builder) { + if (isCacheExpired()) + updateCandidateList(); + String remaining = builder.getRemaining().toLowerCase(Locale.ROOT); + synchronized (syncCache) { + if (candidateCacheList.isEmpty()) { // If the list is empty then return no suggestions + return Suggestions.empty(); // No suggestions + } + + for (String string : candidateCacheList) { // Iterate through the supplied list + if (string.toLowerCase(Locale.ROOT).startsWith(remaining)) { + builder.suggest(string); // Add every single entry to suggestions list. + } + } + } + return builder.buildFuture(); // Create the CompletableFuture containing all the suggestions + } + + private static boolean isCacheExpired() { + return System.currentTimeMillis() - cacheUpdateTime > CACHE_TTL || cacheUpdateTime == 0; + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameTimeFormatter.java b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameTimeFormatter.java new file mode 100644 index 0000000..3811ae8 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupNameTimeFormatter.java @@ -0,0 +1,32 @@ +package com.keuin.kbackupfabric.util.backup; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +public class BackupNameTimeFormatter { + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); + + @Deprecated + public static String getTimeString() { + return LocalDateTime.now().format(formatter); + } + + public static String localDateTimeToString(LocalDateTime localDateTime) { + return localDateTime.format(formatter); + } + + @Deprecated + public static long timeStringToEpochSeconds(String timeString) { + ZoneId systemZone = ZoneId.systemDefault(); // my timezone + LocalDateTime localDateTime = LocalDateTime.parse(timeString, formatter); + ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(localDateTime); + return localDateTime.toEpochSecond(currentOffsetForMyZone); + } + + public static LocalDateTime timeStringToLocalDateTime(String timeString) { + return LocalDateTime.parse(timeString,formatter); + } + +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/BackupType.java b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupType.java new file mode 100644 index 0000000..95a32ae --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/BackupType.java @@ -0,0 +1,21 @@ +package com.keuin.kbackupfabric.util.backup; + +/** + * Representing the backup type. + * Should only be used in BackupFileNameBuilder and BackupFileNameFormatter + */ +public enum BackupType { + + PRIMITIVE_ZIP_BACKUP("Primitive Zip Backup"), + OBJECT_TREE_BACKUP("Object Tree Backup"); + + private final String name; + BackupType(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/builder/BackupFileNameBuilder.java b/src/main/java/com/keuin/kbackupfabric/util/backup/builder/BackupFileNameBuilder.java new file mode 100644 index 0000000..16c598b --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/builder/BackupFileNameBuilder.java @@ -0,0 +1,25 @@ +package com.keuin.kbackupfabric.util.backup.builder; + +import com.sun.istack.internal.NotNull; + +import java.time.LocalDateTime; + +public interface BackupFileNameBuilder { + + static BackupFileNameBuilder primitiveZipBackup() { + return PrimitiveZipBackupFileNameBuilder.getInstance(); + } + + static BackupFileNameBuilder objectTreeBackup() { + return ObjectTreeBackupFileNameBuilder.getInstance(); + } + + /** + * Build a backup file name based on given information. + * @param time when the backup was created. + * @param backupName the custom name of this backup. Note that this should be a valid file name in current file system. + * @return the backup file name string. + */ + String build(@NotNull LocalDateTime time, @NotNull String backupName); + +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/builder/ObjectTreeBackupFileNameBuilder.java b/src/main/java/com/keuin/kbackupfabric/util/backup/builder/ObjectTreeBackupFileNameBuilder.java new file mode 100644 index 0000000..3c15741 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/builder/ObjectTreeBackupFileNameBuilder.java @@ -0,0 +1,20 @@ +package com.keuin.kbackupfabric.util.backup.builder; + +import com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil; +import com.keuin.kbackupfabric.util.backup.BackupNameTimeFormatter; + +import java.time.LocalDateTime; + +public class ObjectTreeBackupFileNameBuilder implements BackupFileNameBuilder { + private static final ObjectTreeBackupFileNameBuilder instance = new ObjectTreeBackupFileNameBuilder(); + + public static ObjectTreeBackupFileNameBuilder getInstance() { + return instance; + } + + @Override + public String build(LocalDateTime time, String backupName) { + String timeString = BackupNameTimeFormatter.localDateTimeToString(time); + return String.format("%s%s_%s%s", BackupFilesystemUtil.getBackupFileNamePrefix(),timeString,backupName,".json"); + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/builder/PrimitiveZipBackupFileNameBuilder.java b/src/main/java/com/keuin/kbackupfabric/util/backup/builder/PrimitiveZipBackupFileNameBuilder.java new file mode 100644 index 0000000..f910c37 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/builder/PrimitiveZipBackupFileNameBuilder.java @@ -0,0 +1,22 @@ +package com.keuin.kbackupfabric.util.backup.builder; + +import com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil; +import com.keuin.kbackupfabric.util.backup.BackupNameTimeFormatter; + +import java.time.LocalDateTime; + +public class PrimitiveZipBackupFileNameBuilder implements BackupFileNameBuilder { + + private static final PrimitiveZipBackupFileNameBuilder instance = new PrimitiveZipBackupFileNameBuilder(); + + public static PrimitiveZipBackupFileNameBuilder getInstance() { + return instance; + } + + @Override + public String build(LocalDateTime time, String backupName) { + String timeString = BackupNameTimeFormatter.localDateTimeToString(time); + return String.format("%s%s_%s%s", BackupFilesystemUtil.getBackupFileNamePrefix(),timeString,backupName,".zip"); + } + +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/BackupFileNameFormatter.java b/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/BackupFileNameFormatter.java new file mode 100644 index 0000000..eae3639 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/BackupFileNameFormatter.java @@ -0,0 +1,32 @@ +package com.keuin.kbackupfabric.util.backup.formatter; + +import com.keuin.kbackupfabric.util.backup.BackupNameTimeFormatter; +import com.sun.istack.internal.NotNull; + +import java.time.LocalDateTime; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public interface BackupFileNameFormatter { + + BackupFileName format(@NotNull String fileName); + + class BackupFileName { + public final LocalDateTime time; + public final String name; + + public BackupFileName(LocalDateTime time, String name) { + this.time = time; + this.name = name; + } + } + + static BackupFileNameFormatter objectTreeBackup() { + return ObjectTreeBackupFileNameFormatter.getInstance(); + } + + static BackupFileNameFormatter primitiveZipBackup() { + return PrimitiveZipBackupFileNameFormatter.getInstance(); + } + +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/ObjectTreeBackupFileNameFormatter.java b/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/ObjectTreeBackupFileNameFormatter.java new file mode 100644 index 0000000..e5503a9 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/ObjectTreeBackupFileNameFormatter.java @@ -0,0 +1,47 @@ +package com.keuin.kbackupfabric.util.backup.formatter; + +import com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil; +import com.keuin.kbackupfabric.util.backup.BackupNameTimeFormatter; +import com.sun.istack.internal.NotNull; +import com.sun.istack.internal.Nullable; +import org.spongepowered.asm.mixin.Overwrite; + +import java.time.LocalDateTime; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ObjectTreeBackupFileNameFormatter implements BackupFileNameFormatter { + + private static final ObjectTreeBackupFileNameFormatter instance = new ObjectTreeBackupFileNameFormatter(); + + public static ObjectTreeBackupFileNameFormatter getInstance() { + return instance; + } + + @Override + public BackupFileNameFormatter.BackupFileName format(@NotNull String fileName) { + LocalDateTime time = getTime(fileName); + String name = getBackupName(fileName); + return new BackupFileNameFormatter.BackupFileName(time,name); + } + + @Nullable + private LocalDateTime getTime(String fileName) { + Matcher matcher = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}").matcher(fileName); + if (matcher.find()) { + String timeString = matcher.group(0); + return BackupNameTimeFormatter.timeStringToLocalDateTime(timeString); + } + return null; + } + + private String getBackupName(String backupFileName) { + try { + if (backupFileName.matches(BackupFilesystemUtil.getBackupFileNamePrefix() + ".+\\.json")) + return backupFileName.substring(BackupFilesystemUtil.getBackupFileNamePrefix().length(), backupFileName.length() - 4); + } catch (IndexOutOfBoundsException ignored) { + } + return backupFileName; + } + +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/PrimitiveZipBackupFileNameFormatter.java b/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/PrimitiveZipBackupFileNameFormatter.java new file mode 100644 index 0000000..40450eb --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/formatter/PrimitiveZipBackupFileNameFormatter.java @@ -0,0 +1,46 @@ +package com.keuin.kbackupfabric.util.backup.formatter; + +import com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil; +import com.keuin.kbackupfabric.util.backup.BackupNameTimeFormatter; +import com.sun.istack.internal.NotNull; +import com.sun.istack.internal.Nullable; + +import java.time.LocalDateTime; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PrimitiveZipBackupFileNameFormatter implements BackupFileNameFormatter { + + private static final PrimitiveZipBackupFileNameFormatter instance = new PrimitiveZipBackupFileNameFormatter(); + + public static PrimitiveZipBackupFileNameFormatter getInstance() { + return instance; + } + + @Override + public BackupFileNameFormatter.BackupFileName format(@NotNull String fileName) { + LocalDateTime time = getTime(fileName); + String name = getBackupName(fileName); + return new BackupFileNameFormatter.BackupFileName(time,name); + } + + @Nullable + private LocalDateTime getTime(String fileName) { + Matcher matcher = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}").matcher(fileName); + if (matcher.find()) { + String timeString = matcher.group(0); + return BackupNameTimeFormatter.timeStringToLocalDateTime(timeString); + } + return null; + } + + private String getBackupName(String backupFileName) { + try { + if (backupFileName.matches(BackupFilesystemUtil.getBackupFileNamePrefix() + ".+\\.zip")) + return backupFileName.substring(BackupFilesystemUtil.getBackupFileNamePrefix().length(), backupFileName.length() - 4); + } catch (IndexOutOfBoundsException ignored) { + } + return backupFileName; + } + +} -- cgit v1.2.3