package me.corriekay.pokegoutil.utils; import java.io.File; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringEscapeUtils; import me.corriekay.pokegoutil.utils.helpers.FileHelper; import org.json.JSONException; import org.json.JSONObject; /** * Config class that manages saving data to the config.json. */ public final class ConfigNew { private static final File file = new File(System.getProperty("user.dir"), "config.json"); private static final ConfigNew cfg = new ConfigNew(); // Constants private static final String CANNOT_FETCH_UNF_STRING = "Could not fetch config item '%s'! Fallback to default: %s%n"; private static final String CANNOT_SAVE_UNF_STRING = "Could not save '%s' to config (%s)!%n"; // Class properties private JSONObject json; private long lastModified = file.lastModified(); /** * Constructor that reads the config file. */ private ConfigNew() { if (!file.exists()) { try { if (!file.createNewFile()) { throw new FileAlreadyExistsException(file.getName()); } } catch (final IOException e) { System.out.println(e.toString()); } json = new JSONObject(); saveConfig(); } else { json = new JSONObject(FileHelper.readFile(file)); } cleanUpAndFill(); } /** * Returns the config object that can read the config. * * @return The Config object. */ public static ConfigNew getConfig() { return cfg; } /** * Returns the default value as Object, so that it may can added again. Just for internal use, NO TYP CHECKING! * * @param configKey The config key. * @return The Object */ private Object getAsObject(final ConfigKey configKey) { Object obj; switch (configKey.type) { case BOOLEAN: obj = getBool(configKey); break; case STRING: obj = getString(configKey); break; case INTEGER: obj = getInt(configKey); break; case DOUBLE: obj = getDouble(configKey); break; default: obj = getJSONObject(configKey); break; } return obj; } /** * Saves given object under the key. Just for internal use, NO TYP CHECKING! * * @param configKey The config key. * @param obj The object to set. */ private void setFromObject(final ConfigKey configKey, Object obj) { switch (configKey.type) { case BOOLEAN: setBool(configKey, (Boolean) obj); break; case STRING: setString(configKey, (String) obj); break; case INTEGER: setInt(configKey, (Integer) obj); break; case DOUBLE: setDouble(configKey, (Double) obj); break; default: setJSONObject(configKey, new JSONObject(obj)); break; } } /** * Returns the JSONObject for given key. The one in the config, or if it does not exist, the default one. * * @param configKey The config key. * @return The JSONObject under the key, or default value. */ public JSONObject getJSONObject(final ConfigKey configKey) { return getJSONObject(configKey, configKey.getDefaultValue()); } /** * Returns the JSONObject for given key. The one in the config, or if it does not exist, the given default value. * * @param configKey The config key. * @param defaultValue The default value to choose if the key does not exist. * @return The JSONObject under the key, or default value. */ public JSONObject getJSONObject(final ConfigKey configKey, final JSONObject defaultValue) { try { final FindResult res = findNode(configKey.keyName, false); return res.getNode().getJSONObject(res.getName()); } catch (final JSONException ignored) { //System.out.printf(CANNOT_FETCH_UNF_STRING, configKey.keyName, defaultValue); setJSONObject(configKey, defaultValue); return defaultValue; } } /** * Saves the given JSONObject under given key. * * @param configKey The config key. * @param value The value to save. */ public void setJSONObject(final ConfigKey configKey, final JSONObject value) { try { final FindResult res = findNode(configKey.keyName, true); if (res.getNode().optJSONObject(res.getName()) != value || value.equals(configKey.getDefaultValue())) { res.getNode().put(res.getName(), value); saveConfig(); } } catch (final JSONException ignored) { System.out.printf(CANNOT_SAVE_UNF_STRING, value, configKey.keyName); } } /** * Returns the Boolean for given key. The one in the config, or if it does not exist, the default one. * * @param configKey The config key. * @return The Boolean under the key, or default value. */ public boolean getBool(final ConfigKey configKey) { return getBool(configKey, configKey.getDefaultValue()); } /** * Returns the Boolean for given key. The one in the config, or if it does not exist, the given default value. * * @param configKey The config key. * @param defaultValue The default value to choose if the key does not exist. * @return The Boolean under the key, or default value. */ public boolean getBool(final ConfigKey configKey, final boolean defaultValue) { try { final FindResult res = findNode(configKey.keyName, false); return res.getNode().getBoolean(res.getName()); } catch (final JSONException ignored) { //System.out.printf(CANNOT_FETCH_UNF_STRING, configKey.keyName, defaultValue); setBool(configKey, defaultValue); return defaultValue; } } /** * Saves the given Boolean under given key. * * @param configKey The config key. * @param value The value to save. */ public void setBool(final ConfigKey configKey, final boolean value) { try { final FindResult res = findNode(configKey.keyName, true); // Set if value is different or if default value should be added boolean defaultValue = configKey.getDefaultValue(); if (res.getNode().optBoolean(res.getName(), defaultValue) != value || value == defaultValue) { res.getNode().put(res.getName(), value); saveConfig(); } } catch (final JSONException ignored) { System.out.printf(CANNOT_SAVE_UNF_STRING, value, configKey.keyName); } } /** * Returns the String for given key. The one in the config, or if it does not exist, the default one. * * @param configKey The config key. * @return The String under the key, or default value. */ public String getString(final ConfigKey configKey) { return getString(configKey, configKey.getDefaultValue()); } /** * Returns the String for given key. The one in the config, or if it does not exist, the given default value. * * @param configKey The config key. * @param defaultValue The default value to choose if the key does not exist. * @return The String under the key, or default value. */ public String getString(final ConfigKey configKey, final String defaultValue) { try { final FindResult res = findNode(configKey.keyName, true); final String value = res.getNode().getString(res.getName()); return StringEscapeUtils.unescapeJson(value); } catch (final JSONException ignored) { //System.out.printf(CANNOT_FETCH_UNF_STRING, configKey.keyName, defaultValue); setString(configKey, defaultValue); return defaultValue; } } /** * Saves the given String under given key. * * @param configKey The config key. * @param value The value to save. */ public void setString(final ConfigKey configKey, final String value) { try { final FindResult res = findNode(configKey.keyName, true); // Set if value is different or if default value should be added if (!res.getNode().optString(res.getName(), "." + configKey.getDefaultValue()).equals(value)) { res.getNode().put(res.getName(), StringEscapeUtils.escapeJson(value)); saveConfig(); } } catch (final JSONException ignored) { System.out.printf(CANNOT_SAVE_UNF_STRING, value, configKey.keyName); } } /** * Returns the Int for given key. The one in the config, or if it does not exist, the default one. * * @param configKey The config key. * @return The Int under the key, or default value. */ public int getInt(final ConfigKey configKey) { return getInt(configKey, configKey.getDefaultValue()); } /** * Returns the Int for given key. The one in the config, or if it does not exist, the given default value. * * @param configKey The config key. * @param defaultValue The default value to choose if the key does not exist. * @return The Int under the key, or default value. */ public int getInt(final ConfigKey configKey, final int defaultValue) { try { final FindResult res = findNode(configKey.keyName, true); return res.getNode().getInt(res.getName()); } catch (final JSONException ignored) { //System.out.printf(CANNOT_FETCH_UNF_STRING, configKey.keyName, defaultValue); setInt(configKey, defaultValue); return defaultValue; } } /** * Saves the given Int under given key. * * @param configKey The config key. * @param value The value to save. */ public void setInt(final ConfigKey configKey, final int value) { try { final FindResult res = findNode(configKey.keyName, true); // Set if value is different or if default value should be added if (res.getNode().optInt(res.getName(), 1 + (int) configKey.getDefaultValue()) != value) { res.getNode().put(res.getName(), value); saveConfig(); } } catch (final JSONException ignored) { System.out.printf(CANNOT_SAVE_UNF_STRING, value, configKey.keyName); } } /** * Returns the Double for given key. The one in the config, or if it does not exist, the default one. * * @param configKey The config key. * @return The Double under the key, or default value. */ public double getDouble(final ConfigKey configKey) { return getDouble(configKey, configKey.getDefaultValue()); } /** * Returns the Double for given key. The one in the config, or if it does not exist, the given default value. * * @param configKey The config key. * @param defaultValue The default value to choose if the key does not exist. * @return The Double under the key, or default value. */ public double getDouble(final ConfigKey configKey, final double defaultValue) { try { final FindResult res = findNode(configKey.keyName, true); return res.getNode().getDouble(res.getName()); } catch (final JSONException ignored) { //System.out.printf(CANNOT_FETCH_UNF_STRING, configKey.keyName, defaultValue); setDouble(configKey, defaultValue); return defaultValue; } } /** * Saves the given Double under given key. * * @param configKey The config key. * @param value The value to save. */ public void setDouble(final ConfigKey configKey, final double value) { try { final FindResult res = findNode(configKey.keyName, true); if (res.getNode().optDouble(res.getName(), 1 + (double) configKey.getDefaultValue()) != value) { res.getNode().put(res.getName(), value); saveConfig(); } } catch (final JSONException ignored) { System.out.printf(CANNOT_SAVE_UNF_STRING, value, configKey.keyName); } } /** * Internal function to find a node with its value. * * @param path The path of the node. * @param create Weather or not the node should be created if it doesn't exist. * @return The Result for searching the node. */ private FindResult findNode(final String path, final boolean create) { checkModified(); final ArrayList<String> parts = new ArrayList<>(Arrays.asList(path.split("\\."))); JSONObject current = json; for (final String item : parts.subList(0, parts.size() - 1)) { if (!current.has(item) && create) { current.put(item, new JSONObject()); } current = current.getJSONObject(item); } return new FindResult(current, parts.get(parts.size() - 1)); } /** * Check if the file was modified. * (Different "lastModified" time) */ private void checkModified() { final long currentModifiedTime = file.lastModified(); if (currentModifiedTime != lastModified) { System.out.print("Modified config.json externally. Will be reloaded now."); // Re-read the file now final String content = FileHelper.readFile(file); if (content != null) { json = new JSONObject(content); } lastModified = currentModifiedTime; } } /** * Removes json nodes that do not belong in the file and fills it with missing values' defaults then. */ private void cleanUpAndFill() { ConfigKey[] keys = ConfigKey.values(); Map<ConfigKey, Object> allConfigs = new HashMap<>(keys.length); // Read all config values or take defaults for (ConfigKey configKey : keys) { Object value = getAsObject(configKey); allConfigs.put(configKey, value); } // Reset the json file and fill it again json = new JSONObject(); for (HashMap.Entry<ConfigKey, Object> entry : allConfigs.entrySet()) { setFromObject(entry.getKey(), entry.getValue()); } } /** * Delete the given key and its value. * * @param configKey The config key. */ public void delete(final ConfigKey configKey) { final FindResult res = findNode(configKey.keyName, false); res.getNode().remove(res.getName()); } /** * Save the config file. */ public void saveConfig() { FileHelper.saveFile(file, json.toString(FileHelper.INDENT)); lastModified = file.lastModified(); } /** * The Result which combines a node with it's value. */ private class FindResult { private final JSONObject node; private final String name; /** * Creates a Result object. * * @param node The node. * @param name The value. */ FindResult(final JSONObject node, final String name) { this.node = node; this.name = name; } /** * Returns the node. * * @return The node. */ public JSONObject getNode() { return this.node; } /** * Returns the name. * * @return The name. */ public String getName() { return this.name; } } }