Adds auto-updating of locale files

https://github.com/BentoBoxWorld/BentoBox/issues/960
This commit is contained in:
tastybento 2019-09-28 17:10:30 -07:00
parent 6799c43a0a
commit f8c4ea568f
2 changed files with 102 additions and 23 deletions

View File

@ -3,6 +3,7 @@ package world.bentobox.bentobox.api.addons;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@ -12,6 +13,7 @@ import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.Listener;
@ -244,8 +246,9 @@ public abstract class Addon {
* - if true, will overwrite previous file
* @param noPath
* - if true, the resource's path will be ignored when saving
* @return file written, or null if none
*/
public void saveResource(String jarResource, File destinationFolder, boolean replace, boolean noPath) {
public File saveResource(String jarResource, File destinationFolder, boolean replace, boolean noPath) {
if (jarResource == null || jarResource.equals("")) {
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
}
@ -269,6 +272,7 @@ public abstract class Addon {
if (!outFile.exists() || replace) {
java.nio.file.Files.copy(in, outFile.toPath());
}
return outFile;
}
} else {
// No file in the jar
@ -279,6 +283,31 @@ public abstract class Addon {
BentoBox.getInstance().logError(
"Could not save from jar file. From " + jarResource + " to " + destinationFolder.getAbsolutePath());
}
return null;
}
/**
* Tries to load a YAML file from the Jar
* @param jarResource - YAML file in jar
* @return YamlConfiguration - may be empty
* @throws IOException - if the file cannot be found or loaded from the Jar
* @throws InvalidConfigurationException - if the yaml is malformed
*/
public YamlConfiguration getYamlFromJar(String jarResource) throws IOException, InvalidConfigurationException {
if (jarResource == null || jarResource.equals("")) {
throw new IllegalArgumentException("jarResource cannot be null or empty");
}
YamlConfiguration result = new YamlConfiguration();
jarResource = jarResource.replace('\\', '/');
try (JarFile jar = new JarFile(file)) {
JarEntry jarConfig = jar.getJarEntry(jarResource);
if (jarConfig != null) {
try (InputStreamReader in = new InputStreamReader(jar.getInputStream(jarConfig))) {
result.load(in);
}
}
}
return result;
}
/**

View File

@ -4,6 +4,7 @@ import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
@ -15,6 +16,7 @@ import java.util.jar.JarFile;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import world.bentobox.bentobox.BentoBox;
@ -108,7 +110,7 @@ public class LocalesManager {
}
/**
* Copies locale files from the addon jar to the file system
* Copies locale files from the addon jar to the file system and updates current locales with the latest references
* @param addon - addon
*/
void copyLocalesFromAddonJar(Addon addon) {
@ -116,14 +118,39 @@ public class LocalesManager {
File localeDir = new File(plugin.getDataFolder(), LOCALE_FOLDER + File.separator + addon.getDescription().getName());
if (!localeDir.exists()) {
localeDir.mkdirs();
// Obtain any locale files and save them
Util.listJarFiles(jar, LOCALE_FOLDER, ".yml").forEach(lf -> addon.saveResource(lf, localeDir, false, true));
}
// Obtain any locale files, save them and update
Util.listJarFiles(jar, LOCALE_FOLDER, ".yml").forEach(lf -> {
File file = addon.saveResource(lf, localeDir, false, true);
// Update
if (file != null) {
updateLocale(addon, file, lf);
}
});
} catch (Exception e) {
plugin.logError(e.getMessage());
}
}
private void updateLocale(Addon addon, File fileLocaleFile, String lf) {
try {
// Load the JAR locale file
YamlConfiguration jarLocale = addon.getYamlFromJar(lf);
// Load the locale file system locale file
YamlConfiguration fileLocale = new YamlConfiguration();
fileLocale.load(fileLocaleFile);
// Copy new keys to file
jarLocale.getKeys(true).stream().filter(k -> !fileLocale.contains(k, false)).forEach(k -> fileLocale.set(k, jarLocale.get(k)));
// Save file
fileLocale.save(fileLocaleFile);
} catch (Exception e) {
plugin.logError("Error updating locale file: " + lf + " : " + e.getMessage());
plugin.logStacktrace(e);
}
}
/**
* Copies all the locale files from the plugin jar to the filesystem.
* Only done if the locale folder does not already exist.
@ -135,18 +162,36 @@ public class LocalesManager {
// If it does exist, then new files will NOT be written!
if (!localeDir.exists()) {
localeDir.mkdirs();
FileLister lister = new FileLister(plugin);
try {
for (String name : lister.listJar(LOCALE_FOLDER)) {
// We cannot use Bukkit's saveResource, because we want it to go into a specific folder, so...
// Get the last part of the name
int lastIndex = name.lastIndexOf('/');
File targetFile = new File(localeDir, name.substring(lastIndex >= 0 ? lastIndex : 0));
copyFile(name, targetFile);
}
FileLister lister = new FileLister(plugin);
try {
for (String name : lister.listJar(LOCALE_FOLDER)) {
// We cannot use Bukkit's saveResource, because we want it to go into a specific folder, so...
// Get the last part of the name
int lastIndex = name.lastIndexOf('/');
File targetFile = new File(localeDir, name.substring(lastIndex >= 0 ? lastIndex : 0));
copyFile(name, targetFile);
// Update the locale file if it exists already
try (InputStreamReader in = new InputStreamReader(plugin.getResource(name))) {
YamlConfiguration jarLocale = new YamlConfiguration();
jarLocale.load(in);
YamlConfiguration fileLocale = new YamlConfiguration();
fileLocale.load(targetFile);
for (String k : jarLocale.getKeys(true)) {
if (!fileLocale.contains(k, false)) {
fileLocale.set(k, jarLocale.get(k));
}
}
// Save it
fileLocale.save(targetFile);
} catch (InvalidConfigurationException e) {
plugin.logError("Could not update locale files from jar " + e.getMessage());
}
} catch (IOException e) {
plugin.logError("Could not copy locale files from jar " + e.getMessage());
}
} catch (IOException e) {
plugin.logError("Could not copy locale files from jar " + e.getMessage());
}
}
@ -180,9 +225,9 @@ public class LocalesManager {
languages.put(localeObject, new BentoBoxLocale(localeObject, languageYaml));
}
} catch (Exception e) {
BentoBox.getInstance().logError("Could not load '" + language.getName() + "' : " + e.getMessage()
plugin.logError("Could not load '" + language.getName() + "' : " + e.getMessage()
+ " with the following cause '" + e.getCause() + "'." +
" The file has likely an invalid YML format or has been made unreadable during the process.");
" The file has likely an invalid YML format or has been made unreadable during the process.");
}
}
}
@ -256,7 +301,11 @@ public class LocalesManager {
plugin.log(ChatColor.AQUA + "Analyzing BentoBox locale files");
user.sendRawMessage(ChatColor.AQUA + SPACER);
loadLocalesFromFile(BENTOBOX);
analyze(fix);
if (languages.containsKey(Locale.US)) {
analyze(user, fix);
} else {
user.sendRawMessage(ChatColor.RED + "No US English in BentoBox to use for analysis!");
}
user.sendRawMessage(ChatColor.AQUA + "Analyzing Addon locale files");
plugin.getAddonsManager().getAddons().forEach(addon -> {
user.sendRawMessage(ChatColor.AQUA + SPACER);
@ -264,22 +313,23 @@ public class LocalesManager {
user.sendRawMessage(ChatColor.AQUA + SPACER);
languages.clear();
loadLocalesFromFile(addon.getDescription().getName());
analyze(fix);
if (languages.containsKey(Locale.US)) {
analyze(user, fix);
} else {
user.sendRawMessage(ChatColor.RED + "No US English to use for analysis!");
}
});
reloadLanguages();
}
/**
*
* @param user
* @param fix whether or not locale files with missing translations should be fixed.
* Not currently supported.
* @since 1.5.0
*/
private void analyze(boolean fix) {
if (!languages.containsKey(Locale.US)) {
return;
}
User user = User.getInstance(Bukkit.getConsoleSender());
private void analyze(User user, boolean fix) {
user.sendRawMessage(ChatColor.GREEN + "The following locales are supported:");
languages.forEach((k,v) -> user.sendRawMessage(ChatColor.GOLD + k.toLanguageTag() + " " + k.getDisplayLanguage() + " " + k.getDisplayCountry()));