Improve automatic configuration upgrades and use separate folder for backups

This commit is contained in:
filoghost 2021-08-07 13:54:22 +02:00
parent 4dc46456a3
commit 72cdacb998
9 changed files with 253 additions and 156 deletions

View File

@ -8,7 +8,6 @@ package me.filoghost.holographicdisplays.plugin;
import com.gmail.filoghost.holographicdisplays.api.internal.HologramsAPIProvider; import com.gmail.filoghost.holographicdisplays.api.internal.HologramsAPIProvider;
import me.filoghost.fcommons.FCommonsPlugin; import me.filoghost.fcommons.FCommonsPlugin;
import me.filoghost.fcommons.FeatureSupport; import me.filoghost.fcommons.FeatureSupport;
import me.filoghost.fcommons.config.exception.ConfigException;
import me.filoghost.fcommons.logging.ErrorCollector; import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.holographicdisplays.api.internal.HolographicDisplaysAPIProvider; import me.filoghost.holographicdisplays.api.internal.HolographicDisplaysAPIProvider;
import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSManager;
@ -20,9 +19,9 @@ import me.filoghost.holographicdisplays.plugin.commands.HologramCommandManager;
import me.filoghost.holographicdisplays.plugin.config.ConfigManager; import me.filoghost.holographicdisplays.plugin.config.ConfigManager;
import me.filoghost.holographicdisplays.plugin.config.HologramDatabase; import me.filoghost.holographicdisplays.plugin.config.HologramDatabase;
import me.filoghost.holographicdisplays.plugin.config.Settings; import me.filoghost.holographicdisplays.plugin.config.Settings;
import me.filoghost.holographicdisplays.plugin.config.upgrade.LegacyAnimationsUpgrade; import me.filoghost.holographicdisplays.plugin.config.upgrade.AnimationsLegacyUpgrade;
import me.filoghost.holographicdisplays.plugin.config.upgrade.LegacyDatabaseUpgrade; import me.filoghost.holographicdisplays.plugin.config.upgrade.DatabaseLegacyUpgrade;
import me.filoghost.holographicdisplays.plugin.config.upgrade.LegacySymbolsUpgrade; import me.filoghost.holographicdisplays.plugin.config.upgrade.SymbolsLegacyUpgrade;
import me.filoghost.holographicdisplays.plugin.hologram.api.APIHologramManager; import me.filoghost.holographicdisplays.plugin.hologram.api.APIHologramManager;
import me.filoghost.holographicdisplays.plugin.hologram.internal.InternalHologramManager; import me.filoghost.holographicdisplays.plugin.hologram.internal.InternalHologramManager;
import me.filoghost.holographicdisplays.plugin.hologram.tracking.LineTrackerManager; import me.filoghost.holographicdisplays.plugin.hologram.tracking.LineTrackerManager;
@ -44,7 +43,6 @@ import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.io.IOException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class HolographicDisplays extends FCommonsPlugin { public class HolographicDisplays extends FCommonsPlugin {
@ -107,21 +105,9 @@ public class HolographicDisplays extends FCommonsPlugin {
APIHologramManager apiHologramManager = new APIHologramManager(lineTrackerManager); APIHologramManager apiHologramManager = new APIHologramManager(lineTrackerManager);
// Run only once at startup, before loading the configuration // Run only once at startup, before loading the configuration
try { new SymbolsLegacyUpgrade(configManager, errorCollector).tryRun();
LegacySymbolsUpgrade.run(configManager, errorCollector); new AnimationsLegacyUpgrade(configManager, errorCollector).tryRun();
} catch (ConfigException e) { new DatabaseLegacyUpgrade(configManager, errorCollector).tryRun();
errorCollector.add(e, "couldn't automatically convert symbols file to the new format");
}
try {
LegacyAnimationsUpgrade.run(configManager, errorCollector);
} catch (IOException e) {
errorCollector.add(e, "couldn't automatically convert animation files to the new format");
}
try {
LegacyDatabaseUpgrade.run(configManager);
} catch (ConfigException | IOException e) {
errorCollector.add(e, "couldn't automatically convert database file to the new format");
}
// Load the configuration // Load the configuration
load(true, errorCollector); load(true, errorCollector);

View File

@ -145,7 +145,7 @@ public class ConfigManager extends BaseConfigManager {
errorCollector.add(e, "error while loading config file \"" + formatPath(file) + "\""); errorCollector.add(e, "error while loading config file \"" + formatPath(file) + "\"");
} }
private String formatPath(Path path) { public String formatPath(Path path) {
return ConfigErrors.formatPath(getRootDataFolder(), path); return ConfigErrors.formatPath(getRootDataFolder(), path);
} }

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.config.upgrade;
import me.filoghost.fcommons.config.Config;
import me.filoghost.fcommons.config.exception.ConfigSaveException;
import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.holographicdisplays.plugin.config.ConfigManager;
import me.filoghost.holographicdisplays.plugin.util.FileUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
public class AnimationsLegacyUpgrade extends LegacyUpgrade {
private static final String SPEED_PREFIX = "speed:";
private final Path animationFolder;
public AnimationsLegacyUpgrade(ConfigManager configManager, ErrorCollector errorCollector) {
super(configManager, errorCollector);
this.animationFolder = configManager.getAnimationsFolder();
}
@Override
public Path getFile() {
return animationFolder;
}
@Override
public void run() throws IOException {
if (!Files.isDirectory(animationFolder)) {
return;
}
try (Stream<Path> animationFiles = Files.list(animationFolder)) {
animationFiles.filter(Files::isRegularFile).forEach(file -> {
tryRun(file, () -> upgradeAnimationFile(file));
});
}
}
private void upgradeAnimationFile(Path oldFile) throws IOException, ConfigSaveException {
String oldFileName = oldFile.getFileName().toString();
if (FileUtils.hasFileExtension(oldFileName, "yml")) {
return; // Probably a file already with the new format
}
List<String> lines = Files.readAllLines(oldFile);
if (lines.size() == 0) {
return;
}
// Remove the first line that only contains the speed
String firstLine = lines.remove(0).trim();
if (!firstLine.toLowerCase().startsWith(SPEED_PREFIX)) {
return; // Not a valid animation
}
String newFileName;
if (FileUtils.hasFileExtension(oldFileName, "txt")) {
newFileName = FileUtils.removeFileExtension(oldFileName) + ".yml";
} else {
newFileName = oldFileName + ".yml";
}
Path newFile = oldFile.resolveSibling(newFileName);
if (Files.isRegularFile(newFile)) {
return; // Already existing, do not override
}
double speed;
try {
speed = Double.parseDouble(firstLine.substring(SPEED_PREFIX.length()).trim());
} catch (NumberFormatException e) {
speed = 0.5;
}
Config config = new Config();
config.setDouble("interval-seconds", speed);
config.setStringList("animation-frames", lines);
createBackupFile(oldFile);
configManager.getConfigLoader(newFile).save(config);
Files.delete(oldFile);
}
}

View File

@ -11,20 +11,30 @@ import me.filoghost.fcommons.config.ConfigSection;
import me.filoghost.fcommons.config.ConfigType; import me.filoghost.fcommons.config.ConfigType;
import me.filoghost.fcommons.config.ConfigValue; import me.filoghost.fcommons.config.ConfigValue;
import me.filoghost.fcommons.config.FileConfig; import me.filoghost.fcommons.config.FileConfig;
import me.filoghost.fcommons.config.exception.ConfigLoadException; import me.filoghost.fcommons.config.exception.ConfigException;
import me.filoghost.fcommons.config.exception.ConfigSaveException; import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.holographicdisplays.plugin.config.ConfigManager; import me.filoghost.holographicdisplays.plugin.config.ConfigManager;
import java.io.IOException; import java.nio.file.Path;
import java.nio.file.Files;
public class LegacyDatabaseUpgrade { public class DatabaseLegacyUpgrade extends LegacyUpgrade implements LegacyUpgradeTask {
public static void run(ConfigManager configManager) throws ConfigLoadException, ConfigSaveException, IOException { private final ConfigLoader databaseConfigLoader;
ConfigLoader databaseConfigLoader = configManager.getConfigLoader("database.yml");
public DatabaseLegacyUpgrade(ConfigManager configManager, ErrorCollector errorCollector) {
super(configManager, errorCollector);
this.databaseConfigLoader = configManager.getConfigLoader("database.yml");
}
@Override
public Path getFile() {
return databaseConfigLoader.getFile();
}
@Override
public void run() throws ConfigException {
if (!databaseConfigLoader.fileExists()) { if (!databaseConfigLoader.fileExists()) {
return; // Database doesn't exist yet, nothing to convert return; // Database file doesn't exist, nothing to upgrade
} }
FileConfig databaseConfig = databaseConfigLoader.load(); FileConfig databaseConfig = databaseConfigLoader.load();
@ -42,12 +52,12 @@ public class LegacyDatabaseUpgrade {
} }
if (changed) { if (changed) {
Files.copy(databaseConfigLoader.getFile(), LegacyUpgradeUtils.getBackupFile(databaseConfigLoader.getFile())); createBackupFile(databaseConfigLoader.getFile());
databaseConfigLoader.save(databaseConfig); databaseConfigLoader.save(databaseConfig);
} }
} }
private static ConfigSection convertLegacySerializedLocation(String legacySerializedLocation) { private ConfigSection convertLegacySerializedLocation(String legacySerializedLocation) {
String[] legacyLocationParts = Strings.splitAndTrim(legacySerializedLocation, ","); String[] legacyLocationParts = Strings.splitAndTrim(legacySerializedLocation, ",");
ConfigSection positionSection = new ConfigSection(); ConfigSection positionSection = new ConfigSection();

View File

@ -1,81 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.config.upgrade;
import me.filoghost.fcommons.config.Config;
import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.holographicdisplays.plugin.config.ConfigManager;
import me.filoghost.holographicdisplays.plugin.util.FileUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
public class LegacyAnimationsUpgrade {
private static final String SPEED_PREFIX = "speed:";
public static void run(ConfigManager configManager, ErrorCollector errorCollector) throws IOException {
Path animationFolder = configManager.getAnimationsFolder();
if (!Files.isDirectory(animationFolder)) {
return;
}
try (Stream<Path> animationFiles = Files.list(animationFolder)) {
animationFiles.filter(Files::isRegularFile).forEach(file -> convertFile(file, configManager, errorCollector));
}
}
private static void convertFile(Path oldFile, ConfigManager configManager, ErrorCollector errorCollector) {
if (LegacyUpgradeUtils.isBackupFile(oldFile)) {
return; // Ignore backup files
}
try {
List<String> lines = Files.readAllLines(oldFile);
if (lines.size() == 0) {
return;
}
// Remove the first line that only contains the speed
String firstLine = lines.remove(0).trim();
if (!firstLine.toLowerCase().startsWith(SPEED_PREFIX)) {
return; // Not a valid animation
}
String newFileName = oldFile.getFileName().toString();
if (FileUtils.hasFileExtension(newFileName, "txt")) {
newFileName = FileUtils.removeFileExtension(newFileName);
}
newFileName += ".yml";
Path newFile = oldFile.resolveSibling(newFileName);
if (Files.isRegularFile(newFile)) {
return; // Already created, do not override
}
double speed;
try {
speed = Double.parseDouble(firstLine.substring(SPEED_PREFIX.length()).trim());
} catch (NumberFormatException e) {
speed = 0.5;
}
Config config = new Config();
config.setDouble("interval-seconds", speed);
config.setStringList("animation-frames", lines);
configManager.getConfigLoader(newFile).save(config);
Files.move(oldFile, LegacyUpgradeUtils.getBackupFile(oldFile));
} catch (Exception e) {
errorCollector.add(e, "couldn't automatically convert animation file \"" + oldFile.getFileName() + "\" to the new format");
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.config.upgrade;
import me.filoghost.fcommons.Preconditions;
import me.filoghost.fcommons.config.exception.ConfigException;
import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.holographicdisplays.plugin.config.ConfigManager;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public abstract class LegacyUpgrade implements LegacyUpgradeTask {
protected final ConfigManager configManager;
private final ErrorCollector errorCollector;
private final Path rootDataFolder;
private final Path backupsFolder;
public LegacyUpgrade(ConfigManager configManager, ErrorCollector errorCollector) {
this.configManager = configManager;
this.errorCollector = errorCollector;
this.rootDataFolder = configManager.getRootDataFolder();
this.backupsFolder = rootDataFolder.resolve("old-files");
}
protected abstract Path getFile();
public final void tryRun() {
tryRun(getFile(), this);
}
protected final void tryRun(Path file, LegacyUpgradeTask task) {
try {
task.run();
} catch (IOException | ConfigException e) {
errorCollector.add(e, "error while upgrading \"" + configManager.formatPath(file) + "\" to the new format");
}
}
protected final void createBackupFile(Path file) {
Preconditions.checkArgument(file.startsWith(rootDataFolder), "file is outside data folder");
Preconditions.checkArgument(!file.startsWith(backupsFolder), "file is inside backups folder");
Path pathFromRootDataFolderToFile = file.subpath(rootDataFolder.getNameCount(), file.getNameCount());
Path backupFile = backupsFolder.resolve(pathFromRootDataFolderToFile);
// Find the first available destination file if already existing
int copyIndex = 1;
while (Files.isRegularFile(backupFile)) {
backupFile = getAlternativeCopyFile(backupFile, copyIndex);
copyIndex++;
}
try {
Files.createDirectories(backupFile.getParent());
Files.copy(file, backupFile);
} catch (IOException e) {
errorCollector.add(e, "error while copying file \"" + configManager.formatPath(file) + "\""
+ " to \"" + configManager.formatPath(backupsFolder) + "\"");
}
}
private Path getAlternativeCopyFile(Path file, int copyIndex) {
String fileName = file.getFileName().toString();
int extensionBeginIndex = fileName.lastIndexOf('.');
String fileNameWithoutExtension;
String extensionWithSeparator;
if (extensionBeginIndex >= 0) {
fileNameWithoutExtension = fileName.substring(0, extensionBeginIndex);
extensionWithSeparator = fileName.substring(extensionBeginIndex);
} else {
fileNameWithoutExtension = fileName;
extensionWithSeparator = "";
}
// Insert the copy index before the extension
return file.resolveSibling(fileNameWithoutExtension + " (" + copyIndex + ")" + extensionWithSeparator);
}
}

View File

@ -0,0 +1,16 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.config.upgrade;
import me.filoghost.fcommons.config.exception.ConfigException;
import java.io.IOException;
public interface LegacyUpgradeTask {
void run() throws IOException, ConfigException;
}

View File

@ -1,22 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.config.upgrade;
import me.filoghost.holographicdisplays.plugin.util.FileUtils;
import java.nio.file.Path;
class LegacyUpgradeUtils {
static Path getBackupFile(Path file) {
return file.resolveSibling(file.getFileName() + ".backup");
}
static boolean isBackupFile(Path file) {
return FileUtils.hasFileExtension(file, "backup");
}
}

View File

@ -11,8 +11,8 @@ import me.filoghost.fcommons.config.ConfigErrors;
import me.filoghost.fcommons.config.ConfigLoader; import me.filoghost.fcommons.config.ConfigLoader;
import me.filoghost.fcommons.config.ConfigPath; import me.filoghost.fcommons.config.ConfigPath;
import me.filoghost.fcommons.config.ConfigSection; import me.filoghost.fcommons.config.ConfigSection;
import me.filoghost.fcommons.config.exception.ConfigException;
import me.filoghost.fcommons.config.exception.ConfigLoadException; import me.filoghost.fcommons.config.exception.ConfigLoadException;
import me.filoghost.fcommons.config.exception.ConfigSaveException;
import me.filoghost.fcommons.logging.ErrorCollector; import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.holographicdisplays.plugin.config.ConfigManager; import me.filoghost.holographicdisplays.plugin.config.ConfigManager;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
@ -22,19 +22,31 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
public class LegacySymbolsUpgrade { public class SymbolsLegacyUpgrade extends LegacyUpgrade {
public static void run(ConfigManager configManager, ErrorCollector errorCollector) throws ConfigLoadException, ConfigSaveException { private final Path oldFile;
Path oldFile = configManager.getRootDataFolder().resolve("symbols.yml");
public SymbolsLegacyUpgrade(ConfigManager configManager, ErrorCollector errorCollector) {
super(configManager, errorCollector);
this.oldFile = configManager.getRootDataFolder().resolve("symbols.yml");
}
@Override
protected Path getFile() {
return oldFile;
}
@Override
public void run() throws IOException, ConfigException {
ConfigLoader newConfigLoader = configManager.getConfigLoader("custom-placeholders.yml"); ConfigLoader newConfigLoader = configManager.getConfigLoader("custom-placeholders.yml");
Path newFile = newConfigLoader.getFile(); Path newFile = newConfigLoader.getFile();
if (!Files.isRegularFile(oldFile)) { if (!Files.isRegularFile(oldFile)) {
return; // Old file doesn't exist, ignore upgrade return; // Old file doesn't exist, nothing to upgrade
} }
if (Files.isRegularFile(newFile)) { if (Files.isRegularFile(newFile)) {
return; // Already created, do not override return; // Already existing, do not override
} }
Config newConfig = new Config(); Config newConfig = new Config();
@ -55,37 +67,32 @@ public class LegacySymbolsUpgrade {
// Ignore bad line // Ignore bad line
if (!line.contains(":")) { if (!line.contains(":")) {
errorCollector.add("couldn't convert invalid line in " + oldFile.getFileName() + ": " + line);
continue; continue;
} }
String[] parts = Strings.splitAndTrim(line, ":", 2); String[] placeholderAndReplacement = Strings.splitAndTrim(line, ":", 2);
String placeholder = unquote(parts[0]); String placeholder = unquote(placeholderAndReplacement[0]);
String replacement = StringEscapeUtils.unescapeJava(unquote(parts[1])); String replacement = StringEscapeUtils.unescapeJava(unquote(placeholderAndReplacement[1]));
placeholdersSection.setString(ConfigPath.literal(placeholder), replacement); placeholdersSection.setString(ConfigPath.literal(placeholder), replacement);
} }
try { createBackupFile(oldFile);
Files.move(oldFile, LegacyUpgradeUtils.getBackupFile(oldFile));
} catch (IOException e) {
errorCollector.add(e, "couldn't rename " + oldFile.getFileName());
}
newConfigLoader.save(newConfig); newConfigLoader.save(newConfig);
Files.delete(oldFile);
} }
private static String unquote(String input) { private static String unquote(String input) {
if (input.length() < 2) { if (input.length() < 2) {
// Too short, cannot be a quoted string // Too short to be a quoted string
return input; return input;
} }
if (input.startsWith("'") && input.endsWith("'")) {
return input.substring(1, input.length() - 1);
} else if (input.startsWith("\"") && input.endsWith("\"")) {
return input.substring(1, input.length() - 1);
}
if ((input.startsWith("'") && input.endsWith("'")) || (input.startsWith("\"") && input.endsWith("\""))) {
return input.substring(1, input.length() - 1);
} else {
return input; return input;
} }
}
} }