New config
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
<includes>
|
<includes>
|
||||||
<include>*.yml</include>
|
<include>*.yml</include>
|
||||||
<include>lang/*.yml</include>
|
<include>lang/*.yml</include>
|
||||||
|
<include>credit/*.txt</include>
|
||||||
</includes>
|
</includes>
|
||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -62,5 +63,11 @@
|
|||||||
<version>1.7</version>
|
<version>1.7</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.checkerframework</groupId>
|
||||||
|
<artifactId>checker-qual</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import com.drtshock.playervaults.commands.DeleteCommand;
|
|||||||
import com.drtshock.playervaults.commands.SignCommand;
|
import com.drtshock.playervaults.commands.SignCommand;
|
||||||
import com.drtshock.playervaults.commands.SignSetInfo;
|
import com.drtshock.playervaults.commands.SignSetInfo;
|
||||||
import com.drtshock.playervaults.commands.VaultCommand;
|
import com.drtshock.playervaults.commands.VaultCommand;
|
||||||
|
import com.drtshock.playervaults.config.Loader;
|
||||||
|
import com.drtshock.playervaults.config.file.Config;
|
||||||
import com.drtshock.playervaults.listeners.Listeners;
|
import com.drtshock.playervaults.listeners.Listeners;
|
||||||
import com.drtshock.playervaults.listeners.SignListener;
|
import com.drtshock.playervaults.listeners.SignListener;
|
||||||
import com.drtshock.playervaults.listeners.VaultPreloadListener;
|
import com.drtshock.playervaults.listeners.VaultPreloadListener;
|
||||||
@@ -53,6 +55,7 @@ import org.bukkit.scheduler.BukkitRunnable;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -83,6 +86,7 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
private String _versionString;
|
private String _versionString;
|
||||||
private int maxVaultAmountPermTest;
|
private int maxVaultAmountPermTest;
|
||||||
private Metrics metrics;
|
private Metrics metrics;
|
||||||
|
private Config config = new Config();
|
||||||
|
|
||||||
public static PlayerVaults getInstance() {
|
public static PlayerVaults getInstance() {
|
||||||
return instance;
|
return instance;
|
||||||
@@ -105,7 +109,7 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
instance = this;
|
instance = this;
|
||||||
loadConfig();
|
loadConfig();
|
||||||
DEBUG = getConfig().getBoolean("debug", false);
|
DEBUG = getConf().isDebug();
|
||||||
debug("config", System.currentTimeMillis());
|
debug("config", System.currentTimeMillis());
|
||||||
uuidData = new File(this.getDataFolder(), "uuidvaults");
|
uuidData = new File(this.getDataFolder(), "uuidvaults");
|
||||||
vaultData = new File(this.getDataFolder(), "base64vaults");
|
vaultData = new File(this.getDataFolder(), "base64vaults");
|
||||||
@@ -123,8 +127,8 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
getServer().getPluginManager().registerEvents(new VaultPreloadListener(), this);
|
getServer().getPluginManager().registerEvents(new VaultPreloadListener(), this);
|
||||||
getServer().getPluginManager().registerEvents(new SignListener(this), this);
|
getServer().getPluginManager().registerEvents(new SignListener(this), this);
|
||||||
debug("registering listeners", System.currentTimeMillis());
|
debug("registering listeners", System.currentTimeMillis());
|
||||||
this.backupsEnabled = this.getConfig().getBoolean("backups.enabled", true);
|
this.backupsEnabled = this.getConf().getStorage().getFlatFile().isBackups();
|
||||||
this.maxVaultAmountPermTest = this.getConfig().getInt("max-vault-amount-perm-to-test", 99);
|
this.maxVaultAmountPermTest = this.getConf().getMaxVaultAmountPermTest();
|
||||||
loadSigns();
|
loadSigns();
|
||||||
debug("loaded signs", System.currentTimeMillis());
|
debug("loaded signs", System.currentTimeMillis());
|
||||||
debug("check update", System.currentTimeMillis());
|
debug("check update", System.currentTimeMillis());
|
||||||
@@ -136,8 +140,8 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
useVault = setupEconomy();
|
useVault = setupEconomy();
|
||||||
debug("setup economy", System.currentTimeMillis());
|
debug("setup economy", System.currentTimeMillis());
|
||||||
|
|
||||||
if (getConfig().getBoolean("cleanup.enable", false)) {
|
if (getConf().getPurge().isEnabled()) {
|
||||||
getServer().getScheduler().runTaskAsynchronously(this, new Cleanup(getConfig().getInt("cleanup.lastEdit", 30)));
|
getServer().getScheduler().runTaskAsynchronously(this, new Cleanup(getConf().getPurge().getDaysSinceLastEdit()));
|
||||||
}
|
}
|
||||||
|
|
||||||
new BukkitRunnable() {
|
new BukkitRunnable() {
|
||||||
@@ -200,14 +204,14 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.metricsSimplePie("signs", () -> getConfig().getBoolean("signs-enabled") ? "enabled" : "disabled");
|
this.metricsSimplePie("signs", () -> getConf().isSigns() ? "enabled" : "disabled");
|
||||||
this.metricsSimplePie("cleanup", () -> getConfig().getBoolean("cleanup.enable") ? "enabled" : "disabled");
|
this.metricsSimplePie("cleanup", () -> getConf().getPurge().isEnabled() ? "enabled" : "disabled");
|
||||||
this.metricsSimplePie("language", () -> getConfig().getString("language", "english"));
|
this.metricsSimplePie("language", () -> getConf().getLanguage());
|
||||||
|
|
||||||
this.metricsDrillPie("block_items", () -> {
|
this.metricsDrillPie("block_items", () -> {
|
||||||
Map<String, Map<String, Integer>> map = new HashMap<>();
|
Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||||
Map<String, Integer> entry = new HashMap<>();
|
Map<String, Integer> entry = new HashMap<>();
|
||||||
if (getConfig().getBoolean("blockitems")) {
|
if (getConf().getItemBlocking().isEnabled()) {
|
||||||
for (Material material : blockedMats) {
|
for (Material material : blockedMats) {
|
||||||
entry.put(material.toString(), 1);
|
entry.put(material.toString(), 1);
|
||||||
}
|
}
|
||||||
@@ -215,7 +219,7 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
if (entry.isEmpty()) {
|
if (entry.isEmpty()) {
|
||||||
entry.put("none", 1);
|
entry.put("none", 1);
|
||||||
}
|
}
|
||||||
map.put(getConfig().getBoolean("blockitems") ? "enabled" : "disabled", entry);
|
map.put(getConf().getItemBlocking().isEnabled() ? "enabled" : "disabled", entry);
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -265,7 +269,7 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getConfig().getBoolean("cleanup.enable", false)) {
|
if (getConf().getPurge().isEnabled()) {
|
||||||
saveSignsFile();
|
saveSignsFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,12 +301,27 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadConfig() {
|
private void loadConfig() {
|
||||||
saveDefaultConfig();
|
File configYaml = new File(this.getDataFolder(), "config.yml");
|
||||||
|
if (configYaml.exists()) {
|
||||||
|
this.config.setFromConfig(this.getConfig());
|
||||||
|
try {
|
||||||
|
Files.move(configYaml.toPath(), this.getDataFolder().toPath().resolve("old_unused_config.yml"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
this.getLogger().log(Level.SEVERE, "Failed to move config for backup", e);
|
||||||
|
configYaml.deleteOnExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Loader.loadAndSave("config", this.config);
|
||||||
|
} catch (IOException | IllegalAccessException e) {
|
||||||
|
this.getLogger().log(Level.SEVERE, "Could not load config.", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Clear just in case this is a reload.
|
// Clear just in case this is a reload.
|
||||||
blockedMats.clear();
|
blockedMats.clear();
|
||||||
if (getConfig().getBoolean("blockitems", false) && getConfig().contains("blocked-items")) {
|
if (getConf().getItemBlocking().isEnabled()) {
|
||||||
for (String s : getConfig().getStringList("blocked-items")) {
|
for (String s : getConf().getItemBlocking().getList()) {
|
||||||
Material mat = Material.matchMaterial(s);
|
Material mat = Material.matchMaterial(s);
|
||||||
if (mat != null) {
|
if (mat != null) {
|
||||||
blockedMats.add(mat);
|
blockedMats.add(mat);
|
||||||
@@ -312,6 +331,10 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Config getConf() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadSigns() {
|
private void loadSigns() {
|
||||||
File signs = new File(getDataFolder(), "signs.yml");
|
File signs = new File(getDataFolder(), "signs.yml");
|
||||||
if (!signs.exists()) {
|
if (!signs.exists()) {
|
||||||
@@ -328,7 +351,7 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void reloadSigns() {
|
private void reloadSigns() {
|
||||||
if (!getConfig().getBoolean("signs-enabled")) {
|
if (!getConf().isSigns()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!signsFile.exists()) loadSigns();
|
if (!signsFile.exists()) loadSigns();
|
||||||
@@ -358,7 +381,7 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void saveSignsFile() {
|
private void saveSignsFile() {
|
||||||
if (!getConfig().getBoolean("signs-enabled")) {
|
if (!getConf().isSigns()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,24 +395,13 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an object in the config.yml
|
|
||||||
*
|
|
||||||
* @param path The path in the config.
|
|
||||||
* @param object What to be saved.
|
|
||||||
* @param conf Where to save the object.
|
|
||||||
*/
|
|
||||||
public <T> void setInConfig(String path, T object, YamlConfiguration conf) {
|
|
||||||
conf.set(path, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadLang() {
|
public void loadLang() {
|
||||||
File folder = new File(getDataFolder(), "lang");
|
File folder = new File(getDataFolder(), "lang");
|
||||||
if (!folder.exists()) {
|
if (!folder.exists()) {
|
||||||
folder.mkdir();
|
folder.mkdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
String definedLanguage = getConfig().getString("language", "english");
|
String definedLanguage = getConf().getLanguage();
|
||||||
|
|
||||||
// Save as default just incase.
|
// Save as default just incase.
|
||||||
File english = null;
|
File english = null;
|
||||||
@@ -444,7 +456,7 @@ public class PlayerVaults extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEconomyEnabled() {
|
public boolean isEconomyEnabled() {
|
||||||
return this.getConfig().getBoolean("economy.enabled", false) && this.useVault;
|
return this.getConf().getEconomy().isEnabled() && this.useVault;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getVaultData() {
|
public File getVaultData() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class SignCommand implements CommandExecutor {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
if (sender.hasPermission("playervaults.signs.set")) {
|
if (sender.hasPermission("playervaults.signs.set")) {
|
||||||
if (!PlayerVaults.getInstance().getConfig().getBoolean("signs-enabled")) {
|
if (!PlayerVaults.getInstance().getConf().isSigns()) {
|
||||||
sender.sendMessage(Lang.TITLE.toString() + Lang.SIGNS_DISABLED.toString());
|
sender.sendMessage(Lang.TITLE.toString() + Lang.SIGNS_DISABLED.toString());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* PlayerVaultsX
|
||||||
|
* Copyright (C) 2013 Trent Hensler, Laxwashere, CmdrKittens
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.config;
|
||||||
|
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.PlayerVaults;
|
||||||
|
import com.drtshock.playervaults.config.file.Config;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class ConfigManager {
|
||||||
|
private final PlayerVaults plugin;
|
||||||
|
private final Config config = new Config();
|
||||||
|
|
||||||
|
public ConfigManager(@NonNull PlayerVaults plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadConfig() {
|
||||||
|
try {
|
||||||
|
Loader.loadAndSave("main", this.config);
|
||||||
|
} catch (IOException | IllegalAccessException e) {
|
||||||
|
this.plugin.getLogger().log(Level.SEVERE, "Could not load config. Using all default values until resolved.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Config getConf() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* PlayerVaultsX
|
||||||
|
* Copyright (C) 2013 Trent Hensler, Laxwashere, CmdrKittens
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.config;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.PlayerVaults;
|
||||||
|
import com.drtshock.playervaults.config.annotation.WipeOnReload;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.Config;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigFactory;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueFactory;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import com.drtshock.playervaults.config.annotation.Comment;
|
||||||
|
import com.drtshock.playervaults.config.annotation.ConfigName;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Loader {
|
||||||
|
public static void loadAndSave(@NonNull String fileName, @NonNull Object config) throws IOException, IllegalAccessException {
|
||||||
|
File file = Loader.getFile(fileName);
|
||||||
|
Loader.loadAndSave(file, Loader.getConf(file), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NonNull File getFile(@NonNull String file) {
|
||||||
|
Path configFolder = PlayerVaults.getInstance().getDataFolder().toPath();
|
||||||
|
if (!configFolder.toFile().exists()) {
|
||||||
|
configFolder.toFile().mkdir();
|
||||||
|
}
|
||||||
|
Path path = configFolder.resolve(file + ".conf");
|
||||||
|
return path.toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NonNull Config getConf(@NonNull File file) {
|
||||||
|
return ConfigFactory.parseFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadAndSave(@NonNull File file, @NonNull Config config, @NonNull Object configObject) throws IOException, IllegalAccessException {
|
||||||
|
ConfigValue value = Loader.loadNode(config, configObject);
|
||||||
|
String s = value.render(ConfigRenderOptions.defaults().setOriginComments(false).setComments(true).setJson(false));
|
||||||
|
Files.write(file.toPath(), s.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigValue load(Config config, Object configObject) throws IOException, IllegalAccessException {
|
||||||
|
return Loader.loadNode(config, configObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<Class<?>> types = new HashSet<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
Loader.types.add(Boolean.TYPE);
|
||||||
|
Loader.types.add(Byte.TYPE);
|
||||||
|
Loader.types.add(Character.TYPE);
|
||||||
|
Loader.types.add(Double.TYPE);
|
||||||
|
Loader.types.add(Float.TYPE);
|
||||||
|
Loader.types.add(Integer.TYPE);
|
||||||
|
Loader.types.add(Long.TYPE);
|
||||||
|
Loader.types.add(Short.TYPE);
|
||||||
|
Loader.types.add(List.class);
|
||||||
|
Loader.types.add(Map.class);
|
||||||
|
Loader.types.add(Set.class);
|
||||||
|
Loader.types.add(String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull ConfigValue loadNode(@NonNull Config current, @NonNull Object object) throws IllegalAccessException {
|
||||||
|
Map<String, ConfigValue> map = new HashMap<>();
|
||||||
|
for (Field field : Loader.getFields(object.getClass())) {
|
||||||
|
if (field.isSynthetic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((field.getModifiers() & Modifier.TRANSIENT) != 0) {
|
||||||
|
if (field.getAnnotation(WipeOnReload.class) != null) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(object, null);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
ConfigName configName = field.getAnnotation(ConfigName.class);
|
||||||
|
Comment comment = field.getAnnotation(Comment.class);
|
||||||
|
String confName = configName == null || configName.value().isEmpty() ? field.getName() : configName.value();
|
||||||
|
ConfigValue curValue = Loader.getOrNull(current, confName);
|
||||||
|
boolean needsValue = curValue == null;
|
||||||
|
|
||||||
|
ConfigValue newValue;
|
||||||
|
Object defaultValue = field.get(object);
|
||||||
|
if (Loader.types.contains(field.getType())) {
|
||||||
|
if (needsValue) {
|
||||||
|
newValue = ConfigValueFactory.fromAnyRef(defaultValue);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
if (Set.class.isAssignableFrom(field.getType()) && curValue.valueType() == ConfigValueType.LIST) {
|
||||||
|
field.set(object, new HashSet<Object>((List<?>) curValue.unwrapped()));
|
||||||
|
} else {
|
||||||
|
field.set(object, curValue.unwrapped());
|
||||||
|
}
|
||||||
|
newValue = curValue;
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
System.out.println("Found incorrect type for " + confName + ": Expected " + field.getType() + ", found " + curValue.unwrapped().getClass());
|
||||||
|
field.set(object, defaultValue);
|
||||||
|
newValue = ConfigValueFactory.fromAnyRef(defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newValue = Loader.loadNode(current, defaultValue);
|
||||||
|
}
|
||||||
|
if (comment != null) {
|
||||||
|
newValue = newValue.withOrigin(newValue.origin().withComments(Arrays.asList(comment.value().split("\n"))));
|
||||||
|
}
|
||||||
|
map.put(confName, newValue);
|
||||||
|
}
|
||||||
|
return ConfigValueFactory.fromMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable ConfigValue getOrNull(@NonNull Config config, @NonNull String path) {
|
||||||
|
return config.hasPath(path) ? config.getValue(path) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull List<Field> getFields(Class<?> clazz) {
|
||||||
|
return Loader.getFields(new ArrayList<>(), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull List<Field> getFields(@NonNull List<Field> fields, @NonNull Class<?> clazz) {
|
||||||
|
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
|
||||||
|
|
||||||
|
if (clazz.getSuperclass() != null) {
|
||||||
|
Loader.getFields(fields, clazz.getSuperclass());
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* PlayerVaultsX
|
||||||
|
* Copyright (C) 2013 Trent Hensler, Laxwashere, CmdrKittens
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.config.annotation;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Comment {
|
||||||
|
@NonNull String value();
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* PlayerVaultsX
|
||||||
|
* Copyright (C) 2013 Trent Hensler, Laxwashere, CmdrKittens
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.config.annotation;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface ConfigName {
|
||||||
|
@NonNull String value();
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* PlayerVaultsX
|
||||||
|
* Copyright (C) 2013 Trent Hensler, Laxwashere, CmdrKittens
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.config.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface WipeOnReload {
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* PlayerVaultsX
|
||||||
|
* Copyright (C) 2013 Trent Hensler, Laxwashere, CmdrKittens
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.config.file;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.config.annotation.Comment;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings({"FieldCanBeLocal", "InnerClassMayBeStatic", "unused"})
|
||||||
|
public class Config {
|
||||||
|
public class Block {
|
||||||
|
private boolean enabled = true;
|
||||||
|
@Comment("Material list for blocked items (does not support ID's), only effective if the feature is enabled.\n" +
|
||||||
|
" If you don't know material names: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html")
|
||||||
|
private List<String> list = new ArrayList<String>() {
|
||||||
|
{
|
||||||
|
this.add("PUMPKIN");
|
||||||
|
this.add("DIAMOND_BLOCK");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getList() {
|
||||||
|
if (this.list == null) {
|
||||||
|
this.list = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Economy {
|
||||||
|
@Comment("Set me to true to enable economy features!")
|
||||||
|
private boolean enabled = false;
|
||||||
|
private double feeToCreate = 100;
|
||||||
|
private double feeToOpen = 10;
|
||||||
|
private double refundOnDelete = 50;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFeeToCreate() {
|
||||||
|
return this.feeToCreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFeeToOpen() {
|
||||||
|
return this.feeToOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRefundOnDelete() {
|
||||||
|
return this.refundOnDelete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurgePlanet {
|
||||||
|
private boolean enabled = false;
|
||||||
|
@Comment("Time, in days, since last edit")
|
||||||
|
private int daysSinceLastEdit = 30;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDaysSinceLastEdit() {
|
||||||
|
return this.daysSinceLastEdit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Storage {
|
||||||
|
public class FlatFile {
|
||||||
|
@Comment("Backups\n" +
|
||||||
|
" Enabling this will create backups of vaults automagically.")
|
||||||
|
private boolean backups = true;
|
||||||
|
|
||||||
|
public boolean isBackups() {
|
||||||
|
return this.backups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FlatFile flatFile = new FlatFile();
|
||||||
|
private String storageType = "flatfile";
|
||||||
|
|
||||||
|
public FlatFile getFlatFile() {
|
||||||
|
return this.flatFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStorageType() {
|
||||||
|
return this.storageType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Comment("PlayerVaults\n" +
|
||||||
|
"Created by: https://github.com/drtshock/PlayerVaults/graphs/contributors/\n" +
|
||||||
|
"Resource page: https://www.spigotmc.org/resources/51204/\n" +
|
||||||
|
"Discord server: https://discordapp.com/invite/JZcWDEt/\n" +
|
||||||
|
"Made with love <3")
|
||||||
|
private boolean aPleasantHello=true;
|
||||||
|
|
||||||
|
@Comment("Debug Mode\n" +
|
||||||
|
" This will print everything the plugin is doing to console.\n" +
|
||||||
|
" You should only enable this if you're working with a contributor to fix something.")
|
||||||
|
private boolean debug = false;
|
||||||
|
|
||||||
|
@Comment("Language\n" +
|
||||||
|
" This determines which language file the plugin will read from.\n" +
|
||||||
|
" Valid options are (don't include .yml): bulgarian, dutch, english, german, turkish, russian")
|
||||||
|
private String language = "english";
|
||||||
|
|
||||||
|
@Comment("Signs\n" +
|
||||||
|
" This will determine whether vault signs are enabled.\n" +
|
||||||
|
" If you don't know what this is or if it's for you, see the resource page.")
|
||||||
|
private boolean signs = false;
|
||||||
|
|
||||||
|
@Comment("Economy\n" +
|
||||||
|
" These are all of the settings for the economy integration. (Requires Vault)\n" +
|
||||||
|
" Bypass permission is: playervaults.free")
|
||||||
|
private Economy economy = new Economy();
|
||||||
|
|
||||||
|
@Comment("Blocked Items\n" +
|
||||||
|
" This will allow you to block specific materials from vaults.\n" +
|
||||||
|
" Bypass permission is: playervaults.bypassblockeditems")
|
||||||
|
private Block itemBlocking = new Block();
|
||||||
|
|
||||||
|
@Comment("Cleanup\n" +
|
||||||
|
" Enabling this will purge vaults that haven't been touched in the specified time frame.\n" +
|
||||||
|
" Reminder: This is only checked during startup.\n" +
|
||||||
|
" This will not lag your server or touch the backups folder.")
|
||||||
|
private PurgePlanet purge = new PurgePlanet();
|
||||||
|
|
||||||
|
@Comment("Sets the highest vault amount this plugin will test perms for")
|
||||||
|
private int maxVaultAmountPermTest = 99;
|
||||||
|
|
||||||
|
@Comment("Storage option. Currently only flatfile, but soon more! :)")
|
||||||
|
private Storage storage = new Storage();
|
||||||
|
|
||||||
|
public void setFromConfig(FileConfiguration c) {
|
||||||
|
this.debug = c.getBoolean("debug", false);
|
||||||
|
this.language = c.getString("language", "english");
|
||||||
|
this.signs = c.getBoolean("signs-enabled", false);
|
||||||
|
this.economy.enabled = c.getBoolean("economy.enabled", false);
|
||||||
|
this.economy.feeToCreate = c.getDouble("economy.cost-to-create", 100);
|
||||||
|
this.economy.feeToOpen = c.getDouble("economy.cost-to-open", 10);
|
||||||
|
this.economy.refundOnDelete = c.getDouble("economy.refund-on-delete", 50);
|
||||||
|
this.itemBlocking.enabled = c.getBoolean("blockitems", true);
|
||||||
|
this.itemBlocking.list = c.getStringList("blocked-items");
|
||||||
|
if (this.itemBlocking.list == null) {
|
||||||
|
this.itemBlocking.list = new ArrayList<>();
|
||||||
|
this.itemBlocking.list.add("PUMPKIN");
|
||||||
|
this.itemBlocking.list.add("DIAMOND_BLOCK");
|
||||||
|
}
|
||||||
|
this.purge.enabled = c.getBoolean("cleanup.enable", false);
|
||||||
|
this.purge.daysSinceLastEdit = c.getInt("cleanup.lastEdit", 30);
|
||||||
|
this.storage.flatFile.backups = c.getBoolean("backups.enabled", true);
|
||||||
|
this.maxVaultAmountPermTest = c.getInt("max-vault-amount-perm-to-test", 99);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDebug() {
|
||||||
|
return this.debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return this.language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSigns() {
|
||||||
|
return this.signs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Economy getEconomy() {
|
||||||
|
return this.economy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Block getItemBlocking() {
|
||||||
|
return this.itemBlocking;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PurgePlanet getPurge() {
|
||||||
|
return this.purge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxVaultAmountPermTest() {
|
||||||
|
return this.maxVaultAmountPermTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Storage getStorage() {
|
||||||
|
return this.storage;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigBeanImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for automatically creating a Java class from a {@link Config}.
|
||||||
|
* See {@link #create(Config,Class)}.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
public class ConfigBeanFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of a class, initializing its fields from a {@link Config}.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Config configSource = ConfigFactory.load().getConfig("foo");
|
||||||
|
* FooConfig config = ConfigBeanFactory.create(configSource, FooConfig.class);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The Java class should follow JavaBean conventions. Field types
|
||||||
|
* can be any of the types you can normally get from a {@link
|
||||||
|
* Config}, including <code>java.time.Duration</code> or {@link
|
||||||
|
* ConfigMemorySize}. Fields may also be another JavaBean-style
|
||||||
|
* class.
|
||||||
|
*
|
||||||
|
* Fields are mapped to config by converting the config key to
|
||||||
|
* camel case. So the key <code>foo-bar</code> becomes JavaBean
|
||||||
|
* setter <code>setFooBar</code>.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param config source of config information
|
||||||
|
* @param clazz class to be instantiated
|
||||||
|
* @param <T> the type of the class to be instantiated
|
||||||
|
* @return an instance of the class populated with data from the config
|
||||||
|
* @throws ConfigException.BadBean
|
||||||
|
* If something is wrong with the JavaBean
|
||||||
|
* @throws ConfigException.ValidationFailed
|
||||||
|
* If the config doesn't conform to the bean's implied schema
|
||||||
|
* @throws ConfigException
|
||||||
|
* Can throw the same exceptions as the getters on <code>Config</code>
|
||||||
|
*/
|
||||||
|
public static <T> T create(Config config, Class<T> clazz) {
|
||||||
|
return ConfigBeanImpl.createInternal(config, clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,447 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigImplUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All exceptions thrown by the library are subclasses of
|
||||||
|
* <code>ConfigException</code>.
|
||||||
|
*/
|
||||||
|
public abstract class ConfigException extends RuntimeException implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
final private transient ConfigOrigin origin;
|
||||||
|
|
||||||
|
protected ConfigException(ConfigOrigin origin, String message,
|
||||||
|
Throwable cause) {
|
||||||
|
super(origin.description() + ": " + message, cause);
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigException(ConfigOrigin origin, String message) {
|
||||||
|
this(origin.description() + ": " + message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.origin = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigException(String message) {
|
||||||
|
this(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an "origin" (such as a filename and line number) for the
|
||||||
|
* exception, or null if none is available. If there's no sensible origin
|
||||||
|
* for a given exception, or the kind of exception doesn't meaningfully
|
||||||
|
* relate to a particular origin file, this returns null. Never assume this
|
||||||
|
* will return non-null, it can always return null.
|
||||||
|
*
|
||||||
|
* @return origin of the problem, or null if unknown/inapplicable
|
||||||
|
*/
|
||||||
|
public ConfigOrigin origin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we customize serialization because ConfigOrigin isn't
|
||||||
|
// serializable and we don't want it to be (don't want to
|
||||||
|
// support it)
|
||||||
|
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
|
||||||
|
out.defaultWriteObject();
|
||||||
|
ConfigImplUtil.writeOrigin(out, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For deserialization - uses reflection to set the final origin field on the object
|
||||||
|
private static <T> void setOriginField(T hasOriginField, Class<T> clazz,
|
||||||
|
ConfigOrigin origin) throws IOException {
|
||||||
|
// circumvent "final"
|
||||||
|
Field f;
|
||||||
|
try {
|
||||||
|
f = clazz.getDeclaredField("origin");
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new IOException(clazz.getSimpleName() + " has no origin field?", e);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
throw new IOException("unable to fill out origin field in " +
|
||||||
|
clazz.getSimpleName(), e);
|
||||||
|
}
|
||||||
|
f.setAccessible(true);
|
||||||
|
try {
|
||||||
|
f.set(hasOriginField, origin);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IOException("unable to set origin field", e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IOException("unable to set origin field", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readObject(java.io.ObjectInputStream in) throws IOException,
|
||||||
|
ClassNotFoundException {
|
||||||
|
in.defaultReadObject();
|
||||||
|
ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
|
||||||
|
setOriginField(this, ConfigException.class, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that the type of a value does not match the type you
|
||||||
|
* requested.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class WrongType extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public WrongType(ConfigOrigin origin, String path, String expected, String actual,
|
||||||
|
Throwable cause) {
|
||||||
|
super(origin, path + " has type " + actual + " rather than " + expected, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WrongType(ConfigOrigin origin, String path, String expected, String actual) {
|
||||||
|
this(origin, path, expected, actual, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WrongType(ConfigOrigin origin, String message, Throwable cause) {
|
||||||
|
super(origin, message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WrongType(ConfigOrigin origin, String message) {
|
||||||
|
super(origin, message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicates that the setting was never set to anything, not even
|
||||||
|
* null.
|
||||||
|
*/
|
||||||
|
public static class Missing extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public Missing(String path, Throwable cause) {
|
||||||
|
super("No configuration setting found for key '" + path + "'",
|
||||||
|
cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Missing(String path) {
|
||||||
|
this(path, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Missing(ConfigOrigin origin, String message, Throwable cause) {
|
||||||
|
super(origin, message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Missing(ConfigOrigin origin, String message) {
|
||||||
|
this(origin, message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicates that the setting was treated as missing because it
|
||||||
|
* was set to null.
|
||||||
|
*/
|
||||||
|
public static class Null extends Missing {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static String makeMessage(String path, String expected) {
|
||||||
|
if (expected != null) {
|
||||||
|
return "Configuration key '" + path
|
||||||
|
+ "' is set to null but expected " + expected;
|
||||||
|
} else {
|
||||||
|
return "Configuration key '" + path + "' is null";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Null(ConfigOrigin origin, String path, String expected,
|
||||||
|
Throwable cause) {
|
||||||
|
super(origin, makeMessage(path, expected), cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Null(ConfigOrigin origin, String path, String expected) {
|
||||||
|
this(origin, path, expected, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that a value was messed up, for example you may have
|
||||||
|
* asked for a duration and the value can't be sensibly parsed as a
|
||||||
|
* duration.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class BadValue extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BadValue(ConfigOrigin origin, String path, String message,
|
||||||
|
Throwable cause) {
|
||||||
|
super(origin, "Invalid value at '" + path + "': " + message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadValue(ConfigOrigin origin, String path, String message) {
|
||||||
|
this(origin, path, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadValue(String path, String message, Throwable cause) {
|
||||||
|
super("Invalid value at '" + path + "': " + message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadValue(String path, String message) {
|
||||||
|
this(path, message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that a path expression was invalid. Try putting
|
||||||
|
* double quotes around path elements that contain "special" characters.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class BadPath extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BadPath(ConfigOrigin origin, String path, String message,
|
||||||
|
Throwable cause) {
|
||||||
|
super(origin,
|
||||||
|
path != null ? ("Invalid path '" + path + "': " + message)
|
||||||
|
: message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadPath(ConfigOrigin origin, String path, String message) {
|
||||||
|
this(origin, path, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadPath(String path, String message, Throwable cause) {
|
||||||
|
super(path != null ? ("Invalid path '" + path + "': " + message)
|
||||||
|
: message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadPath(String path, String message) {
|
||||||
|
this(path, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadPath(ConfigOrigin origin, String message) {
|
||||||
|
this(origin, null, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that there's a bug in something (possibly the
|
||||||
|
* library itself) or the runtime environment is broken. This exception
|
||||||
|
* should never be handled; instead, something should be fixed to keep the
|
||||||
|
* exception from occurring. This exception can be thrown by any method in
|
||||||
|
* the library.
|
||||||
|
*/
|
||||||
|
public static class BugOrBroken extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BugOrBroken(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BugOrBroken(String message) {
|
||||||
|
this(message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that there was an IO error.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class IO extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public IO(ConfigOrigin origin, String message, Throwable cause) {
|
||||||
|
super(origin, message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IO(ConfigOrigin origin, String message) {
|
||||||
|
this(origin, message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that there was a parse error.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class Parse extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public Parse(ConfigOrigin origin, String message, Throwable cause) {
|
||||||
|
super(origin, message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parse(ConfigOrigin origin, String message) {
|
||||||
|
this(origin, message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that a substitution did not resolve to anything.
|
||||||
|
* Thrown by {@link Config#resolve}.
|
||||||
|
*/
|
||||||
|
public static class UnresolvedSubstitution extends Parse {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UnresolvedSubstitution(ConfigOrigin origin, String detail, Throwable cause) {
|
||||||
|
super(origin, "Could not resolve substitution to a value: " + detail, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnresolvedSubstitution(ConfigOrigin origin, String detail) {
|
||||||
|
this(origin, detail, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that you tried to use a function that requires
|
||||||
|
* substitutions to be resolved, but substitutions have not been resolved
|
||||||
|
* (that is, {@link Config#resolve} was not called). This is always a bug in
|
||||||
|
* either application code or the library; it's wrong to write a handler for
|
||||||
|
* this exception because you should be able to fix the code to avoid it by
|
||||||
|
* adding calls to {@link Config#resolve}.
|
||||||
|
*/
|
||||||
|
public static class NotResolved extends BugOrBroken {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public NotResolved(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotResolved(String message) {
|
||||||
|
this(message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a problem that occurred in {@link Config#checkValid}. A
|
||||||
|
* {@link ConfigException.ValidationFailed} exception thrown from
|
||||||
|
* <code>checkValid()</code> includes a list of problems encountered.
|
||||||
|
*/
|
||||||
|
public static class ValidationProblem implements Serializable {
|
||||||
|
|
||||||
|
final private String path;
|
||||||
|
final private transient ConfigOrigin origin;
|
||||||
|
final private String problem;
|
||||||
|
|
||||||
|
public ValidationProblem(String path, ConfigOrigin origin, String problem) {
|
||||||
|
this.path = path;
|
||||||
|
this.origin = origin;
|
||||||
|
this.problem = problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the config setting causing the problem.
|
||||||
|
* @return the path of the problem setting
|
||||||
|
*/
|
||||||
|
public String path() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns where the problem occurred (origin may include info on the
|
||||||
|
* file, line number, etc.).
|
||||||
|
* @return the origin of the problem setting
|
||||||
|
*/
|
||||||
|
public ConfigOrigin origin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a description of the problem.
|
||||||
|
* @return description of the problem
|
||||||
|
*/
|
||||||
|
public String problem() {
|
||||||
|
return problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We customize serialization because ConfigOrigin isn't
|
||||||
|
// serializable and we don't want it to be
|
||||||
|
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
|
||||||
|
out.defaultWriteObject();
|
||||||
|
ConfigImplUtil.writeOrigin(out, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readObject(java.io.ObjectInputStream in) throws IOException,
|
||||||
|
ClassNotFoundException {
|
||||||
|
in.defaultReadObject();
|
||||||
|
ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
|
||||||
|
setOriginField(this, ValidationProblem.class, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ValidationProblem(" + path + "," + origin + "," + problem + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that {@link Config#checkValid} found validity
|
||||||
|
* problems. The problems are available via the {@link #problems()} method.
|
||||||
|
* The <code>getMessage()</code> of this exception is a potentially very
|
||||||
|
* long string listing all the problems found.
|
||||||
|
*/
|
||||||
|
public static class ValidationFailed extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
final private Iterable<ValidationProblem> problems;
|
||||||
|
|
||||||
|
public ValidationFailed(Iterable<ValidationProblem> problems) {
|
||||||
|
super(makeMessage(problems), null);
|
||||||
|
this.problems = problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<ValidationProblem> problems() {
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String makeMessage(Iterable<ValidationProblem> problems) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (ValidationProblem p : problems) {
|
||||||
|
sb.append(p.origin().description());
|
||||||
|
sb.append(": ");
|
||||||
|
sb.append(p.path());
|
||||||
|
sb.append(": ");
|
||||||
|
sb.append(p.problem());
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
if (sb.length() == 0)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"ValidationFailed must have a non-empty list of problems");
|
||||||
|
sb.setLength(sb.length() - 2); // chop comma and space
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some problem with a JavaBean we are trying to initialize.
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
public static class BadBean extends BugOrBroken {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BadBean(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadBean(String message) {
|
||||||
|
this(message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception that doesn't fall into any other category.
|
||||||
|
*/
|
||||||
|
public static class Generic extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public Generic(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Generic(String message) {
|
||||||
|
this(message, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+56
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context provided to a {@link ConfigIncluder}; this interface is only useful
|
||||||
|
* inside a {@code ConfigIncluder} implementation, and is not intended for apps
|
||||||
|
* to implement.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement this interface</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*/
|
||||||
|
public interface ConfigIncludeContext {
|
||||||
|
/**
|
||||||
|
* Tries to find a name relative to whatever is doing the including, for
|
||||||
|
* example in the same directory as the file doing the including. Returns
|
||||||
|
* null if it can't meaningfully create a relative name. The returned
|
||||||
|
* parseable may not exist; this function is not required to do any IO, just
|
||||||
|
* compute what the name would be.
|
||||||
|
*
|
||||||
|
* The passed-in filename has to be a complete name (with extension), not
|
||||||
|
* just a basename. (Include statements in config files are allowed to give
|
||||||
|
* just a basename.)
|
||||||
|
*
|
||||||
|
* @param filename
|
||||||
|
* the name to make relative to the resource doing the including
|
||||||
|
* @return parseable item relative to the resource doing the including, or
|
||||||
|
* null
|
||||||
|
*/
|
||||||
|
ConfigParseable relativeTo(String filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse options to use (if you use another method to get a
|
||||||
|
* {@link ConfigParseable} then use {@link ConfigParseable#options()}
|
||||||
|
* instead though).
|
||||||
|
*
|
||||||
|
* @return the parse options
|
||||||
|
*/
|
||||||
|
ConfigParseOptions parseOptions();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy this {@link ConfigIncludeContext} giving it a new value for its parseOptions.
|
||||||
|
*
|
||||||
|
* @param options new parse options to use
|
||||||
|
*
|
||||||
|
* @return the updated copy of this context
|
||||||
|
*/
|
||||||
|
ConfigIncludeContext setParseOptions(ConfigParseOptions options);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this interface and provide an instance to
|
||||||
|
* {@link ConfigParseOptions#setIncluder ConfigParseOptions.setIncluder()} to
|
||||||
|
* customize handling of {@code include} statements in config files. You may
|
||||||
|
* also want to implement {@link ConfigIncluderClasspath},
|
||||||
|
* {@link ConfigIncluderFile}, and {@link ConfigIncluderURL}, or not.
|
||||||
|
*/
|
||||||
|
public interface ConfigIncluder {
|
||||||
|
/**
|
||||||
|
* Returns a new includer that falls back to the given includer. This is how
|
||||||
|
* you can obtain the default includer; it will be provided as a fallback.
|
||||||
|
* It's up to your includer to chain to it if you want to. You might want to
|
||||||
|
* merge any files found by the fallback includer with any objects you load
|
||||||
|
* yourself.
|
||||||
|
*
|
||||||
|
* It's important to handle the case where you already have the fallback
|
||||||
|
* with a "return this", i.e. this method should not create a new object if
|
||||||
|
* the fallback is the same one you already have. The same fallback may be
|
||||||
|
* added repeatedly.
|
||||||
|
*
|
||||||
|
* @param fallback the previous includer for chaining
|
||||||
|
* @return a new includer
|
||||||
|
*/
|
||||||
|
ConfigIncluder withFallback(ConfigIncluder fallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses another item to be included. The returned object typically would
|
||||||
|
* not have substitutions resolved. You can throw a ConfigException here to
|
||||||
|
* abort parsing, or return an empty object, but may not return null.
|
||||||
|
*
|
||||||
|
* This method is used for a "heuristic" include statement that does not
|
||||||
|
* specify file, URL, or classpath resource. If the include statement does
|
||||||
|
* specify, then the same class implementing {@link ConfigIncluder} must
|
||||||
|
* also implement {@link ConfigIncluderClasspath},
|
||||||
|
* {@link ConfigIncluderFile}, or {@link ConfigIncluderURL} as needed, or a
|
||||||
|
* default includer will be used.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* some info about the include context
|
||||||
|
* @param what
|
||||||
|
* the include statement's argument
|
||||||
|
* @return a non-null ConfigObject
|
||||||
|
*/
|
||||||
|
ConfigObject include(ConfigIncludeContext context, String what);
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this <em>in addition to</em> {@link ConfigIncluder} if you want to
|
||||||
|
* support inclusion of files with the {@code include classpath("resource")}
|
||||||
|
* syntax. If you do not implement this but do implement {@link ConfigIncluder},
|
||||||
|
* attempts to load classpath resources will use the default includer.
|
||||||
|
*/
|
||||||
|
public interface ConfigIncluderClasspath {
|
||||||
|
/**
|
||||||
|
* Parses another item to be included. The returned object typically would
|
||||||
|
* not have substitutions resolved. You can throw a ConfigException here to
|
||||||
|
* abort parsing, or return an empty object, but may not return null.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* some info about the include context
|
||||||
|
* @param what
|
||||||
|
* the include statement's argument
|
||||||
|
* @return a non-null ConfigObject
|
||||||
|
*/
|
||||||
|
ConfigObject includeResources(ConfigIncludeContext context, String what);
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this <em>in addition to</em> {@link ConfigIncluder} if you want to
|
||||||
|
* support inclusion of files with the {@code include file("filename")} syntax.
|
||||||
|
* If you do not implement this but do implement {@link ConfigIncluder},
|
||||||
|
* attempts to load files will use the default includer.
|
||||||
|
*/
|
||||||
|
public interface ConfigIncluderFile {
|
||||||
|
/**
|
||||||
|
* Parses another item to be included. The returned object typically would
|
||||||
|
* not have substitutions resolved. You can throw a ConfigException here to
|
||||||
|
* abort parsing, or return an empty object, but may not return null.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* some info about the include context
|
||||||
|
* @param what
|
||||||
|
* the include statement's argument
|
||||||
|
* @return a non-null ConfigObject
|
||||||
|
*/
|
||||||
|
ConfigObject includeFile(ConfigIncludeContext context, File what);
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this <em>in addition to</em> {@link ConfigIncluder} if you want to
|
||||||
|
* support inclusion of files with the {@code include url("http://example.com")}
|
||||||
|
* syntax. If you do not implement this but do implement {@link ConfigIncluder},
|
||||||
|
* attempts to load URLs will use the default includer.
|
||||||
|
*/
|
||||||
|
public interface ConfigIncluderURL {
|
||||||
|
/**
|
||||||
|
* Parses another item to be included. The returned object typically would
|
||||||
|
* not have substitutions resolved. You can throw a ConfigException here to
|
||||||
|
* abort parsing, or return an empty object, but may not return null.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* some info about the include context
|
||||||
|
* @param what
|
||||||
|
* the include statement's argument
|
||||||
|
* @return a non-null ConfigObject
|
||||||
|
*/
|
||||||
|
ConfigObject includeURL(ConfigIncludeContext context, URL what);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtype of {@link ConfigValue} representing a list value, as in JSON's
|
||||||
|
* {@code [1,2,3]} syntax.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@code ConfigList} implements {@code java.util.List<ConfigValue>} so you can
|
||||||
|
* use it like a regular Java list. Or call {@link #unwrapped()} to unwrap the
|
||||||
|
* list elements into plain Java values.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Like all {@link ConfigValue} subtypes, {@code ConfigList} is immutable. This
|
||||||
|
* makes it threadsafe and you never have to create "defensive copies." The
|
||||||
|
* mutator methods from {@link java.util.List} all throw
|
||||||
|
* {@link java.lang.UnsupportedOperationException}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The {@link ConfigValue#valueType} method on a list returns
|
||||||
|
* {@link ConfigValueType#LIST}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement {@code ConfigList}</em>; it should only be implemented
|
||||||
|
* by the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ConfigList extends List<ConfigValue>, ConfigValue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively unwraps the list, returning a list of plain Java values such
|
||||||
|
* as Integer or String or whatever is in the list.
|
||||||
|
*
|
||||||
|
* @return a {@link java.util.List} containing plain Java objects
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
List<Object> unwrapped();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigList withOrigin(ConfigOrigin origin);
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method allows you to alter default config loading strategy for all the code which
|
||||||
|
* calls {@link ConfigFactory#load}.
|
||||||
|
*
|
||||||
|
* Usually you don't have to implement this interface but it may be required
|
||||||
|
* when you fixing a improperly implemented library with unavailable source code.
|
||||||
|
*
|
||||||
|
* You have to define VM property {@code config.strategy} to replace default strategy with your own.
|
||||||
|
*/
|
||||||
|
public interface ConfigLoadingStrategy {
|
||||||
|
/**
|
||||||
|
* This method must load and parse application config.
|
||||||
|
*
|
||||||
|
* @param parseOptions {@link ConfigParseOptions} to use
|
||||||
|
* @return loaded config
|
||||||
|
*/
|
||||||
|
Config parseApplicationConfig(ConfigParseOptions parseOptions);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable class representing an amount of memory. Use
|
||||||
|
* static factory methods such as {@link
|
||||||
|
* ConfigMemorySize#ofBytes(long)} to create instances.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
public final class ConfigMemorySize {
|
||||||
|
private final long bytes;
|
||||||
|
|
||||||
|
private ConfigMemorySize(long bytes) {
|
||||||
|
if (bytes < 0)
|
||||||
|
throw new IllegalArgumentException("Attempt to construct ConfigMemorySize with negative number: " + bytes);
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a ConfigMemorySize representing the given
|
||||||
|
* number of bytes.
|
||||||
|
* @since 1.3.0
|
||||||
|
* @param bytes a number of bytes
|
||||||
|
* @return an instance representing the number of bytes
|
||||||
|
*/
|
||||||
|
public static ConfigMemorySize ofBytes(long bytes) {
|
||||||
|
return new ConfigMemorySize(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size in bytes.
|
||||||
|
* @since 1.3.0
|
||||||
|
* @return how many bytes
|
||||||
|
*/
|
||||||
|
public long toBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ConfigMemorySize(" + bytes + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof ConfigMemorySize) {
|
||||||
|
return ((ConfigMemorySize)other).bytes == this.bytes;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// in Java 8 this can become Long.hashCode(bytes)
|
||||||
|
return Long.valueOf(bytes).hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker for types whose instances can be merged, that is {@link Config} and
|
||||||
|
* {@link ConfigValue}. Instances of {@code Config} and {@code ConfigValue} can
|
||||||
|
* be combined into a single new instance using the
|
||||||
|
* {@link ConfigMergeable#withFallback withFallback()} method.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement this interface</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*/
|
||||||
|
public interface ConfigMergeable {
|
||||||
|
/**
|
||||||
|
* Returns a new value computed by merging this value with another, with
|
||||||
|
* keys in this value "winning" over the other one.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This associative operation may be used to combine configurations from
|
||||||
|
* multiple sources (such as multiple configuration files).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The semantics of merging are described in the <a
|
||||||
|
* href="https://github.com/lightbend/config/blob/master/HOCON.md">spec
|
||||||
|
* for HOCON</a>. Merging typically occurs when either the same object is
|
||||||
|
* created twice in the same file, or two config files are both loaded. For
|
||||||
|
* example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* foo = { a: 42 }
|
||||||
|
* foo = { b: 43 }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Here, the two objects are merged as if you had written:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* foo = { a: 42, b: 43 }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Only {@link ConfigObject} and {@link Config} instances do anything in
|
||||||
|
* this method (they need to merge the fallback keys into themselves). All
|
||||||
|
* other values just return the original value, since they automatically
|
||||||
|
* override any fallback. This means that objects do not merge "across"
|
||||||
|
* non-objects; if you write
|
||||||
|
* <code>object.withFallback(nonObject).withFallback(otherObject)</code>,
|
||||||
|
* then <code>otherObject</code> will simply be ignored. This is an
|
||||||
|
* intentional part of how merging works, because non-objects such as
|
||||||
|
* strings and integers replace (rather than merging with) any prior value:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* foo = { a: 42 }
|
||||||
|
* foo = 10
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Here, the number 10 "wins" and the value of <code>foo</code> would be
|
||||||
|
* simply 10. Again, for details see the spec.
|
||||||
|
*
|
||||||
|
* @param other
|
||||||
|
* an object whose keys should be used as fallbacks, if the keys
|
||||||
|
* are not present in this one
|
||||||
|
* @return a new object (or the original one, if the fallback doesn't get
|
||||||
|
* used)
|
||||||
|
*/
|
||||||
|
ConfigMergeable withFallback(ConfigMergeable other);
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtype of {@link ConfigValue} representing an object (AKA dictionary or map)
|
||||||
|
* value, as in JSON's curly brace <code>{ "a" : 42 }</code> syntax.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* An object may also be viewed as a {@link Config} by calling
|
||||||
|
* {@link ConfigObject#toConfig()}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@code ConfigObject} implements {@code java.util.Map<String, ConfigValue>} so
|
||||||
|
* you can use it like a regular Java map. Or call {@link #unwrapped()} to
|
||||||
|
* unwrap the map to a map with plain Java values rather than
|
||||||
|
* {@code ConfigValue}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Like all {@link ConfigValue} subtypes, {@code ConfigObject} is immutable.
|
||||||
|
* This makes it threadsafe and you never have to create "defensive copies." The
|
||||||
|
* mutator methods from {@link java.util.Map} all throw
|
||||||
|
* {@link java.lang.UnsupportedOperationException}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The {@link ConfigValue#valueType} method on an object returns
|
||||||
|
* {@link ConfigValueType#OBJECT}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* In most cases you want to use the {@link Config} interface rather than this
|
||||||
|
* one. Call {@link #toConfig()} to convert a {@code ConfigObject} to a
|
||||||
|
* {@code Config}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The API for a {@code ConfigObject} is in terms of keys, while the API for a
|
||||||
|
* {@link Config} is in terms of path expressions. Conceptually,
|
||||||
|
* {@code ConfigObject} is a tree of maps from keys to values, while a
|
||||||
|
* {@code Config} is a one-level map from paths to values.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
||||||
|
* between path expressions and individual path elements (keys).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A {@code ConfigObject} may contain null values, which will have
|
||||||
|
* {@link ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If
|
||||||
|
* {@link ConfigObject#get(Object)} returns Java's null then the key was not
|
||||||
|
* present in the parsed file (or wherever this value tree came from). If
|
||||||
|
* {@code get("key")} returns a {@link ConfigValue} with type
|
||||||
|
* {@code ConfigValueType#NULL} then the key was set to null explicitly in the
|
||||||
|
* config file.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement interface {@code ConfigObject}</em>; it should only be
|
||||||
|
* implemented by the config library. Arbitrary implementations will not work
|
||||||
|
* because the library internals assume a specific concrete implementation.
|
||||||
|
* Also, this interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*/
|
||||||
|
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this object to a {@link Config} instance, enabling you to use
|
||||||
|
* path expressions to find values in the object. This is a constant-time
|
||||||
|
* operation (it is not proportional to the size of the object).
|
||||||
|
*
|
||||||
|
* @return a {@link Config} with this object as its root
|
||||||
|
*/
|
||||||
|
Config toConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively unwraps the object, returning a map from String to whatever
|
||||||
|
* plain Java values are unwrapped from the object's values.
|
||||||
|
*
|
||||||
|
* @return a {@link java.util.Map} containing plain Java objects
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
Map<String, Object> unwrapped();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigObject withFallback(ConfigMergeable other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a {@link ConfigValue} at the given key, or returns null if there is
|
||||||
|
* no value. The returned {@link ConfigValue} may have
|
||||||
|
* {@link ConfigValueType#NULL} or any other type, and the passed-in key
|
||||||
|
* must be a key in this object (rather than a path expression).
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* key to look up
|
||||||
|
*
|
||||||
|
* @return the value at the key or null if none
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
ConfigValue get(Object key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone the object with only the given key (and its children) retained; all
|
||||||
|
* sibling keys are removed.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* key to keep
|
||||||
|
* @return a copy of the object minus all keys except the one specified
|
||||||
|
*/
|
||||||
|
ConfigObject withOnlyKey(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone the object with the given key removed.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* key to remove
|
||||||
|
* @return a copy of the object minus the specified key
|
||||||
|
*/
|
||||||
|
ConfigObject withoutKey(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code ConfigObject} based on this one, but with the given key
|
||||||
|
* set to the given value. Does not modify this instance (since it's
|
||||||
|
* immutable). If the key already has a value, that value is replaced. To
|
||||||
|
* remove a value, use {@link ConfigObject#withoutKey(String)}.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* key to add
|
||||||
|
* @param value
|
||||||
|
* value at the new key
|
||||||
|
* @return the new instance with the new map entry
|
||||||
|
*/
|
||||||
|
ConfigObject withValue(String key, ConfigValue value);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigObject withOrigin(ConfigOrigin origin);
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the origin (such as filename and line number) of a
|
||||||
|
* {@link ConfigValue} for use in error messages. Obtain the origin of a value
|
||||||
|
* with {@link ConfigValue#origin}. Exceptions may have an origin, see
|
||||||
|
* {@link ConfigException#origin}, but be careful because
|
||||||
|
* <code>ConfigException.origin()</code> may return null.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It's best to use this interface only for debugging; its accuracy is
|
||||||
|
* "best effort" rather than guaranteed, and a potentially-noticeable amount of
|
||||||
|
* memory could probably be saved if origins were not kept around, so in the
|
||||||
|
* future there might be some option to discard origins.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement this interface</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*/
|
||||||
|
public interface ConfigOrigin {
|
||||||
|
/**
|
||||||
|
* Returns a string describing the origin of a value or exception. This will
|
||||||
|
* never return null.
|
||||||
|
*
|
||||||
|
* @return string describing the origin
|
||||||
|
*/
|
||||||
|
public String description();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a filename describing the origin. This will return null if the
|
||||||
|
* origin was not a file.
|
||||||
|
*
|
||||||
|
* @return filename of the origin or null
|
||||||
|
*/
|
||||||
|
public String filename();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a URL describing the origin. This will return null if the origin
|
||||||
|
* has no meaningful URL.
|
||||||
|
*
|
||||||
|
* @return url of the origin or null
|
||||||
|
*/
|
||||||
|
public URL url();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a classpath resource name describing the origin. This will return
|
||||||
|
* null if the origin was not a classpath resource.
|
||||||
|
*
|
||||||
|
* @return resource name of the origin or null
|
||||||
|
*/
|
||||||
|
public String resource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a line number where the value or exception originated. This will
|
||||||
|
* return -1 if there's no meaningful line number.
|
||||||
|
*
|
||||||
|
* @return line number or -1 if none is available
|
||||||
|
*/
|
||||||
|
public int lineNumber();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns any comments that appeared to "go with" this place in the file.
|
||||||
|
* Often an empty list, but never null. The details of this are subject to
|
||||||
|
* change, but at the moment comments that are immediately before an array
|
||||||
|
* element or object field, with no blank line after the comment, "go with"
|
||||||
|
* that element or field.
|
||||||
|
*
|
||||||
|
* @return any comments that seemed to "go with" this origin, empty list if
|
||||||
|
* none
|
||||||
|
*/
|
||||||
|
public List<String> comments();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code ConfigOrigin} based on this one, but with the given
|
||||||
|
* comments. Does not modify this instance or any {@code ConfigValue}s with
|
||||||
|
* this origin (since they are immutable). To set the returned origin to a
|
||||||
|
* {@code ConfigValue}, use {@link ConfigValue#withOrigin}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that when the given comments are equal to the comments on this object,
|
||||||
|
* a new instance may not be created and {@code this} is returned directly.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param comments the comments used on the returned origin
|
||||||
|
* @return the ConfigOrigin with the given comments
|
||||||
|
*/
|
||||||
|
public ConfigOrigin withComments(List<String> comments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code ConfigOrigin} based on this one, but with the given
|
||||||
|
* line number. This origin must be a FILE, URL or RESOURCE. Does not modify
|
||||||
|
* this instance or any {@code ConfigValue}s with this origin (since they are
|
||||||
|
* immutable). To set the returned origin to a {@code ConfigValue}, use
|
||||||
|
* {@link ConfigValue#withOrigin}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that when the given lineNumber are equal to the lineNumber on this
|
||||||
|
* object, a new instance may not be created and {@code this} is returned
|
||||||
|
* directly.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param lineNumber the new line number
|
||||||
|
* @return the created ConfigOrigin
|
||||||
|
*/
|
||||||
|
public ConfigOrigin withLineNumber(int lineNumber);
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains some static factory methods for building a {@link
|
||||||
|
* ConfigOrigin}. {@code ConfigOrigin}s are automatically created when you
|
||||||
|
* call other API methods to get a {@code ConfigValue} or {@code Config}.
|
||||||
|
* But you can also set the origin of an existing {@code ConfigValue}, using
|
||||||
|
* {@link ConfigValue#withOrigin(ConfigOrigin)}.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
public final class ConfigOriginFactory {
|
||||||
|
private ConfigOriginFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default origin for values when no other information is
|
||||||
|
* provided. This is the origin used in {@link ConfigValueFactory
|
||||||
|
* #fromAnyRef(Object)}.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @return the default origin
|
||||||
|
*/
|
||||||
|
public static ConfigOrigin newSimple() {
|
||||||
|
return newSimple(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an origin with the given description.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param description brief description of what the origin is
|
||||||
|
* @return a new origin
|
||||||
|
*/
|
||||||
|
public static ConfigOrigin newSimple(String description) {
|
||||||
|
return ConfigImpl.newSimpleOrigin(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file origin with the given filename.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param filename the filename of this origin
|
||||||
|
* @return a new origin
|
||||||
|
*/
|
||||||
|
public static ConfigOrigin newFile(String filename) {
|
||||||
|
return ConfigImpl.newFileOrigin(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a url origin with the given URL object.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param url the url of this origin
|
||||||
|
* @return a new origin
|
||||||
|
*/
|
||||||
|
public static ConfigOrigin newURL(URL url) {
|
||||||
|
return ConfigImpl.newURLOrigin(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
+228
@@ -0,0 +1,228 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of options related to parsing.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This object is immutable, so the "setters" return a new object.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Here is an example of creating a custom {@code ConfigParseOptions}:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigParseOptions options = ConfigParseOptions.defaults()
|
||||||
|
* .setSyntax(ConfigSyntax.JSON)
|
||||||
|
* .setAllowMissing(false)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class ConfigParseOptions {
|
||||||
|
final ConfigSyntax syntax;
|
||||||
|
final String originDescription;
|
||||||
|
final boolean allowMissing;
|
||||||
|
final ConfigIncluder includer;
|
||||||
|
final ClassLoader classLoader;
|
||||||
|
|
||||||
|
private ConfigParseOptions(ConfigSyntax syntax, String originDescription, boolean allowMissing,
|
||||||
|
ConfigIncluder includer, ClassLoader classLoader) {
|
||||||
|
this.syntax = syntax;
|
||||||
|
this.originDescription = originDescription;
|
||||||
|
this.allowMissing = allowMissing;
|
||||||
|
this.includer = includer;
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an instance of <code>ConfigParseOptions</code> with all fields
|
||||||
|
* set to the default values. Start with this instance and make any
|
||||||
|
* changes you need.
|
||||||
|
* @return the default parse options
|
||||||
|
*/
|
||||||
|
public static ConfigParseOptions defaults() {
|
||||||
|
return new ConfigParseOptions(null, null, true, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the file format. If set to null, try to guess from any available
|
||||||
|
* filename extension; if guessing fails, assume {@link ConfigSyntax#CONF}.
|
||||||
|
*
|
||||||
|
* @param syntax
|
||||||
|
* a syntax or {@code null} for best guess
|
||||||
|
* @return options with the syntax set
|
||||||
|
*/
|
||||||
|
public ConfigParseOptions setSyntax(ConfigSyntax syntax) {
|
||||||
|
if (this.syntax == syntax)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigParseOptions(syntax, this.originDescription, this.allowMissing,
|
||||||
|
this.includer, this.classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current syntax option, which may be null for "any".
|
||||||
|
* @return the current syntax or null
|
||||||
|
*/
|
||||||
|
public ConfigSyntax getSyntax() {
|
||||||
|
return syntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a description for the thing being parsed. In most cases this will be
|
||||||
|
* set up for you to something like the filename, but if you provide just an
|
||||||
|
* input stream you might want to improve on it. Set to null to allow the
|
||||||
|
* library to come up with something automatically. This description is the
|
||||||
|
* basis for the {@link ConfigOrigin} of the parsed values.
|
||||||
|
*
|
||||||
|
* @param originDescription description to put in the {@link ConfigOrigin}
|
||||||
|
* @return options with the origin description set
|
||||||
|
*/
|
||||||
|
public ConfigParseOptions setOriginDescription(String originDescription) {
|
||||||
|
// findbugs complains about == here but is wrong, do not "fix"
|
||||||
|
if (this.originDescription == originDescription)
|
||||||
|
return this;
|
||||||
|
else if (this.originDescription != null && originDescription != null
|
||||||
|
&& this.originDescription.equals(originDescription))
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigParseOptions(this.syntax, originDescription, this.allowMissing,
|
||||||
|
this.includer, this.classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current origin description, which may be null for "automatic".
|
||||||
|
* @return the current origin description or null
|
||||||
|
*/
|
||||||
|
public String getOriginDescription() {
|
||||||
|
return originDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** this is package-private, not public API */
|
||||||
|
ConfigParseOptions withFallbackOriginDescription(String originDescription) {
|
||||||
|
if (this.originDescription == null)
|
||||||
|
return setOriginDescription(originDescription);
|
||||||
|
else
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to false to throw an exception if the item being parsed (for example
|
||||||
|
* a file) is missing. Set to true to just return an empty document in that
|
||||||
|
* case. Note that this setting applies on only to fetching the root document,
|
||||||
|
* it has no effect on any nested includes.
|
||||||
|
*
|
||||||
|
* @param allowMissing true to silently ignore missing item
|
||||||
|
* @return options with the "allow missing" flag set
|
||||||
|
*/
|
||||||
|
public ConfigParseOptions setAllowMissing(boolean allowMissing) {
|
||||||
|
if (this.allowMissing == allowMissing)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigParseOptions(this.syntax, this.originDescription, allowMissing,
|
||||||
|
this.includer, this.classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current "allow missing" flag.
|
||||||
|
* @return whether we allow missing files
|
||||||
|
*/
|
||||||
|
public boolean getAllowMissing() {
|
||||||
|
return allowMissing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a {@link ConfigIncluder} which customizes how includes are handled.
|
||||||
|
* null means to use the default includer.
|
||||||
|
*
|
||||||
|
* @param includer the includer to use or null for default
|
||||||
|
* @return new version of the parse options with different includer
|
||||||
|
*/
|
||||||
|
public ConfigParseOptions setIncluder(ConfigIncluder includer) {
|
||||||
|
if (this.includer == includer)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing,
|
||||||
|
includer, this.classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends a {@link ConfigIncluder} which customizes how
|
||||||
|
* includes are handled. To prepend your includer, the
|
||||||
|
* library calls {@link ConfigIncluder#withFallback} on your
|
||||||
|
* includer to append the existing includer to it.
|
||||||
|
*
|
||||||
|
* @param includer the includer to prepend (may not be null)
|
||||||
|
* @return new version of the parse options with different includer
|
||||||
|
*/
|
||||||
|
public ConfigParseOptions prependIncluder(ConfigIncluder includer) {
|
||||||
|
if (includer == null)
|
||||||
|
throw new NullPointerException("null includer passed to prependIncluder");
|
||||||
|
if (this.includer == includer)
|
||||||
|
return this;
|
||||||
|
else if (this.includer != null)
|
||||||
|
return setIncluder(includer.withFallback(this.includer));
|
||||||
|
else
|
||||||
|
return setIncluder(includer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a {@link ConfigIncluder} which customizes how
|
||||||
|
* includes are handled. To append, the library calls {@link
|
||||||
|
* ConfigIncluder#withFallback} on the existing includer.
|
||||||
|
*
|
||||||
|
* @param includer the includer to append (may not be null)
|
||||||
|
* @return new version of the parse options with different includer
|
||||||
|
*/
|
||||||
|
public ConfigParseOptions appendIncluder(ConfigIncluder includer) {
|
||||||
|
if (includer == null)
|
||||||
|
throw new NullPointerException("null includer passed to appendIncluder");
|
||||||
|
if (this.includer == includer)
|
||||||
|
return this;
|
||||||
|
else if (this.includer != null)
|
||||||
|
return setIncluder(this.includer.withFallback(includer));
|
||||||
|
else
|
||||||
|
return setIncluder(includer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current includer (will be null for the default includer).
|
||||||
|
* @return current includer or null
|
||||||
|
*/
|
||||||
|
public ConfigIncluder getIncluder() {
|
||||||
|
return includer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the class loader. If set to null,
|
||||||
|
* <code>Thread.currentThread().getContextClassLoader()</code> will be used.
|
||||||
|
*
|
||||||
|
* @param loader
|
||||||
|
* a class loader or {@code null} to use thread context class
|
||||||
|
* loader
|
||||||
|
* @return options with the class loader set
|
||||||
|
*/
|
||||||
|
public ConfigParseOptions setClassLoader(ClassLoader loader) {
|
||||||
|
if (this.classLoader == loader)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing,
|
||||||
|
this.includer, loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the class loader; never returns {@code null}, if the class loader was
|
||||||
|
* unset, returns
|
||||||
|
* <code>Thread.currentThread().getContextClassLoader()</code>.
|
||||||
|
*
|
||||||
|
* @return class loader to use
|
||||||
|
*/
|
||||||
|
public ClassLoader getClassLoader() {
|
||||||
|
if (this.classLoader == null)
|
||||||
|
return Thread.currentThread().getContextClassLoader();
|
||||||
|
else
|
||||||
|
return this.classLoader;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An opaque handle to something that can be parsed, obtained from
|
||||||
|
* {@link ConfigIncludeContext}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement this interface</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*/
|
||||||
|
public interface ConfigParseable {
|
||||||
|
/**
|
||||||
|
* Parse whatever it is. The options should come from
|
||||||
|
* {@link ConfigParseable#options options()} but you could tweak them if you
|
||||||
|
* like.
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
* parse options, should be based on the ones from
|
||||||
|
* {@link ConfigParseable#options options()}
|
||||||
|
* @return the parsed object
|
||||||
|
*/
|
||||||
|
ConfigObject parse(ConfigParseOptions options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link ConfigOrigin} describing the origin of the parseable
|
||||||
|
* item.
|
||||||
|
* @return the origin of the parseable item
|
||||||
|
*/
|
||||||
|
ConfigOrigin origin();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the initial options, which can be modified then passed to parse().
|
||||||
|
* These options will have the right description, includer, and other
|
||||||
|
* parameters already set up.
|
||||||
|
* @return the initial options
|
||||||
|
*/
|
||||||
|
ConfigParseOptions options();
|
||||||
|
}
|
||||||
+182
@@ -0,0 +1,182 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A set of options related to rendering a {@link ConfigValue}. Passed to
|
||||||
|
* {@link ConfigValue#render(ConfigRenderOptions)}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Here is an example of creating a {@code ConfigRenderOptions}:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigRenderOptions options =
|
||||||
|
* ConfigRenderOptions.defaults().setComments(false)
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public final class ConfigRenderOptions {
|
||||||
|
private final boolean originComments;
|
||||||
|
private final boolean comments;
|
||||||
|
private final boolean formatted;
|
||||||
|
private final boolean json;
|
||||||
|
|
||||||
|
private ConfigRenderOptions(boolean originComments, boolean comments, boolean formatted,
|
||||||
|
boolean json) {
|
||||||
|
this.originComments = originComments;
|
||||||
|
this.comments = comments;
|
||||||
|
this.formatted = formatted;
|
||||||
|
this.json = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default render options which are verbose (commented and
|
||||||
|
* formatted). See {@link ConfigRenderOptions#concise} for stripped-down
|
||||||
|
* options. This rendering will not be valid JSON since it has comments.
|
||||||
|
*
|
||||||
|
* @return the default render options
|
||||||
|
*/
|
||||||
|
public static ConfigRenderOptions defaults() {
|
||||||
|
return new ConfigRenderOptions(true, true, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns concise render options (no whitespace or comments). For a
|
||||||
|
* resolved {@link Config}, the concise rendering will be valid JSON.
|
||||||
|
*
|
||||||
|
* @return the concise render options
|
||||||
|
*/
|
||||||
|
public static ConfigRenderOptions concise() {
|
||||||
|
return new ConfigRenderOptions(false, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with comments toggled. This controls human-written
|
||||||
|
* comments but not the autogenerated "origin of this setting" comments,
|
||||||
|
* which are controlled by {@link ConfigRenderOptions#setOriginComments}.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to include comments in the render
|
||||||
|
* @return options with requested setting for comments
|
||||||
|
*/
|
||||||
|
public ConfigRenderOptions setComments(boolean value) {
|
||||||
|
if (value == comments)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigRenderOptions(originComments, value, formatted, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options enable comments. This method is mostly used
|
||||||
|
* by the config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return true if comments should be rendered
|
||||||
|
*/
|
||||||
|
public boolean getComments() {
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with origin comments toggled. If this is enabled, the
|
||||||
|
* library generates comments for each setting based on the
|
||||||
|
* {@link ConfigValue#origin} of that setting's value. For example these
|
||||||
|
* comments might tell you which file a setting comes from.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@code setOriginComments()} controls only these autogenerated
|
||||||
|
* "origin of this setting" comments, to toggle regular comments use
|
||||||
|
* {@link ConfigRenderOptions#setComments}.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to include autogenerated setting-origin comments in the
|
||||||
|
* render
|
||||||
|
* @return options with origin comments toggled
|
||||||
|
*/
|
||||||
|
public ConfigRenderOptions setOriginComments(boolean value) {
|
||||||
|
if (value == originComments)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigRenderOptions(value, comments, formatted, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options enable automated origin comments. This method
|
||||||
|
* is mostly used by the config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return true if origin comments should be rendered
|
||||||
|
*/
|
||||||
|
public boolean getOriginComments() {
|
||||||
|
return originComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with formatting toggled. Formatting means indentation and
|
||||||
|
* whitespace, enabling formatting makes things prettier but larger.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to enable formatting
|
||||||
|
* @return options with requested setting for formatting
|
||||||
|
*/
|
||||||
|
public ConfigRenderOptions setFormatted(boolean value) {
|
||||||
|
if (value == formatted)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigRenderOptions(originComments, comments, value, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options enable formatting. This method is mostly used
|
||||||
|
* by the config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return true if the options enable formatting
|
||||||
|
*/
|
||||||
|
public boolean getFormatted() {
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with JSON toggled. JSON means that HOCON extensions
|
||||||
|
* (omitting commas, quotes for example) won't be used. However, whether to
|
||||||
|
* use comments is controlled by the separate {@link #setComments(boolean)}
|
||||||
|
* and {@link #setOriginComments(boolean)} options. So if you enable
|
||||||
|
* comments you will get invalid JSON despite setting this to true.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to include non-JSON extensions in the render
|
||||||
|
* @return options with requested setting for JSON
|
||||||
|
*/
|
||||||
|
public ConfigRenderOptions setJson(boolean value) {
|
||||||
|
if (value == json)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ConfigRenderOptions(originComments, comments, formatted, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options enable JSON. This method is mostly used by
|
||||||
|
* the config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return true if only JSON should be rendered
|
||||||
|
*/
|
||||||
|
public boolean getJson() {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder("ConfigRenderOptions(");
|
||||||
|
if (originComments)
|
||||||
|
sb.append("originComments,");
|
||||||
|
if (comments)
|
||||||
|
sb.append("comments,");
|
||||||
|
if (formatted)
|
||||||
|
sb.append("formatted,");
|
||||||
|
if (json)
|
||||||
|
sb.append("json,");
|
||||||
|
if (sb.charAt(sb.length() - 1) == ',')
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of options related to resolving substitutions. Substitutions use the
|
||||||
|
* <code>${foo.bar}</code> syntax and are documented in the <a
|
||||||
|
* href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON</a>
|
||||||
|
* spec.
|
||||||
|
* <p>
|
||||||
|
* Typically this class would be used with the method
|
||||||
|
* {@link Config#resolve(ConfigResolveOptions)}.
|
||||||
|
* <p>
|
||||||
|
* This object is immutable, so the "setters" return a new object.
|
||||||
|
* <p>
|
||||||
|
* Here is an example of creating a custom {@code ConfigResolveOptions}:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigResolveOptions options = ConfigResolveOptions.defaults()
|
||||||
|
* .setUseSystemEnvironment(false)
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* In addition to {@link ConfigResolveOptions#defaults}, there's a prebuilt
|
||||||
|
* {@link ConfigResolveOptions#noSystem} which avoids looking at any system
|
||||||
|
* environment variables or other external system information. (Right now,
|
||||||
|
* environment variables are the only example.)
|
||||||
|
*/
|
||||||
|
public final class ConfigResolveOptions {
|
||||||
|
private final boolean useSystemEnvironment;
|
||||||
|
private final boolean allowUnresolved;
|
||||||
|
private final ConfigResolver resolver;
|
||||||
|
|
||||||
|
private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolved,
|
||||||
|
ConfigResolver resolver) {
|
||||||
|
this.useSystemEnvironment = useSystemEnvironment;
|
||||||
|
this.allowUnresolved = allowUnresolved;
|
||||||
|
this.resolver = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default resolve options. By default the system environment
|
||||||
|
* will be used and unresolved substitutions are not allowed.
|
||||||
|
*
|
||||||
|
* @return the default resolve options
|
||||||
|
*/
|
||||||
|
public static ConfigResolveOptions defaults() {
|
||||||
|
return new ConfigResolveOptions(true, false, NULL_RESOLVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns resolve options that disable any reference to "system" data
|
||||||
|
* (currently, this means environment variables).
|
||||||
|
*
|
||||||
|
* @return the resolve options with env variables disabled
|
||||||
|
*/
|
||||||
|
public static ConfigResolveOptions noSystem() {
|
||||||
|
return defaults().setUseSystemEnvironment(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with use of environment variables set to the given value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to resolve substitutions falling back to environment
|
||||||
|
* variables.
|
||||||
|
* @return options with requested setting for use of environment variables
|
||||||
|
*/
|
||||||
|
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
|
||||||
|
return new ConfigResolveOptions(value, allowUnresolved, resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options enable use of system environment variables.
|
||||||
|
* This method is mostly used by the config lib internally, not by
|
||||||
|
* applications.
|
||||||
|
*
|
||||||
|
* @return true if environment variables should be used
|
||||||
|
*/
|
||||||
|
public boolean getUseSystemEnvironment() {
|
||||||
|
return useSystemEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with "allow unresolved" set to the given value. By
|
||||||
|
* default, unresolved substitutions are an error. If unresolved
|
||||||
|
* substitutions are allowed, then a future attempt to use the unresolved
|
||||||
|
* value may fail, but {@link Config#resolve(ConfigResolveOptions)} itself
|
||||||
|
* will not throw.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to silently ignore unresolved substitutions.
|
||||||
|
* @return options with requested setting for whether to allow substitutions
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public ConfigResolveOptions setAllowUnresolved(boolean value) {
|
||||||
|
return new ConfigResolveOptions(useSystemEnvironment, value, resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options where the given resolver used as a fallback if a
|
||||||
|
* reference cannot be otherwise resolved. This resolver will only be called
|
||||||
|
* after resolution has failed to substitute with a value from within the
|
||||||
|
* config itself and with any other resolvers that have been appended before
|
||||||
|
* this one. Multiple resolvers can be added using,
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigResolveOptions options = ConfigResolveOptions.defaults()
|
||||||
|
* .appendResolver(primary)
|
||||||
|
* .appendResolver(secondary)
|
||||||
|
* .appendResolver(tertiary);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* With this config unresolved references will first be resolved with the
|
||||||
|
* primary resolver, if that fails then the secondary, and finally if that
|
||||||
|
* also fails the tertiary.
|
||||||
|
*
|
||||||
|
* If all fallbacks fail to return a substitution "allow unresolved"
|
||||||
|
* determines whether resolution fails or continues.
|
||||||
|
*`
|
||||||
|
* @param value the resolver to fall back to
|
||||||
|
* @return options that use the given resolver as a fallback
|
||||||
|
* @since 1.3.2
|
||||||
|
*/
|
||||||
|
public ConfigResolveOptions appendResolver(ConfigResolver value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new ConfigException.BugOrBroken("null resolver passed to appendResolver");
|
||||||
|
} else if (value == this.resolver) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return new ConfigResolveOptions(useSystemEnvironment, allowUnresolved,
|
||||||
|
this.resolver.withFallback(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolver to use as a fallback if a substitution cannot be
|
||||||
|
* otherwise resolved. Never returns null. This method is mostly used by the
|
||||||
|
* config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return the non-null fallback resolver
|
||||||
|
* @since 1.3.2
|
||||||
|
*/
|
||||||
|
public ConfigResolver getResolver() {
|
||||||
|
return this.resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options allow unresolved substitutions. This method
|
||||||
|
* is mostly used by the config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return true if unresolved substitutions are allowed
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public boolean getAllowUnresolved() {
|
||||||
|
return allowUnresolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton resolver that never resolves paths.
|
||||||
|
*/
|
||||||
|
private static final ConfigResolver NULL_RESOLVER = new ConfigResolver() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue lookup(String path) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigResolver withFallback(ConfigResolver fallback) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this interface and provide an instance to
|
||||||
|
* {@link ConfigResolveOptions#appendResolver ConfigResolveOptions.appendResolver()}
|
||||||
|
* to provide custom behavior when unresolved substitutions are encountered
|
||||||
|
* during resolution.
|
||||||
|
* @since 1.3.2
|
||||||
|
*/
|
||||||
|
public interface ConfigResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value to substitute for the given unresolved path. To get the
|
||||||
|
* components of the path use {@link ConfigUtil#splitPath(String)}. If a
|
||||||
|
* non-null value is returned that value will be substituted, otherwise
|
||||||
|
* resolution will continue to consider the substitution as still
|
||||||
|
* unresolved.
|
||||||
|
*
|
||||||
|
* @param path the unresolved path
|
||||||
|
* @return the value to use as a substitution or null
|
||||||
|
*/
|
||||||
|
public ConfigValue lookup(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new resolver that falls back to the given resolver if this
|
||||||
|
* one doesn't provide a substitution itself.
|
||||||
|
*
|
||||||
|
* It's important to handle the case where you already have the fallback
|
||||||
|
* with a "return this", i.e. this method should not create a new object if
|
||||||
|
* the fallback is the same one you already have. The same fallback may be
|
||||||
|
* added repeatedly.
|
||||||
|
*
|
||||||
|
* @param fallback the previous includer for chaining
|
||||||
|
* @return a new resolver
|
||||||
|
*/
|
||||||
|
public ConfigResolver withFallback(ConfigResolver fallback);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The syntax of a character stream (<a href="http://json.org">JSON</a>, <a
|
||||||
|
* href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON</a>
|
||||||
|
* aka ".conf", or <a href=
|
||||||
|
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
||||||
|
* >Java properties</a>).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum ConfigSyntax {
|
||||||
|
/**
|
||||||
|
* Pedantically strict <a href="http://json.org">JSON</a> format; no
|
||||||
|
* comments, no unexpected commas, no duplicate keys in the same object.
|
||||||
|
* Associated with the <code>.json</code> file extension and
|
||||||
|
* <code>application/json</code> Content-Type.
|
||||||
|
*/
|
||||||
|
JSON,
|
||||||
|
/**
|
||||||
|
* The JSON-superset <a
|
||||||
|
* href="https://github.com/lightbend/config/blob/master/HOCON.md"
|
||||||
|
* >HOCON</a> format. Associated with the <code>.conf</code> file extension
|
||||||
|
* and <code>application/hocon</code> Content-Type.
|
||||||
|
*/
|
||||||
|
CONF,
|
||||||
|
/**
|
||||||
|
* Standard <a href=
|
||||||
|
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
||||||
|
* >Java properties</a> format. Associated with the <code>.properties</code>
|
||||||
|
* file extension and <code>text/x-java-properties</code> Content-Type.
|
||||||
|
*/
|
||||||
|
PROPERTIES;
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigImplUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains static utility methods.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class ConfigUtil {
|
||||||
|
private ConfigUtil() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quotes and escapes a string, as in the JSON specification.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* a string
|
||||||
|
* @return the string quoted and escaped
|
||||||
|
*/
|
||||||
|
public static String quoteString(String s) {
|
||||||
|
return ConfigImplUtil.renderJsonString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a list of keys to a path expression, by quoting the path
|
||||||
|
* elements as needed and then joining them separated by a period. A path
|
||||||
|
* expression is usable with a {@link Config}, while individual path
|
||||||
|
* elements are usable with a {@link ConfigObject}.
|
||||||
|
* <p>
|
||||||
|
* See the overview documentation for {@link Config} for more detail on path
|
||||||
|
* expressions vs. keys.
|
||||||
|
*
|
||||||
|
* @param elements
|
||||||
|
* the keys in the path
|
||||||
|
* @return a path expression
|
||||||
|
* @throws ConfigException
|
||||||
|
* if there are no elements
|
||||||
|
*/
|
||||||
|
public static String joinPath(String... elements) {
|
||||||
|
return ConfigImplUtil.joinPath(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a list of strings to a path expression, by quoting the path
|
||||||
|
* elements as needed and then joining them separated by a period. A path
|
||||||
|
* expression is usable with a {@link Config}, while individual path
|
||||||
|
* elements are usable with a {@link ConfigObject}.
|
||||||
|
* <p>
|
||||||
|
* See the overview documentation for {@link Config} for more detail on path
|
||||||
|
* expressions vs. keys.
|
||||||
|
*
|
||||||
|
* @param elements
|
||||||
|
* the keys in the path
|
||||||
|
* @return a path expression
|
||||||
|
* @throws ConfigException
|
||||||
|
* if the list is empty
|
||||||
|
*/
|
||||||
|
public static String joinPath(List<String> elements) {
|
||||||
|
return ConfigImplUtil.joinPath(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a path expression into a list of keys, by splitting on period
|
||||||
|
* and unquoting the individual path elements. A path expression is usable
|
||||||
|
* with a {@link Config}, while individual path elements are usable with a
|
||||||
|
* {@link ConfigObject}.
|
||||||
|
* <p>
|
||||||
|
* See the overview documentation for {@link Config} for more detail on path
|
||||||
|
* expressions vs. keys.
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* a path expression
|
||||||
|
* @return the individual keys in the path
|
||||||
|
* @throws ConfigException
|
||||||
|
* if the path expression is invalid
|
||||||
|
*/
|
||||||
|
public static List<String> splitPath(String path) {
|
||||||
|
return ConfigImplUtil.splitPath(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable value, following the <a href="http://json.org">JSON</a> type
|
||||||
|
* schema.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Because this object is immutable, it is safe to use from multiple threads and
|
||||||
|
* there's no need for "defensive copies."
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement interface {@code ConfigValue}</em>; it should only be
|
||||||
|
* implemented by the config library. Arbitrary implementations will not work
|
||||||
|
* because the library internals assume a specific concrete implementation.
|
||||||
|
* Also, this interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*/
|
||||||
|
public interface ConfigValue extends ConfigMergeable {
|
||||||
|
/**
|
||||||
|
* The origin of the value (file, line number, etc.), for debugging and
|
||||||
|
* error messages.
|
||||||
|
*
|
||||||
|
* @return where the value came from
|
||||||
|
*/
|
||||||
|
ConfigOrigin origin();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ConfigValueType} of the value; matches the JSON type schema.
|
||||||
|
*
|
||||||
|
* @return value's type
|
||||||
|
*/
|
||||||
|
ConfigValueType valueType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value as a plain Java boxed value, that is, a {@code String},
|
||||||
|
* {@code Number}, {@code Boolean}, {@code Map<String,Object>},
|
||||||
|
* {@code List<Object>}, or {@code null}, matching the {@link #valueType()}
|
||||||
|
* of this {@code ConfigValue}. If the value is a {@link ConfigObject} or
|
||||||
|
* {@link ConfigList}, it is recursively unwrapped.
|
||||||
|
* @return a plain Java value corresponding to this ConfigValue
|
||||||
|
*/
|
||||||
|
Object unwrapped();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the config value as a HOCON string. This method is primarily
|
||||||
|
* intended for debugging, so it tries to add helpful comments and
|
||||||
|
* whitespace.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the config value has not been resolved (see {@link Config#resolve}),
|
||||||
|
* it's possible that it can't be rendered as valid HOCON. In that case the
|
||||||
|
* rendering should still be useful for debugging but you might not be able
|
||||||
|
* to parse it. If the value has been resolved, it will always be parseable.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is equivalent to
|
||||||
|
* {@code render(ConfigRenderOptions.defaults())}.
|
||||||
|
*
|
||||||
|
* @return the rendered value
|
||||||
|
*/
|
||||||
|
String render();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the config value to a string, using the provided options.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the config value has not been resolved (see {@link Config#resolve}),
|
||||||
|
* it's possible that it can't be rendered as valid HOCON. In that case the
|
||||||
|
* rendering should still be useful for debugging but you might not be able
|
||||||
|
* to parse it. If the value has been resolved, it will always be parseable.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the config value has been resolved and the options disable all
|
||||||
|
* HOCON-specific features (such as comments), the rendering will be valid
|
||||||
|
* JSON. If you enable HOCON-only features such as comments, the rendering
|
||||||
|
* will not be valid JSON.
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
* the rendering options
|
||||||
|
* @return the rendered value
|
||||||
|
*/
|
||||||
|
String render(ConfigRenderOptions options);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigValue withFallback(ConfigMergeable other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places the value inside a {@link Config} at the given path. See also
|
||||||
|
* {@link ConfigValue#atKey(String)}.
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* path to store this value at.
|
||||||
|
* @return a {@code Config} instance containing this value at the given
|
||||||
|
* path.
|
||||||
|
*/
|
||||||
|
Config atPath(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places the value inside a {@link Config} at the given key. See also
|
||||||
|
* {@link ConfigValue#atPath(String)}.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* key to store this value at.
|
||||||
|
* @return a {@code Config} instance containing this value at the given key.
|
||||||
|
*/
|
||||||
|
Config atKey(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code ConfigValue} based on this one, but with the given
|
||||||
|
* origin. This is useful when you are parsing a new format of file or setting
|
||||||
|
* comments for a single ConfigValue.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param origin the origin set on the returned value
|
||||||
|
* @return the new ConfigValue with the given origin
|
||||||
|
*/
|
||||||
|
ConfigValue withOrigin(ConfigOrigin origin);
|
||||||
|
}
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds some static factory methods for building {@link ConfigValue}
|
||||||
|
* instances. See also {@link ConfigFactory} which has methods for parsing files
|
||||||
|
* and certain in-memory data structures.
|
||||||
|
*/
|
||||||
|
public final class ConfigValueFactory {
|
||||||
|
private ConfigValueFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ConfigValue} from a plain Java boxed value, which may be
|
||||||
|
* a <code>Boolean</code>, <code>Number</code>, <code>String</code>,
|
||||||
|
* <code>Map</code>, <code>Iterable</code>, or <code>null</code>. A
|
||||||
|
* <code>Map</code> must be a <code>Map</code> from String to more values
|
||||||
|
* that can be supplied to <code>fromAnyRef()</code>. An
|
||||||
|
* <code>Iterable</code> must iterate over more values that can be supplied
|
||||||
|
* to <code>fromAnyRef()</code>. A <code>Map</code> will become a
|
||||||
|
* {@link ConfigObject} and an <code>Iterable</code> will become a
|
||||||
|
* {@link ConfigList}. If the <code>Iterable</code> is not an ordered
|
||||||
|
* collection, results could be strange, since <code>ConfigList</code> is
|
||||||
|
* ordered.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* In a <code>Map</code> passed to <code>fromAnyRef()</code>, the map's keys
|
||||||
|
* are plain keys, not path expressions. So if your <code>Map</code> has a
|
||||||
|
* key "foo.bar" then you will get one object with a key called "foo.bar",
|
||||||
|
* rather than an object with a key "foo" containing another object with a
|
||||||
|
* key "bar".
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The originDescription will be used to set the origin() field on the
|
||||||
|
* ConfigValue. It should normally be the name of the file the values came
|
||||||
|
* from, or something short describing the value such as "default settings".
|
||||||
|
* The originDescription is prefixed to error messages so users can tell
|
||||||
|
* where problematic values are coming from.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Supplying the result of ConfigValue.unwrapped() to this function is
|
||||||
|
* guaranteed to work and should give you back a ConfigValue that matches
|
||||||
|
* the one you unwrapped. The re-wrapped ConfigValue will lose some
|
||||||
|
* information that was present in the original such as its origin, but it
|
||||||
|
* will have matching values.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If you pass in a <code>ConfigValue</code> to this
|
||||||
|
* function, it will be returned unmodified. (The
|
||||||
|
* <code>originDescription</code> will be ignored in this
|
||||||
|
* case.)
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This function throws if you supply a value that cannot be converted to a
|
||||||
|
* ConfigValue, but supplying such a value is a bug in your program, so you
|
||||||
|
* should never handle the exception. Just fix your program (or report a bug
|
||||||
|
* against this library).
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* object to convert to ConfigValue
|
||||||
|
* @param originDescription
|
||||||
|
* name of origin file or brief description of what the value is
|
||||||
|
* @return a new value
|
||||||
|
*/
|
||||||
|
public static ConfigValue fromAnyRef(Object object, String originDescription) {
|
||||||
|
return ConfigImpl.fromAnyRef(object, originDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the {@link #fromAnyRef(Object,String)} documentation for details.
|
||||||
|
* This is a typesafe wrapper that only works on {@link java.util.Map} and
|
||||||
|
* returns {@link ConfigObject} rather than {@link ConfigValue}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If your <code>Map</code> has a key "foo.bar" then you will get one object
|
||||||
|
* with a key called "foo.bar", rather than an object with a key "foo"
|
||||||
|
* containing another object with a key "bar". The keys in the map are keys;
|
||||||
|
* not path expressions. That is, the <code>Map</code> corresponds exactly
|
||||||
|
* to a single {@code ConfigObject}. The keys will not be parsed or
|
||||||
|
* modified, and the values are wrapped in ConfigValue. To get nested
|
||||||
|
* {@code ConfigObject}, some of the values in the map would have to be more
|
||||||
|
* maps.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* See also {@link ConfigFactory#parseMap(Map,String)} which interprets the
|
||||||
|
* keys in the map as path expressions.
|
||||||
|
*
|
||||||
|
* @param values map from keys to plain Java values
|
||||||
|
* @param originDescription description to use in {@link ConfigOrigin} of created values
|
||||||
|
* @return a new {@link ConfigObject} value
|
||||||
|
*/
|
||||||
|
public static ConfigObject fromMap(Map<String, ? extends Object> values,
|
||||||
|
String originDescription) {
|
||||||
|
return (ConfigObject) fromAnyRef(values, originDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the {@link #fromAnyRef(Object,String)} documentation for details.
|
||||||
|
* This is a typesafe wrapper that only works on {@link java.lang.Iterable}
|
||||||
|
* and returns {@link ConfigList} rather than {@link ConfigValue}.
|
||||||
|
*
|
||||||
|
* @param values list of plain Java values
|
||||||
|
* @param originDescription description to use in {@link ConfigOrigin} of created values
|
||||||
|
* @return a new {@link ConfigList} value
|
||||||
|
*/
|
||||||
|
public static ConfigList fromIterable(Iterable<? extends Object> values,
|
||||||
|
String originDescription) {
|
||||||
|
return (ConfigList) fromAnyRef(values, originDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the other overload {@link #fromAnyRef(Object,String)} for details,
|
||||||
|
* this one just uses a default origin description.
|
||||||
|
*
|
||||||
|
* @param object a plain Java value
|
||||||
|
* @return a new {@link ConfigValue}
|
||||||
|
*/
|
||||||
|
public static ConfigValue fromAnyRef(Object object) {
|
||||||
|
return fromAnyRef(object, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the other overload {@link #fromMap(Map,String)} for details, this one
|
||||||
|
* just uses a default origin description.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* See also {@link ConfigFactory#parseMap(Map)} which interprets the keys in
|
||||||
|
* the map as path expressions.
|
||||||
|
*
|
||||||
|
* @param values map from keys to plain Java values
|
||||||
|
* @return a new {@link ConfigObject}
|
||||||
|
*/
|
||||||
|
public static ConfigObject fromMap(Map<String, ? extends Object> values) {
|
||||||
|
return fromMap(values, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the other overload of {@link #fromIterable(Iterable, String)} for
|
||||||
|
* details, this one just uses a default origin description.
|
||||||
|
*
|
||||||
|
* @param values list of plain Java values
|
||||||
|
* @return a new {@link ConfigList}
|
||||||
|
*/
|
||||||
|
public static ConfigList fromIterable(Iterable<? extends Object> values) {
|
||||||
|
return fromIterable(values, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of a configuration value (following the <a
|
||||||
|
* href="http://json.org">JSON</a> type schema).
|
||||||
|
*/
|
||||||
|
public enum ConfigValueType {
|
||||||
|
OBJECT, LIST, NUMBER, BOOLEAN, NULL, STRING
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default config loading strategy. Able to load resource, file or URL.
|
||||||
|
* Behavior may be altered by defining one of VM properties
|
||||||
|
* {@code config.resource}, {@code config.file} or {@code config.url}
|
||||||
|
*/
|
||||||
|
public class DefaultConfigLoadingStrategy implements ConfigLoadingStrategy {
|
||||||
|
@Override
|
||||||
|
public Config parseApplicationConfig(ConfigParseOptions parseOptions) {
|
||||||
|
ClassLoader loader = parseOptions.getClassLoader();
|
||||||
|
if (loader == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"ClassLoader should have been set here; bug in ConfigFactory. "
|
||||||
|
+ "(You can probably work around this bug by passing in a class loader or calling currentThread().setContextClassLoader() though.)");
|
||||||
|
|
||||||
|
int specified = 0;
|
||||||
|
|
||||||
|
// override application.conf with config.file, config.resource,
|
||||||
|
// config.url if requested.
|
||||||
|
String resource = System.getProperty("config.resource");
|
||||||
|
if (resource != null)
|
||||||
|
specified += 1;
|
||||||
|
String file = System.getProperty("config.file");
|
||||||
|
if (file != null)
|
||||||
|
specified += 1;
|
||||||
|
String url = System.getProperty("config.url");
|
||||||
|
if (url != null)
|
||||||
|
specified += 1;
|
||||||
|
|
||||||
|
if (specified == 0) {
|
||||||
|
return ConfigFactory.parseResourcesAnySyntax("application", parseOptions);
|
||||||
|
} else if (specified > 1) {
|
||||||
|
throw new ConfigException.Generic("You set more than one of config.file='" + file
|
||||||
|
+ "', config.url='" + url + "', config.resource='" + resource
|
||||||
|
+ "'; don't know which one to use!");
|
||||||
|
} else {
|
||||||
|
// the override file/url/resource MUST be present or it's an error
|
||||||
|
ConfigParseOptions overrideOptions = parseOptions.setAllowMissing(false);
|
||||||
|
if (resource != null) {
|
||||||
|
if (resource.startsWith("/"))
|
||||||
|
resource = resource.substring(1);
|
||||||
|
// this deliberately does not parseResourcesAnySyntax; if
|
||||||
|
// people want that they can use an include statement.
|
||||||
|
return ConfigFactory.parseResources(loader, resource, overrideOptions);
|
||||||
|
} else if (file != null) {
|
||||||
|
return ConfigFactory.parseFile(new File(file), overrideOptions);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return ConfigFactory.parseURL(new URL(url), overrideOptions);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new ConfigException.Generic("Bad URL in config.url system property: '"
|
||||||
|
+ url + "': " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an config property to be {@code null}.
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Optional {
|
||||||
|
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.parser.ConfigNode;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
abstract class AbstractConfigNode implements ConfigNode {
|
||||||
|
abstract Collection<Token> tokens();
|
||||||
|
final public String render() {
|
||||||
|
StringBuilder origText = new StringBuilder();
|
||||||
|
Iterable<Token> tokens = tokens();
|
||||||
|
for (Token t : tokens) {
|
||||||
|
origText.append(t.tokenText());
|
||||||
|
}
|
||||||
|
return origText.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public boolean equals(Object other) {
|
||||||
|
return other instanceof AbstractConfigNode && render().equals(((AbstractConfigNode)other).render());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public int hashCode() {
|
||||||
|
return render().hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
// This is required if we want
|
||||||
|
// to be referencing the AbstractConfigNode class in implementation rather than the
|
||||||
|
// ConfigNode interface, as we can't cast an AbstractConfigNode to an interface
|
||||||
|
abstract class AbstractConfigNodeValue extends AbstractConfigNode {
|
||||||
|
|
||||||
|
}
|
||||||
+221
@@ -0,0 +1,221 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigMergeable;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
abstract class AbstractConfigObject extends AbstractConfigValue implements ConfigObject, Container {
|
||||||
|
final private SimpleConfig config;
|
||||||
|
|
||||||
|
protected AbstractConfigObject(ConfigOrigin origin) {
|
||||||
|
super(origin);
|
||||||
|
this.config = new SimpleConfig(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfig toConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigObject toFallbackValue() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract public AbstractConfigObject withOnlyKey(String key);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract public AbstractConfigObject withoutKey(String key);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract public AbstractConfigObject withValue(String key, ConfigValue value);
|
||||||
|
|
||||||
|
abstract protected AbstractConfigObject withOnlyPathOrNull(Path path);
|
||||||
|
|
||||||
|
abstract AbstractConfigObject withOnlyPath(Path path);
|
||||||
|
|
||||||
|
abstract AbstractConfigObject withoutPath(Path path);
|
||||||
|
|
||||||
|
abstract AbstractConfigObject withValue(Path path, ConfigValue value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This looks up the key with no transformation or type conversion of any
|
||||||
|
* kind, and returns null if the key is not present. The object must be
|
||||||
|
* resolved along the nodes needed to get the key or
|
||||||
|
* ConfigException.NotResolved will be thrown.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return the unmodified raw value or null
|
||||||
|
*/
|
||||||
|
protected final AbstractConfigValue peekAssumingResolved(String key, Path originalPath) {
|
||||||
|
try {
|
||||||
|
return attemptPeekWithPartialResolve(key);
|
||||||
|
} catch (ConfigException.NotResolved e) {
|
||||||
|
throw ConfigImpl.improveNotResolved(originalPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up the key on an only-partially-resolved object, with no
|
||||||
|
* transformation or type conversion of any kind; if 'this' is not resolved
|
||||||
|
* then try to look up the key anyway if possible.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* key to look up
|
||||||
|
* @return the value of the key, or null if known not to exist
|
||||||
|
* @throws ConfigException.NotResolved
|
||||||
|
* if can't figure out key's value (or existence) without more
|
||||||
|
* resolving
|
||||||
|
*/
|
||||||
|
abstract AbstractConfigValue attemptPeekWithPartialResolve(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the path with no transformation or type conversion. Returns null
|
||||||
|
* if the path is not found; throws ConfigException.NotResolved if we need
|
||||||
|
* to go through an unresolved node to look up the path.
|
||||||
|
*/
|
||||||
|
protected AbstractConfigValue peekPath(Path path) {
|
||||||
|
return peekPath(this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigValue peekPath(AbstractConfigObject self, Path path) {
|
||||||
|
try {
|
||||||
|
// we'll fail if anything along the path can't
|
||||||
|
// be looked at without resolving.
|
||||||
|
Path next = path.remainder();
|
||||||
|
AbstractConfigValue v = self.attemptPeekWithPartialResolve(path.first());
|
||||||
|
|
||||||
|
if (next == null) {
|
||||||
|
return v;
|
||||||
|
} else {
|
||||||
|
if (v instanceof AbstractConfigObject) {
|
||||||
|
return peekPath((AbstractConfigObject) v, next);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ConfigException.NotResolved e) {
|
||||||
|
throw ConfigImpl.improveNotResolved(path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract AbstractConfigObject newCopy(ResolveStatus status, ConfigOrigin origin);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigObject newCopy(ConfigOrigin origin) {
|
||||||
|
return newCopy(resolveStatus(), origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigObject constructDelayedMerge(ConfigOrigin origin,
|
||||||
|
List<AbstractConfigValue> stack) {
|
||||||
|
return new ConfigDelayedMergeObject(origin, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected abstract AbstractConfigObject mergedWithObject(AbstractConfigObject fallback);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigObject withFallback(ConfigMergeable mergeable) {
|
||||||
|
return (AbstractConfigObject) super.withFallback(mergeable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigOrigin mergeOrigins(
|
||||||
|
Collection<? extends AbstractConfigValue> stack) {
|
||||||
|
if (stack.isEmpty())
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"can't merge origins on empty list");
|
||||||
|
List<ConfigOrigin> origins = new ArrayList<ConfigOrigin>();
|
||||||
|
ConfigOrigin firstOrigin = null;
|
||||||
|
int numMerged = 0;
|
||||||
|
for (AbstractConfigValue v : stack) {
|
||||||
|
if (firstOrigin == null)
|
||||||
|
firstOrigin = v.origin();
|
||||||
|
|
||||||
|
if (v instanceof AbstractConfigObject
|
||||||
|
&& ((AbstractConfigObject) v).resolveStatus() == ResolveStatus.RESOLVED
|
||||||
|
&& ((ConfigObject) v).isEmpty()) {
|
||||||
|
// don't include empty files or the .empty()
|
||||||
|
// config in the description, since they are
|
||||||
|
// likely to be "implementation details"
|
||||||
|
} else {
|
||||||
|
origins.add(v.origin());
|
||||||
|
numMerged += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numMerged == 0) {
|
||||||
|
// the configs were all empty, so just use the first one
|
||||||
|
origins.add(firstOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SimpleConfigOrigin.mergeOrigins(origins);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigOrigin mergeOrigins(AbstractConfigObject... stack) {
|
||||||
|
return mergeOrigins(Arrays.asList(stack));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract ResolveResult<? extends AbstractConfigObject> resolveSubstitutions(ResolveContext context,
|
||||||
|
ResolveSource source)
|
||||||
|
throws NotPossibleToResolve;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract AbstractConfigObject relativized(final Path prefix);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract AbstractConfigValue get(Object key);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected abstract void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options);
|
||||||
|
|
||||||
|
private static UnsupportedOperationException weAreImmutable(String method) {
|
||||||
|
return new UnsupportedOperationException("ConfigObject is immutable, you can't call Map."
|
||||||
|
+ method);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw weAreImmutable("clear");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue put(String arg0, ConfigValue arg1) {
|
||||||
|
throw weAreImmutable("put");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends String, ? extends ConfigValue> arg0) {
|
||||||
|
throw weAreImmutable("putAll");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue remove(Object arg0) {
|
||||||
|
throw weAreImmutable("remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigObject withOrigin(ConfigOrigin origin) {
|
||||||
|
return (AbstractConfigObject) super.withOrigin(origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
+411
@@ -0,0 +1,411 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigMergeable;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Trying very hard to avoid a parent reference in config values; when you have
|
||||||
|
* a tree like this, the availability of parent() tends to result in a lot of
|
||||||
|
* improperly-factored and non-modular code. Please don't add parent().
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
||||||
|
|
||||||
|
final private SimpleConfigOrigin origin;
|
||||||
|
|
||||||
|
AbstractConfigValue(ConfigOrigin origin) {
|
||||||
|
this.origin = (SimpleConfigOrigin) origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigOrigin origin() {
|
||||||
|
return this.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception means that a value is inherently not resolveable, at the
|
||||||
|
* moment the only known cause is a cycle of substitutions. This is a
|
||||||
|
* checked exception since it's internal to the library and we want to be
|
||||||
|
* sure we handle it before passing it out to public API. This is only
|
||||||
|
* supposed to be thrown by the target of a cyclic reference and it's
|
||||||
|
* supposed to be caught by the ConfigReference looking up that reference,
|
||||||
|
* so it should be impossible for an outermost resolve() to throw this.
|
||||||
|
*
|
||||||
|
* Contrast with ConfigException.NotResolved which just means nobody called
|
||||||
|
* resolve().
|
||||||
|
*/
|
||||||
|
static class NotPossibleToResolve extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
final private String traceString;
|
||||||
|
|
||||||
|
NotPossibleToResolve(ResolveContext context) {
|
||||||
|
super("was not possible to resolve");
|
||||||
|
this.traceString = context.traceString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String traceString() {
|
||||||
|
return traceString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called only by ResolveContext.resolve().
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* state of the current resolve
|
||||||
|
* @param source
|
||||||
|
* where to look up values
|
||||||
|
* @return a new value if there were changes, or this if no changes
|
||||||
|
*/
|
||||||
|
ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(ResolveContext context, ResolveSource source)
|
||||||
|
throws NotPossibleToResolve {
|
||||||
|
return ResolveResult.make(context, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveStatus resolveStatus() {
|
||||||
|
return ResolveStatus.RESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static List<AbstractConfigValue> replaceChildInList(List<AbstractConfigValue> list,
|
||||||
|
AbstractConfigValue child, AbstractConfigValue replacement) {
|
||||||
|
int i = 0;
|
||||||
|
while (i < list.size() && list.get(i) != child)
|
||||||
|
++i;
|
||||||
|
if (i == list.size())
|
||||||
|
throw new ConfigException.BugOrBroken("tried to replace " + child + " which is not in " + list);
|
||||||
|
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>(list);
|
||||||
|
if (replacement != null)
|
||||||
|
newStack.set(i, replacement);
|
||||||
|
else
|
||||||
|
newStack.remove(i);
|
||||||
|
|
||||||
|
if (newStack.isEmpty())
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return newStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean hasDescendantInList(List<AbstractConfigValue> list, AbstractConfigValue descendant) {
|
||||||
|
for (AbstractConfigValue v : list) {
|
||||||
|
if (v == descendant)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// now the expensive traversal
|
||||||
|
for (AbstractConfigValue v : list) {
|
||||||
|
if (v instanceof Container && ((Container) v).hasDescendant(descendant))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used when including one file in another; the included file is
|
||||||
|
* relativized to the path it's included into in the parent file. The point
|
||||||
|
* is that if you include a file at foo.bar in the parent, and the included
|
||||||
|
* file as a substitution ${a.b.c}, the included substitution now needs to
|
||||||
|
* be ${foo.bar.a.b.c} because we resolve substitutions globally only after
|
||||||
|
* parsing everything.
|
||||||
|
*
|
||||||
|
* @param prefix
|
||||||
|
* @return value relativized to the given path or the same value if nothing
|
||||||
|
* to do
|
||||||
|
*/
|
||||||
|
AbstractConfigValue relativized(Path prefix) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected interface Modifier {
|
||||||
|
// keyOrNull is null for non-objects
|
||||||
|
AbstractConfigValue modifyChildMayThrow(String keyOrNull, AbstractConfigValue v)
|
||||||
|
throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract class NoExceptionsModifier implements Modifier {
|
||||||
|
@Override
|
||||||
|
public final AbstractConfigValue modifyChildMayThrow(String keyOrNull, AbstractConfigValue v)
|
||||||
|
throws Exception {
|
||||||
|
try {
|
||||||
|
return modifyChild(keyOrNull, v);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new ConfigException.BugOrBroken("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract AbstractConfigValue modifyChild(String keyOrNull, AbstractConfigValue v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue toFallbackValue() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract AbstractConfigValue newCopy(ConfigOrigin origin);
|
||||||
|
|
||||||
|
// this is virtualized rather than a field because only some subclasses
|
||||||
|
// really need to store the boolean, and they may be able to pack it
|
||||||
|
// with another boolean to save space.
|
||||||
|
protected boolean ignoresFallbacks() {
|
||||||
|
// if we are not resolved, then somewhere in this value there's
|
||||||
|
// a substitution that may need to look at the fallbacks.
|
||||||
|
return resolveStatus() == ResolveStatus.RESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractConfigValue withFallbacksIgnored() {
|
||||||
|
if (ignoresFallbacks())
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"value class doesn't implement forced fallback-ignoring " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the withFallback() implementation is supposed to avoid calling
|
||||||
|
// mergedWith* if we're ignoring fallbacks.
|
||||||
|
protected final void requireNotIgnoringFallbacks() {
|
||||||
|
if (ignoresFallbacks())
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"method should not have been called with ignoresFallbacks=true "
|
||||||
|
+ getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractConfigValue constructDelayedMerge(ConfigOrigin origin,
|
||||||
|
List<AbstractConfigValue> stack) {
|
||||||
|
return new ConfigDelayedMerge(origin, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final AbstractConfigValue mergedWithTheUnmergeable(
|
||||||
|
Collection<AbstractConfigValue> stack, Unmergeable fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
// if we turn out to be an object, and the fallback also does,
|
||||||
|
// then a merge may be required; delay until we resolve.
|
||||||
|
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
|
||||||
|
newStack.addAll(stack);
|
||||||
|
newStack.addAll(fallback.unmergedValues());
|
||||||
|
return constructDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AbstractConfigValue delayMerge(Collection<AbstractConfigValue> stack,
|
||||||
|
AbstractConfigValue fallback) {
|
||||||
|
// if we turn out to be an object, and the fallback also does,
|
||||||
|
// then a merge may be required.
|
||||||
|
// if we contain a substitution, resolving it may need to look
|
||||||
|
// back to the fallback.
|
||||||
|
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
|
||||||
|
newStack.addAll(stack);
|
||||||
|
newStack.add(fallback);
|
||||||
|
return constructDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final AbstractConfigValue mergedWithObject(Collection<AbstractConfigValue> stack,
|
||||||
|
AbstractConfigObject fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
if (this instanceof AbstractConfigObject)
|
||||||
|
throw new ConfigException.BugOrBroken("Objects must reimplement mergedWithObject");
|
||||||
|
|
||||||
|
return mergedWithNonObject(stack, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final AbstractConfigValue mergedWithNonObject(Collection<AbstractConfigValue> stack,
|
||||||
|
AbstractConfigValue fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
if (resolveStatus() == ResolveStatus.RESOLVED) {
|
||||||
|
// falling back to a non-object doesn't merge anything, and also
|
||||||
|
// prohibits merging any objects that we fall back to later.
|
||||||
|
// so we have to switch to ignoresFallbacks mode.
|
||||||
|
return withFallbacksIgnored();
|
||||||
|
} else {
|
||||||
|
// if unresolved, we may have to look back to fallbacks as part of
|
||||||
|
// the resolution process, so always delay
|
||||||
|
return delayMerge(stack, fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractConfigValue mergedWithTheUnmergeable(Unmergeable fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
return mergedWithTheUnmergeable(Collections.singletonList(this), fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractConfigValue mergedWithObject(AbstractConfigObject fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
return mergedWithObject(Collections.singletonList(this), fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractConfigValue mergedWithNonObject(AbstractConfigValue fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
return mergedWithNonObject(Collections.singletonList(this), fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue withOrigin(ConfigOrigin origin) {
|
||||||
|
if (this.origin == origin)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return newCopy(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is only overridden to change the return type
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue withFallback(ConfigMergeable mergeable) {
|
||||||
|
if (ignoresFallbacks()) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
ConfigValue other = ((MergeableValue) mergeable).toFallbackValue();
|
||||||
|
|
||||||
|
if (other instanceof Unmergeable) {
|
||||||
|
return mergedWithTheUnmergeable((Unmergeable) other);
|
||||||
|
} else if (other instanceof AbstractConfigObject) {
|
||||||
|
return mergedWithObject((AbstractConfigObject) other);
|
||||||
|
} else {
|
||||||
|
return mergedWithNonObject((AbstractConfigValue) other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof ConfigValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
if (other instanceof ConfigValue) {
|
||||||
|
return canEqual(other)
|
||||||
|
&& (this.valueType() ==
|
||||||
|
((ConfigValue) other).valueType())
|
||||||
|
&& ConfigImplUtil.equalsHandlingNull(this.unwrapped(),
|
||||||
|
((ConfigValue) other).unwrapped());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
Object o = this.unwrapped();
|
||||||
|
if (o == null)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return o.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
render(sb, 0, true /* atRoot */, null /* atKey */, ConfigRenderOptions.concise());
|
||||||
|
return getClass().getSimpleName() + "(" + sb.toString() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void indent(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
||||||
|
if (options.getFormatted()) {
|
||||||
|
int remaining = indent;
|
||||||
|
while (remaining > 0) {
|
||||||
|
sb.append(" ");
|
||||||
|
--remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||||
|
if (atKey != null) {
|
||||||
|
String renderedKey;
|
||||||
|
if (options.getJson())
|
||||||
|
renderedKey = ConfigImplUtil.renderJsonString(atKey);
|
||||||
|
else
|
||||||
|
renderedKey = ConfigImplUtil.renderStringUnquotedIfPossible(atKey);
|
||||||
|
|
||||||
|
sb.append(renderedKey);
|
||||||
|
|
||||||
|
if (options.getJson()) {
|
||||||
|
if (options.getFormatted())
|
||||||
|
sb.append(" : ");
|
||||||
|
else
|
||||||
|
sb.append(":");
|
||||||
|
} else {
|
||||||
|
// in non-JSON we can omit the colon or equals before an object
|
||||||
|
if (this instanceof ConfigObject) {
|
||||||
|
if (options.getFormatted())
|
||||||
|
sb.append(' ');
|
||||||
|
} else {
|
||||||
|
sb.append("=");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render(sb, indent, atRoot, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
Object u = unwrapped();
|
||||||
|
sb.append(u.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String render() {
|
||||||
|
return render(ConfigRenderOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String render(ConfigRenderOptions options) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
render(sb, 0, true, null, options);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// toString() is a debugging-oriented string but this is defined
|
||||||
|
// to create a string that would parse back to the value in JSON.
|
||||||
|
// It only works for primitive values (that would be a single token)
|
||||||
|
// which are auto-converted to strings when concatenating with
|
||||||
|
// other strings or by the DefaultTransformer.
|
||||||
|
String transformToString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfig atKey(ConfigOrigin origin, String key) {
|
||||||
|
Map<String, AbstractConfigValue> m = Collections.singletonMap(key, this);
|
||||||
|
return (new SimpleConfigObject(origin, m)).toConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfig atKey(String key) {
|
||||||
|
return atKey(SimpleConfigOrigin.newSimple("atKey(" + key + ")"), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfig atPath(ConfigOrigin origin, Path path) {
|
||||||
|
Path parent = path.parent();
|
||||||
|
SimpleConfig result = atKey(origin, path.last());
|
||||||
|
while (parent != null) {
|
||||||
|
String key = parent.last();
|
||||||
|
result = result.atKey(origin, key);
|
||||||
|
parent = parent.parent();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfig atPath(String pathExpression) {
|
||||||
|
SimpleConfigOrigin origin = SimpleConfigOrigin.newSimple("atPath(" + pathExpression + ")");
|
||||||
|
return atPath(origin, Path.newPath(pathExpression));
|
||||||
|
}
|
||||||
|
}
|
||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.beans.BeanInfo;
|
||||||
|
import java.beans.IntrospectionException;
|
||||||
|
import java.beans.Introspector;
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.Config;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigList;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigMemorySize;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation detail, not ABI stable, do not touch.
|
||||||
|
* For use only by the {@link com.typesafe.config} package.
|
||||||
|
*/
|
||||||
|
public class ConfigBeanImpl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is public ONLY for use by the "config" package, DO NOT USE this ABI
|
||||||
|
* may change.
|
||||||
|
* @param <T> type of the bean
|
||||||
|
* @param config config to use
|
||||||
|
* @param clazz class of the bean
|
||||||
|
* @return the bean instance
|
||||||
|
*/
|
||||||
|
public static <T> T createInternal(Config config, Class<T> clazz) {
|
||||||
|
if (((SimpleConfig)config).root().resolveStatus() != ResolveStatus.RESOLVED)
|
||||||
|
throw new ConfigException.NotResolved(
|
||||||
|
"need to Config#resolve() a config before using it to initialize a bean, see the API docs for Config#resolve()");
|
||||||
|
|
||||||
|
Map<String, AbstractConfigValue> configProps = new HashMap<String, AbstractConfigValue>();
|
||||||
|
Map<String, String> originalNames = new HashMap<String, String>();
|
||||||
|
for (Map.Entry<String, ConfigValue> configProp : config.root().entrySet()) {
|
||||||
|
String originalName = configProp.getKey();
|
||||||
|
String camelName = ConfigImplUtil.toCamelCase(originalName);
|
||||||
|
// if a setting is in there both as some hyphen name and the camel name,
|
||||||
|
// the camel one wins
|
||||||
|
if (originalNames.containsKey(camelName) && !originalName.equals(camelName)) {
|
||||||
|
// if we aren't a camel name to start with, we lose.
|
||||||
|
// if we are or we are the first matching key, we win.
|
||||||
|
} else {
|
||||||
|
configProps.put(camelName, (AbstractConfigValue) configProp.getValue());
|
||||||
|
originalNames.put(camelName, originalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BeanInfo beanInfo = null;
|
||||||
|
try {
|
||||||
|
beanInfo = Introspector.getBeanInfo(clazz);
|
||||||
|
} catch (IntrospectionException e) {
|
||||||
|
throw new ConfigException.BadBean("Could not get bean information for class " + clazz.getName(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<PropertyDescriptor> beanProps = new ArrayList<PropertyDescriptor>();
|
||||||
|
for (PropertyDescriptor beanProp : beanInfo.getPropertyDescriptors()) {
|
||||||
|
if (beanProp.getReadMethod() == null || beanProp.getWriteMethod() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
beanProps.add(beanProp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to throw all validation issues at once (this does not comprehensively
|
||||||
|
// find every issue, but it should find common ones).
|
||||||
|
List<ConfigException.ValidationProblem> problems = new ArrayList<ConfigException.ValidationProblem>();
|
||||||
|
for (PropertyDescriptor beanProp : beanProps) {
|
||||||
|
Method setter = beanProp.getWriteMethod();
|
||||||
|
Class<?> parameterClass = setter.getParameterTypes()[0];
|
||||||
|
|
||||||
|
ConfigValueType expectedType = getValueTypeOrNull(parameterClass);
|
||||||
|
if (expectedType != null) {
|
||||||
|
String name = originalNames.get(beanProp.getName());
|
||||||
|
if (name == null)
|
||||||
|
name = beanProp.getName();
|
||||||
|
Path path = Path.newKey(name);
|
||||||
|
AbstractConfigValue configValue = configProps.get(beanProp.getName());
|
||||||
|
if (configValue != null) {
|
||||||
|
SimpleConfig.checkValid(path, expectedType, configValue, problems);
|
||||||
|
} else {
|
||||||
|
if (!isOptionalProperty(clazz, beanProp)) {
|
||||||
|
SimpleConfig.addMissing(problems, expectedType, path, config.origin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!problems.isEmpty()) {
|
||||||
|
throw new ConfigException.ValidationFailed(problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the bean instance
|
||||||
|
T bean = clazz.newInstance();
|
||||||
|
for (PropertyDescriptor beanProp : beanProps) {
|
||||||
|
Method setter = beanProp.getWriteMethod();
|
||||||
|
Type parameterType = setter.getGenericParameterTypes()[0];
|
||||||
|
Class<?> parameterClass = setter.getParameterTypes()[0];
|
||||||
|
String configPropName = originalNames.get(beanProp.getName());
|
||||||
|
// Is the property key missing in the config?
|
||||||
|
if (configPropName == null) {
|
||||||
|
// If so, continue if the field is marked as @{link Optional}
|
||||||
|
if (isOptionalProperty(clazz, beanProp)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Otherwise, raise a {@link Missing} exception right here
|
||||||
|
throw new ConfigException.Missing(beanProp.getName());
|
||||||
|
}
|
||||||
|
Object unwrapped = getValue(clazz, parameterType, parameterClass, config, configPropName);
|
||||||
|
setter.invoke(bean, unwrapped);
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
throw new ConfigException.BadBean(clazz.getName() + " needs a public no-args constructor to be used as a bean", e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new ConfigException.BadBean(clazz.getName() + " getters and setters are not accessible, they must be for use as a bean", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new ConfigException.BadBean("Calling bean method on " + clazz.getName() + " caused an exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we could magically make this work in many cases by doing
|
||||||
|
// getAnyRef() (or getValue().unwrapped()), but anytime we
|
||||||
|
// rely on that, we aren't doing the type conversions Config
|
||||||
|
// usually does, and we will throw ClassCastException instead
|
||||||
|
// of a nicer error message giving the name of the bad
|
||||||
|
// setting. So, instead, we only support a limited number of
|
||||||
|
// types plus you can always use Object, ConfigValue, Config,
|
||||||
|
// ConfigObject, etc. as an escape hatch.
|
||||||
|
private static Object getValue(Class<?> beanClass, Type parameterType, Class<?> parameterClass, Config config,
|
||||||
|
String configPropName) {
|
||||||
|
if (parameterClass == Boolean.class || parameterClass == boolean.class) {
|
||||||
|
return config.getBoolean(configPropName);
|
||||||
|
} else if (parameterClass == Integer.class || parameterClass == int.class) {
|
||||||
|
return config.getInt(configPropName);
|
||||||
|
} else if (parameterClass == Double.class || parameterClass == double.class) {
|
||||||
|
return config.getDouble(configPropName);
|
||||||
|
} else if (parameterClass == Long.class || parameterClass == long.class) {
|
||||||
|
return config.getLong(configPropName);
|
||||||
|
} else if (parameterClass == String.class) {
|
||||||
|
return config.getString(configPropName);
|
||||||
|
} else if (parameterClass == Duration.class) {
|
||||||
|
return config.getDuration(configPropName);
|
||||||
|
} else if (parameterClass == ConfigMemorySize.class) {
|
||||||
|
return config.getMemorySize(configPropName);
|
||||||
|
} else if (parameterClass == Object.class) {
|
||||||
|
return config.getAnyRef(configPropName);
|
||||||
|
} else if (parameterClass == List.class) {
|
||||||
|
return getListValue(beanClass, parameterType, parameterClass, config, configPropName);
|
||||||
|
} else if (parameterClass == Set.class) {
|
||||||
|
return getSetValue(beanClass, parameterType, parameterClass, config, configPropName);
|
||||||
|
} else if (parameterClass == Map.class) {
|
||||||
|
// we could do better here, but right now we don't.
|
||||||
|
Type[] typeArgs = ((ParameterizedType)parameterType).getActualTypeArguments();
|
||||||
|
if (typeArgs[0] != String.class || typeArgs[1] != Object.class) {
|
||||||
|
throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported Map<" + typeArgs[0] + "," + typeArgs[1] + ">, only Map<String,Object> is supported right now");
|
||||||
|
}
|
||||||
|
return config.getObject(configPropName).unwrapped();
|
||||||
|
} else if (parameterClass == Config.class) {
|
||||||
|
return config.getConfig(configPropName);
|
||||||
|
} else if (parameterClass == ConfigObject.class) {
|
||||||
|
return config.getObject(configPropName);
|
||||||
|
} else if (parameterClass == ConfigValue.class) {
|
||||||
|
return config.getValue(configPropName);
|
||||||
|
} else if (parameterClass == ConfigList.class) {
|
||||||
|
return config.getList(configPropName);
|
||||||
|
} else if (parameterClass.isEnum()) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Enum enumValue = config.getEnum((Class<Enum>) parameterClass, configPropName);
|
||||||
|
return enumValue;
|
||||||
|
} else if (hasAtLeastOneBeanProperty(parameterClass)) {
|
||||||
|
return createInternal(config.getConfig(configPropName), parameterClass);
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BadBean("Bean property " + configPropName + " of class " + beanClass.getName() + " has unsupported type " + parameterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getSetValue(Class<?> beanClass, Type parameterType, Class<?> parameterClass, Config config, String configPropName) {
|
||||||
|
return new HashSet((List) getListValue(beanClass, parameterType, parameterClass, config, configPropName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getListValue(Class<?> beanClass, Type parameterType, Class<?> parameterClass, Config config, String configPropName) {
|
||||||
|
Type elementType = ((ParameterizedType)parameterType).getActualTypeArguments()[0];
|
||||||
|
|
||||||
|
if (elementType == Boolean.class) {
|
||||||
|
return config.getBooleanList(configPropName);
|
||||||
|
} else if (elementType == Integer.class) {
|
||||||
|
return config.getIntList(configPropName);
|
||||||
|
} else if (elementType == Double.class) {
|
||||||
|
return config.getDoubleList(configPropName);
|
||||||
|
} else if (elementType == Long.class) {
|
||||||
|
return config.getLongList(configPropName);
|
||||||
|
} else if (elementType == String.class) {
|
||||||
|
return config.getStringList(configPropName);
|
||||||
|
} else if (elementType == Duration.class) {
|
||||||
|
return config.getDurationList(configPropName);
|
||||||
|
} else if (elementType == ConfigMemorySize.class) {
|
||||||
|
return config.getMemorySizeList(configPropName);
|
||||||
|
} else if (elementType == Object.class) {
|
||||||
|
return config.getAnyRefList(configPropName);
|
||||||
|
} else if (elementType == Config.class) {
|
||||||
|
return config.getConfigList(configPropName);
|
||||||
|
} else if (elementType == ConfigObject.class) {
|
||||||
|
return config.getObjectList(configPropName);
|
||||||
|
} else if (elementType == ConfigValue.class) {
|
||||||
|
return config.getList(configPropName);
|
||||||
|
} else if (((Class<?>) elementType).isEnum()) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Enum> enumValues = config.getEnumList((Class<Enum>) elementType, configPropName);
|
||||||
|
return enumValues;
|
||||||
|
} else if (hasAtLeastOneBeanProperty((Class<?>) elementType)) {
|
||||||
|
List<Object> beanList = new ArrayList<Object>();
|
||||||
|
List<? extends Config> configList = config.getConfigList(configPropName);
|
||||||
|
for (Config listMember : configList) {
|
||||||
|
beanList.add(createInternal(listMember, (Class<?>) elementType));
|
||||||
|
}
|
||||||
|
return beanList;
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported list element type " + elementType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// null if we can't easily say; this is heuristic/best-effort
|
||||||
|
private static ConfigValueType getValueTypeOrNull(Class<?> parameterClass) {
|
||||||
|
if (parameterClass == Boolean.class || parameterClass == boolean.class) {
|
||||||
|
return ConfigValueType.BOOLEAN;
|
||||||
|
} else if (parameterClass == Integer.class || parameterClass == int.class) {
|
||||||
|
return ConfigValueType.NUMBER;
|
||||||
|
} else if (parameterClass == Double.class || parameterClass == double.class) {
|
||||||
|
return ConfigValueType.NUMBER;
|
||||||
|
} else if (parameterClass == Long.class || parameterClass == long.class) {
|
||||||
|
return ConfigValueType.NUMBER;
|
||||||
|
} else if (parameterClass == String.class) {
|
||||||
|
return ConfigValueType.STRING;
|
||||||
|
} else if (parameterClass == Duration.class) {
|
||||||
|
return null;
|
||||||
|
} else if (parameterClass == ConfigMemorySize.class) {
|
||||||
|
return null;
|
||||||
|
} else if (parameterClass == List.class) {
|
||||||
|
return ConfigValueType.LIST;
|
||||||
|
} else if (parameterClass == Map.class) {
|
||||||
|
return ConfigValueType.OBJECT;
|
||||||
|
} else if (parameterClass == Config.class) {
|
||||||
|
return ConfigValueType.OBJECT;
|
||||||
|
} else if (parameterClass == ConfigObject.class) {
|
||||||
|
return ConfigValueType.OBJECT;
|
||||||
|
} else if (parameterClass == ConfigList.class) {
|
||||||
|
return ConfigValueType.LIST;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasAtLeastOneBeanProperty(Class<?> clazz) {
|
||||||
|
BeanInfo beanInfo = null;
|
||||||
|
try {
|
||||||
|
beanInfo = Introspector.getBeanInfo(clazz);
|
||||||
|
} catch (IntrospectionException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PropertyDescriptor beanProp : beanInfo.getPropertyDescriptors()) {
|
||||||
|
if (beanProp.getReadMethod() != null && beanProp.getWriteMethod() != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isOptionalProperty(Class beanClass, PropertyDescriptor beanProp) {
|
||||||
|
Field field = getField(beanClass, beanProp.getName());
|
||||||
|
return field != null && (field.getAnnotationsByType(Optional.class).length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getField(Class beanClass, String fieldName) {
|
||||||
|
try {
|
||||||
|
Field field = beanClass.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// Don't give up yet. Try to look for field in super class, if any.
|
||||||
|
}
|
||||||
|
beanClass = beanClass.getSuperclass();
|
||||||
|
if (beanClass == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getField(beanClass, fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
final class ConfigBoolean extends AbstractConfigValue implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
final private boolean value;
|
||||||
|
|
||||||
|
ConfigBoolean(ConfigOrigin origin, boolean value) {
|
||||||
|
super(origin);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.BOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean unwrapped() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String transformToString() {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigBoolean newCopy(ConfigOrigin origin) {
|
||||||
|
return new ConfigBoolean(origin, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ConfigConcatenation represents a list of values to be concatenated (see the
|
||||||
|
* spec). It only has to exist if at least one value is an unresolved
|
||||||
|
* substitution, otherwise we could go ahead and collapse the list into a single
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* Right now this is always a list of strings and ${} references, but in the
|
||||||
|
* future should support a list of ConfigList. We may also support
|
||||||
|
* concatenations of objects, but ConfigDelayedMerge should be used for that
|
||||||
|
* since a concat of objects really will merge, not concatenate.
|
||||||
|
*/
|
||||||
|
final class ConfigConcatenation extends AbstractConfigValue implements Unmergeable, Container {
|
||||||
|
|
||||||
|
final private List<AbstractConfigValue> pieces;
|
||||||
|
|
||||||
|
ConfigConcatenation(ConfigOrigin origin, List<AbstractConfigValue> pieces) {
|
||||||
|
super(origin);
|
||||||
|
this.pieces = pieces;
|
||||||
|
|
||||||
|
if (pieces.size() < 2)
|
||||||
|
throw new ConfigException.BugOrBroken("Created concatenation with less than 2 items: "
|
||||||
|
+ this);
|
||||||
|
|
||||||
|
boolean hadUnmergeable = false;
|
||||||
|
for (AbstractConfigValue p : pieces) {
|
||||||
|
if (p instanceof ConfigConcatenation)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"ConfigConcatenation should never be nested: " + this);
|
||||||
|
if (p instanceof Unmergeable)
|
||||||
|
hadUnmergeable = true;
|
||||||
|
}
|
||||||
|
if (!hadUnmergeable)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"Created concatenation without an unmergeable in it: " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigException.NotResolved notResolved() {
|
||||||
|
return new ConfigException.NotResolved(
|
||||||
|
"need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: "
|
||||||
|
+ this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object unwrapped() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigConcatenation newCopy(ConfigOrigin newOrigin) {
|
||||||
|
return new ConfigConcatenation(newOrigin, pieces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean ignoresFallbacks() {
|
||||||
|
// we can never ignore fallbacks because if a child ConfigReference
|
||||||
|
// is self-referential we have to look lower in the merge stack
|
||||||
|
// for its value.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigConcatenation> unmergedValues() {
|
||||||
|
return Collections.singleton(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isIgnoredWhitespace(AbstractConfigValue value) {
|
||||||
|
return (value instanceof ConfigString) && !((ConfigString)value).wasQuoted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add left and right, or their merger, to builder.
|
||||||
|
*/
|
||||||
|
private static void join(ArrayList<AbstractConfigValue> builder, AbstractConfigValue origRight) {
|
||||||
|
AbstractConfigValue left = builder.get(builder.size() - 1);
|
||||||
|
AbstractConfigValue right = origRight;
|
||||||
|
|
||||||
|
// check for an object which can be converted to a list
|
||||||
|
// (this will be an object with numeric keys, like foo.0, foo.1)
|
||||||
|
if (left instanceof ConfigObject && right instanceof SimpleConfigList) {
|
||||||
|
left = DefaultTransformer.transform(left, ConfigValueType.LIST);
|
||||||
|
} else if (left instanceof SimpleConfigList && right instanceof ConfigObject) {
|
||||||
|
right = DefaultTransformer.transform(right, ConfigValueType.LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since this depends on the type of two instances, I couldn't think
|
||||||
|
// of much alternative to an instanceof chain. Visitors are sometimes
|
||||||
|
// used for multiple dispatch but seems like overkill.
|
||||||
|
AbstractConfigValue joined = null;
|
||||||
|
if (left instanceof ConfigObject && right instanceof ConfigObject) {
|
||||||
|
joined = right.withFallback(left);
|
||||||
|
} else if (left instanceof SimpleConfigList && right instanceof SimpleConfigList) {
|
||||||
|
joined = ((SimpleConfigList)left).concatenate((SimpleConfigList)right);
|
||||||
|
} else if ((left instanceof SimpleConfigList || left instanceof ConfigObject) &&
|
||||||
|
isIgnoredWhitespace(right)) {
|
||||||
|
joined = left;
|
||||||
|
// it should be impossible that left is whitespace and right is a list or object
|
||||||
|
} else if (left instanceof ConfigConcatenation || right instanceof ConfigConcatenation) {
|
||||||
|
throw new ConfigException.BugOrBroken("unflattened ConfigConcatenation");
|
||||||
|
} else if (left instanceof Unmergeable || right instanceof Unmergeable) {
|
||||||
|
// leave joined=null, cannot join
|
||||||
|
} else {
|
||||||
|
// handle primitive type or primitive type mixed with object or list
|
||||||
|
String s1 = left.transformToString();
|
||||||
|
String s2 = right.transformToString();
|
||||||
|
if (s1 == null || s2 == null) {
|
||||||
|
throw new ConfigException.WrongType(left.origin(),
|
||||||
|
"Cannot concatenate object or list with a non-object-or-list, " + left
|
||||||
|
+ " and " + right + " are not compatible");
|
||||||
|
} else {
|
||||||
|
ConfigOrigin joinedOrigin = SimpleConfigOrigin.mergeOrigins(left.origin(),
|
||||||
|
right.origin());
|
||||||
|
joined = new ConfigString.Quoted(joinedOrigin, s1 + s2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (joined == null) {
|
||||||
|
builder.add(right);
|
||||||
|
} else {
|
||||||
|
builder.remove(builder.size() - 1);
|
||||||
|
builder.add(joined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<AbstractConfigValue> consolidate(List<AbstractConfigValue> pieces) {
|
||||||
|
if (pieces.size() < 2) {
|
||||||
|
return pieces;
|
||||||
|
} else {
|
||||||
|
List<AbstractConfigValue> flattened = new ArrayList<AbstractConfigValue>(pieces.size());
|
||||||
|
for (AbstractConfigValue v : pieces) {
|
||||||
|
if (v instanceof ConfigConcatenation) {
|
||||||
|
flattened.addAll(((ConfigConcatenation) v).pieces);
|
||||||
|
} else {
|
||||||
|
flattened.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<AbstractConfigValue> consolidated = new ArrayList<AbstractConfigValue>(
|
||||||
|
flattened.size());
|
||||||
|
for (AbstractConfigValue v : flattened) {
|
||||||
|
if (consolidated.isEmpty())
|
||||||
|
consolidated.add(v);
|
||||||
|
else
|
||||||
|
join(consolidated, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return consolidated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigValue concatenate(List<AbstractConfigValue> pieces) {
|
||||||
|
List<AbstractConfigValue> consolidated = consolidate(pieces);
|
||||||
|
if (consolidated.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else if (consolidated.size() == 1) {
|
||||||
|
return consolidated.get(0);
|
||||||
|
} else {
|
||||||
|
ConfigOrigin mergedOrigin = SimpleConfigOrigin.mergeOrigins(consolidated);
|
||||||
|
return new ConfigConcatenation(mergedOrigin, consolidated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(ResolveContext context, ResolveSource source)
|
||||||
|
throws NotPossibleToResolve {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||||
|
int indent = context.depth() + 2;
|
||||||
|
ConfigImpl.trace(indent - 1, "concatenation has " + pieces.size() + " pieces:");
|
||||||
|
int count = 0;
|
||||||
|
for (AbstractConfigValue v : pieces) {
|
||||||
|
ConfigImpl.trace(indent, count + ": " + v);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right now there's no reason to pushParent here because the
|
||||||
|
// content of ConfigConcatenation should not need to replaceChild,
|
||||||
|
// but if it did we'd have to do this.
|
||||||
|
ResolveSource sourceWithParent = source; // .pushParent(this);
|
||||||
|
ResolveContext newContext = context;
|
||||||
|
|
||||||
|
List<AbstractConfigValue> resolved = new ArrayList<AbstractConfigValue>(pieces.size());
|
||||||
|
for (AbstractConfigValue p : pieces) {
|
||||||
|
// to concat into a string we have to do a full resolve,
|
||||||
|
// so unrestrict the context, then put restriction back afterward
|
||||||
|
Path restriction = newContext.restrictToChild();
|
||||||
|
ResolveResult<? extends AbstractConfigValue> result = newContext.unrestricted()
|
||||||
|
.resolve(p, sourceWithParent);
|
||||||
|
AbstractConfigValue r = result.value;
|
||||||
|
newContext = result.context.restrict(restriction);
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(context.depth(), "resolved concat piece to " + r);
|
||||||
|
if (r == null) {
|
||||||
|
// it was optional... omit
|
||||||
|
} else {
|
||||||
|
resolved.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now need to concat everything
|
||||||
|
List<AbstractConfigValue> joined = consolidate(resolved);
|
||||||
|
// if unresolved is allowed we can just become another
|
||||||
|
// ConfigConcatenation
|
||||||
|
if (joined.size() > 1 && context.options().getAllowUnresolved())
|
||||||
|
return ResolveResult.make(newContext, new ConfigConcatenation(this.origin(), joined));
|
||||||
|
else if (joined.isEmpty())
|
||||||
|
// we had just a list of optional references using ${?}
|
||||||
|
return ResolveResult.make(newContext, null);
|
||||||
|
else if (joined.size() == 1)
|
||||||
|
return ResolveResult.make(newContext, joined.get(0));
|
||||||
|
else
|
||||||
|
throw new ConfigException.BugOrBroken("Bug in the library; resolved list was joined to too many values: "
|
||||||
|
+ joined);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveStatus resolveStatus() {
|
||||||
|
return ResolveStatus.UNRESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigConcatenation replaceChild(AbstractConfigValue child, AbstractConfigValue replacement) {
|
||||||
|
List<AbstractConfigValue> newPieces = replaceChildInList(pieces, child, replacement);
|
||||||
|
if (newPieces == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return new ConfigConcatenation(origin(), newPieces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDescendant(AbstractConfigValue descendant) {
|
||||||
|
return hasDescendantInList(pieces, descendant);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when you graft a substitution into another object,
|
||||||
|
// you have to prefix it with the location in that object
|
||||||
|
// where you grafted it; but save prefixLength so
|
||||||
|
// system property and env variable lookups don't get
|
||||||
|
// broken.
|
||||||
|
@Override
|
||||||
|
ConfigConcatenation relativized(Path prefix) {
|
||||||
|
List<AbstractConfigValue> newPieces = new ArrayList<AbstractConfigValue>();
|
||||||
|
for (AbstractConfigValue p : pieces) {
|
||||||
|
newPieces.add(p.relativized(prefix));
|
||||||
|
}
|
||||||
|
return new ConfigConcatenation(origin(), newPieces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof ConfigConcatenation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
if (other instanceof ConfigConcatenation) {
|
||||||
|
return canEqual(other) && this.pieces.equals(((ConfigConcatenation) other).pieces);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
return pieces.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
for (AbstractConfigValue p : pieces) {
|
||||||
|
p.render(sb, indent, atRoot, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+342
@@ -0,0 +1,342 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The issue here is that we want to first merge our stack of config files, and
|
||||||
|
* then we want to evaluate substitutions. But if two substitutions both expand
|
||||||
|
* to an object, we might need to merge those two objects. Thus, we can't ever
|
||||||
|
* "override" a substitution when we do a merge; instead we have to save the
|
||||||
|
* stack of values that should be merged, and resolve the merge when we evaluate
|
||||||
|
* substitutions.
|
||||||
|
*/
|
||||||
|
final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeable,
|
||||||
|
ReplaceableMergeStack {
|
||||||
|
|
||||||
|
// earlier items in the stack win
|
||||||
|
final private List<AbstractConfigValue> stack;
|
||||||
|
|
||||||
|
ConfigDelayedMerge(ConfigOrigin origin, List<AbstractConfigValue> stack) {
|
||||||
|
super(origin);
|
||||||
|
this.stack = stack;
|
||||||
|
if (stack.isEmpty())
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"creating empty delayed merge value");
|
||||||
|
|
||||||
|
for (AbstractConfigValue v : stack) {
|
||||||
|
if (v instanceof ConfigDelayedMerge || v instanceof ConfigDelayedMergeObject)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
throw new ConfigException.NotResolved(
|
||||||
|
"called valueType() on value with unresolved substitutions, need to Config#resolve() first, see API docs");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object unwrapped() {
|
||||||
|
throw new ConfigException.NotResolved(
|
||||||
|
"called unwrapped() on value with unresolved substitutions, need to Config#resolve() first, see API docs");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(ResolveContext context, ResolveSource source)
|
||||||
|
throws NotPossibleToResolve {
|
||||||
|
return resolveSubstitutions(this, stack, context, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static method also used by ConfigDelayedMergeObject
|
||||||
|
static ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(ReplaceableMergeStack replaceable,
|
||||||
|
List<AbstractConfigValue> stack,
|
||||||
|
ResolveContext context, ResolveSource source) throws NotPossibleToResolve {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||||
|
ConfigImpl.trace(context.depth(), "delayed merge stack has " + stack.size() + " items:");
|
||||||
|
int count = 0;
|
||||||
|
for (AbstractConfigValue v : stack) {
|
||||||
|
ConfigImpl.trace(context.depth() + 1, count + ": " + v);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to resolve substitutions, we need to recursively resolve
|
||||||
|
// the stack of stuff to merge, and merge the stack so
|
||||||
|
// we won't be a delayed merge anymore. If restrictToChildOrNull
|
||||||
|
// is non-null, or resolve options allow partial resolves,
|
||||||
|
// we may remain a delayed merge though.
|
||||||
|
|
||||||
|
ResolveContext newContext = context;
|
||||||
|
int count = 0;
|
||||||
|
AbstractConfigValue merged = null;
|
||||||
|
for (AbstractConfigValue end : stack) {
|
||||||
|
// the end value may or may not be resolved already
|
||||||
|
|
||||||
|
ResolveSource sourceForEnd;
|
||||||
|
|
||||||
|
if (end instanceof ReplaceableMergeStack)
|
||||||
|
throw new ConfigException.BugOrBroken("A delayed merge should not contain another one: " + replaceable);
|
||||||
|
else if (end instanceof Unmergeable) {
|
||||||
|
// the remainder could be any kind of value, including another
|
||||||
|
// ConfigDelayedMerge
|
||||||
|
AbstractConfigValue remainder = replaceable.makeReplacement(context, count + 1);
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(), "remainder portion: " + remainder);
|
||||||
|
|
||||||
|
// If, while resolving 'end' we come back to the same
|
||||||
|
// merge stack, we only want to look _below_ 'end'
|
||||||
|
// in the stack. So we arrange to replace the
|
||||||
|
// ConfigDelayedMerge with a value that is only
|
||||||
|
// the remainder of the stack below this one.
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(), "building sourceForEnd");
|
||||||
|
|
||||||
|
// we resetParents() here because we'll be resolving "end"
|
||||||
|
// against a root which does NOT contain "end"
|
||||||
|
sourceForEnd = source.replaceWithinCurrentParent((AbstractConfigValue) replaceable, remainder);
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(), " sourceForEnd before reset parents but after replace: "
|
||||||
|
+ sourceForEnd);
|
||||||
|
|
||||||
|
sourceForEnd = sourceForEnd.resetParents();
|
||||||
|
} else {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(),
|
||||||
|
"will resolve end against the original source with parent pushed");
|
||||||
|
|
||||||
|
sourceForEnd = source.pushParent(replaceable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||||
|
ConfigImpl.trace(newContext.depth(), "sourceForEnd =" + sourceForEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(), "Resolving highest-priority item in delayed merge " + end
|
||||||
|
+ " against " + sourceForEnd + " endWasRemoved=" + (source != sourceForEnd));
|
||||||
|
ResolveResult<? extends AbstractConfigValue> result = newContext.resolve(end, sourceForEnd);
|
||||||
|
AbstractConfigValue resolvedEnd = result.value;
|
||||||
|
newContext = result.context;
|
||||||
|
|
||||||
|
if (resolvedEnd != null) {
|
||||||
|
if (merged == null) {
|
||||||
|
merged = resolvedEnd;
|
||||||
|
} else {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth() + 1, "merging " + merged + " with fallback " + resolvedEnd);
|
||||||
|
merged = merged.withFallback(resolvedEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(), "stack merged, yielding: " + merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveResult.make(newContext, merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue makeReplacement(ResolveContext context, int skipping) {
|
||||||
|
return ConfigDelayedMerge.makeReplacement(context, stack, skipping);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static method also used by ConfigDelayedMergeObject; end may be null
|
||||||
|
static AbstractConfigValue makeReplacement(ResolveContext context, List<AbstractConfigValue> stack, int skipping) {
|
||||||
|
List<AbstractConfigValue> subStack = stack.subList(skipping, stack.size());
|
||||||
|
|
||||||
|
if (subStack.isEmpty()) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(context.depth(), "Nothing else in the merge stack, replacing with null");
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
// generate a new merge stack from only the remaining items
|
||||||
|
AbstractConfigValue merged = null;
|
||||||
|
for (AbstractConfigValue v : subStack) {
|
||||||
|
if (merged == null)
|
||||||
|
merged = v;
|
||||||
|
else
|
||||||
|
merged = merged.withFallback(v);
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveStatus resolveStatus() {
|
||||||
|
return ResolveStatus.UNRESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue replaceChild(AbstractConfigValue child, AbstractConfigValue replacement) {
|
||||||
|
List<AbstractConfigValue> newStack = replaceChildInList(stack, child, replacement);
|
||||||
|
if (newStack == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return new ConfigDelayedMerge(origin(), newStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDescendant(AbstractConfigValue descendant) {
|
||||||
|
return hasDescendantInList(stack, descendant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigDelayedMerge relativized(Path prefix) {
|
||||||
|
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
|
||||||
|
for (AbstractConfigValue o : stack) {
|
||||||
|
newStack.add(o.relativized(prefix));
|
||||||
|
}
|
||||||
|
return new ConfigDelayedMerge(origin(), newStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static utility shared with ConfigDelayedMergeObject
|
||||||
|
static boolean stackIgnoresFallbacks(List<AbstractConfigValue> stack) {
|
||||||
|
AbstractConfigValue last = stack.get(stack.size() - 1);
|
||||||
|
return last.ignoresFallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean ignoresFallbacks() {
|
||||||
|
return stackIgnoresFallbacks(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigValue newCopy(ConfigOrigin newOrigin) {
|
||||||
|
return new ConfigDelayedMerge(newOrigin, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final ConfigDelayedMerge mergedWithTheUnmergeable(Unmergeable fallback) {
|
||||||
|
return (ConfigDelayedMerge) mergedWithTheUnmergeable(stack, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final ConfigDelayedMerge mergedWithObject(AbstractConfigObject fallback) {
|
||||||
|
return (ConfigDelayedMerge) mergedWithObject(stack, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigDelayedMerge mergedWithNonObject(AbstractConfigValue fallback) {
|
||||||
|
return (ConfigDelayedMerge) mergedWithNonObject(stack, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<AbstractConfigValue> unmergedValues() {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof ConfigDelayedMerge;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
if (other instanceof ConfigDelayedMerge) {
|
||||||
|
return canEqual(other)
|
||||||
|
&& (this.stack == ((ConfigDelayedMerge) other).stack || this.stack
|
||||||
|
.equals(((ConfigDelayedMerge) other).stack));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
return stack.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||||
|
render(stack, sb, indent, atRoot, atKey, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
render(sb, indent, atRoot, null, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static method also used by ConfigDelayedMergeObject.
|
||||||
|
static void render(List<AbstractConfigValue> stack, StringBuilder sb, int indent, boolean atRoot, String atKey,
|
||||||
|
ConfigRenderOptions options) {
|
||||||
|
boolean commentMerge = options.getComments();
|
||||||
|
if (commentMerge) {
|
||||||
|
sb.append("# unresolved merge of " + stack.size() + " values follows (\n");
|
||||||
|
if (atKey == null) {
|
||||||
|
indent(sb, indent, options);
|
||||||
|
sb.append("# this unresolved merge will not be parseable because it's at the root of the object\n");
|
||||||
|
indent(sb, indent, options);
|
||||||
|
sb.append("# the HOCON format has no way to list multiple root objects in a single file\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AbstractConfigValue> reversed = new ArrayList<AbstractConfigValue>();
|
||||||
|
reversed.addAll(stack);
|
||||||
|
Collections.reverse(reversed);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (AbstractConfigValue v : reversed) {
|
||||||
|
if (commentMerge) {
|
||||||
|
indent(sb, indent, options);
|
||||||
|
if (atKey != null) {
|
||||||
|
sb.append("# unmerged value " + i + " for key "
|
||||||
|
+ ConfigImplUtil.renderJsonString(atKey) + " from ");
|
||||||
|
} else {
|
||||||
|
sb.append("# unmerged value " + i + " from ");
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
sb.append(v.origin().description());
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
for (String comment : v.origin().comments()) {
|
||||||
|
indent(sb, indent, options);
|
||||||
|
sb.append("# ");
|
||||||
|
sb.append(comment);
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent(sb, indent, options);
|
||||||
|
|
||||||
|
if (atKey != null) {
|
||||||
|
sb.append(ConfigImplUtil.renderJsonString(atKey));
|
||||||
|
if (options.getFormatted())
|
||||||
|
sb.append(" : ");
|
||||||
|
else
|
||||||
|
sb.append(":");
|
||||||
|
}
|
||||||
|
v.render(sb, indent, atRoot, options);
|
||||||
|
sb.append(",");
|
||||||
|
if (options.getFormatted())
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
// chop comma or newline
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
if (options.getFormatted()) {
|
||||||
|
sb.setLength(sb.length() - 1); // also chop comma
|
||||||
|
sb.append("\n"); // put a newline back
|
||||||
|
}
|
||||||
|
if (commentMerge) {
|
||||||
|
indent(sb, indent, options);
|
||||||
|
sb.append("# ) end of unresolved merge\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+326
@@ -0,0 +1,326 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigList;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigMergeable;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
|
||||||
|
// This is just like ConfigDelayedMerge except we know statically
|
||||||
|
// that it will turn out to be an object.
|
||||||
|
final class ConfigDelayedMergeObject extends AbstractConfigObject implements Unmergeable,
|
||||||
|
ReplaceableMergeStack {
|
||||||
|
|
||||||
|
final private List<AbstractConfigValue> stack;
|
||||||
|
|
||||||
|
ConfigDelayedMergeObject(ConfigOrigin origin, List<AbstractConfigValue> stack) {
|
||||||
|
super(origin);
|
||||||
|
this.stack = stack;
|
||||||
|
|
||||||
|
if (stack.isEmpty())
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"creating empty delayed merge object");
|
||||||
|
if (!(stack.get(0) instanceof AbstractConfigObject))
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"created a delayed merge object not guaranteed to be an object");
|
||||||
|
|
||||||
|
for (AbstractConfigValue v : stack) {
|
||||||
|
if (v instanceof ConfigDelayedMerge || v instanceof ConfigDelayedMergeObject)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"placed nested DelayedMerge in a ConfigDelayedMergeObject, should have consolidated stack");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigDelayedMergeObject newCopy(ResolveStatus status, ConfigOrigin origin) {
|
||||||
|
if (status != resolveStatus())
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"attempt to create resolved ConfigDelayedMergeObject");
|
||||||
|
return new ConfigDelayedMergeObject(origin, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveResult<? extends AbstractConfigObject> resolveSubstitutions(ResolveContext context, ResolveSource source)
|
||||||
|
throws NotPossibleToResolve {
|
||||||
|
ResolveResult<? extends AbstractConfigValue> merged = ConfigDelayedMerge.resolveSubstitutions(this, stack,
|
||||||
|
context, source);
|
||||||
|
return merged.asObjectResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue makeReplacement(ResolveContext context, int skipping) {
|
||||||
|
return ConfigDelayedMerge.makeReplacement(context, stack, skipping);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveStatus resolveStatus() {
|
||||||
|
return ResolveStatus.UNRESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue replaceChild(AbstractConfigValue child, AbstractConfigValue replacement) {
|
||||||
|
List<AbstractConfigValue> newStack = replaceChildInList(stack, child, replacement);
|
||||||
|
if (newStack == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return new ConfigDelayedMergeObject(origin(), newStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDescendant(AbstractConfigValue descendant) {
|
||||||
|
return hasDescendantInList(stack, descendant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigDelayedMergeObject relativized(Path prefix) {
|
||||||
|
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
|
||||||
|
for (AbstractConfigValue o : stack) {
|
||||||
|
newStack.add(o.relativized(prefix));
|
||||||
|
}
|
||||||
|
return new ConfigDelayedMergeObject(origin(), newStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean ignoresFallbacks() {
|
||||||
|
return ConfigDelayedMerge.stackIgnoresFallbacks(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final ConfigDelayedMergeObject mergedWithTheUnmergeable(Unmergeable fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
return (ConfigDelayedMergeObject) mergedWithTheUnmergeable(stack, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final ConfigDelayedMergeObject mergedWithObject(AbstractConfigObject fallback) {
|
||||||
|
return mergedWithNonObject(fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final ConfigDelayedMergeObject mergedWithNonObject(AbstractConfigValue fallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
return (ConfigDelayedMergeObject) mergedWithNonObject(stack, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigDelayedMergeObject withFallback(ConfigMergeable mergeable) {
|
||||||
|
return (ConfigDelayedMergeObject) super.withFallback(mergeable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigDelayedMergeObject withOnlyKey(String key) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigDelayedMergeObject withoutKey(String key) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigObject withOnlyPathOrNull(Path path) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
AbstractConfigObject withOnlyPath(Path path) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
AbstractConfigObject withoutPath(Path path) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigDelayedMergeObject withValue(String key, ConfigValue value) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigDelayedMergeObject withValue(Path path, ConfigValue value) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<AbstractConfigValue> unmergedValues() {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof ConfigDelayedMergeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
if (other instanceof ConfigDelayedMergeObject) {
|
||||||
|
return canEqual(other)
|
||||||
|
&& (this.stack == ((ConfigDelayedMergeObject) other).stack || this.stack
|
||||||
|
.equals(((ConfigDelayedMergeObject) other).stack));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
return stack.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||||
|
ConfigDelayedMerge.render(stack, sb, indent, atRoot, atKey, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
render(sb, indent, atRoot, null, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfigException notResolved() {
|
||||||
|
return new ConfigException.NotResolved(
|
||||||
|
"need to Config#resolve() before using this object, see the API docs for Config#resolve()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> unwrapped() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue get(Object key) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<java.util.Map.Entry<String, ConfigValue>> entrySet() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> keySet() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigValue> values() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigValue attemptPeekWithPartialResolve(String key) {
|
||||||
|
// a partial resolve of a ConfigDelayedMergeObject always results in a
|
||||||
|
// SimpleConfigObject because all the substitutions in the stack get
|
||||||
|
// resolved in order to look up the partial.
|
||||||
|
// So we know here that we have not been resolved at all even
|
||||||
|
// partially.
|
||||||
|
// Given that, all this code is probably gratuitous, since the app code
|
||||||
|
// is likely broken. But in general we only throw NotResolved if you try
|
||||||
|
// to touch the exact key that isn't resolved, so this is in that
|
||||||
|
// spirit.
|
||||||
|
|
||||||
|
// we'll be able to return a key if we have a value that ignores
|
||||||
|
// fallbacks, prior to any unmergeable values.
|
||||||
|
for (AbstractConfigValue layer : stack) {
|
||||||
|
if (layer instanceof AbstractConfigObject) {
|
||||||
|
AbstractConfigObject objectLayer = (AbstractConfigObject) layer;
|
||||||
|
AbstractConfigValue v = objectLayer.attemptPeekWithPartialResolve(key);
|
||||||
|
if (v != null) {
|
||||||
|
if (v.ignoresFallbacks()) {
|
||||||
|
// we know we won't need to merge anything in to this
|
||||||
|
// value
|
||||||
|
return v;
|
||||||
|
} else {
|
||||||
|
// we can't return this value because we know there are
|
||||||
|
// unmergeable values later in the stack that may
|
||||||
|
// contain values that need to be merged with this
|
||||||
|
// value. we'll throw the exception when we get to those
|
||||||
|
// unmergeable values, so continue here.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (layer instanceof Unmergeable) {
|
||||||
|
// an unmergeable object (which would be another
|
||||||
|
// ConfigDelayedMergeObject) can't know that a key is
|
||||||
|
// missing, so it can't return null; it can only return a
|
||||||
|
// value or throw NotPossibleToResolve
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"should not be reached: unmergeable object returned null value");
|
||||||
|
} else {
|
||||||
|
// a non-unmergeable AbstractConfigObject that returned null
|
||||||
|
// for the key in question is not relevant, we can keep
|
||||||
|
// looking for a value.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (layer instanceof Unmergeable) {
|
||||||
|
throw new ConfigException.NotResolved("Key '" + key + "' is not available at '"
|
||||||
|
+ origin().description() + "' because value at '"
|
||||||
|
+ layer.origin().description()
|
||||||
|
+ "' has not been resolved and may turn out to contain or hide '" + key
|
||||||
|
+ "'."
|
||||||
|
+ " Be sure to Config#resolve() before using a config object.");
|
||||||
|
} else if (layer.resolveStatus() == ResolveStatus.UNRESOLVED) {
|
||||||
|
// if the layer is not an object, and not a substitution or
|
||||||
|
// merge,
|
||||||
|
// then it's something that's unresolved because it _contains_
|
||||||
|
// an unresolved object... i.e. it's an array
|
||||||
|
if (!(layer instanceof ConfigList))
|
||||||
|
throw new ConfigException.BugOrBroken("Expecting a list here, not " + layer);
|
||||||
|
// all later objects will be hidden so we can say we won't find
|
||||||
|
// the key
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
// non-object, but resolved, like an integer or something.
|
||||||
|
// has no children so the one we're after won't be in it.
|
||||||
|
// we would only have this in the stack in case something
|
||||||
|
// else "looks back" to it due to a cycle.
|
||||||
|
// anyway at this point we know we can't find the key anymore.
|
||||||
|
if (!layer.ignoresFallbacks()) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"resolved non-object should ignore fallbacks");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we get here, then we never found anything unresolved which means
|
||||||
|
// the ConfigDelayedMergeObject should not have existed. some
|
||||||
|
// invariant was violated.
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"Delayed merge stack does not contain any unmergeable values");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+716
@@ -0,0 +1,716 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
final class ConfigDocumentParser {
|
||||||
|
static ConfigNodeRoot parse(Iterator<Token> tokens, ConfigOrigin origin, ConfigParseOptions options) {
|
||||||
|
ConfigSyntax syntax = options.getSyntax() == null ? ConfigSyntax.CONF : options.getSyntax();
|
||||||
|
ParseContext context = new ParseContext(syntax, origin, tokens);
|
||||||
|
return context.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigNodeValue parseValue(Iterator<Token> tokens, ConfigOrigin origin, ConfigParseOptions options) {
|
||||||
|
ConfigSyntax syntax = options.getSyntax() == null ? ConfigSyntax.CONF : options.getSyntax();
|
||||||
|
ParseContext context = new ParseContext(syntax, origin, tokens);
|
||||||
|
return context.parseSingleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
static private final class ParseContext {
|
||||||
|
private int lineNumber;
|
||||||
|
final private Stack<Token> buffer;
|
||||||
|
final private Iterator<Token> tokens;
|
||||||
|
final private ConfigSyntax flavor;
|
||||||
|
final private ConfigOrigin baseOrigin;
|
||||||
|
// this is the number of "equals" we are inside,
|
||||||
|
// used to modify the error message to reflect that
|
||||||
|
// someone may think this is .properties format.
|
||||||
|
int equalsCount;
|
||||||
|
|
||||||
|
ParseContext(ConfigSyntax flavor, ConfigOrigin origin, Iterator<Token> tokens) {
|
||||||
|
lineNumber = 1;
|
||||||
|
buffer = new Stack<Token>();
|
||||||
|
this.tokens = tokens;
|
||||||
|
this.flavor = flavor;
|
||||||
|
this.equalsCount = 0;
|
||||||
|
this.baseOrigin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token popToken() {
|
||||||
|
if (buffer.isEmpty()) {
|
||||||
|
return tokens.next();
|
||||||
|
}
|
||||||
|
return buffer.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token nextToken() {
|
||||||
|
Token t = popToken();
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
if (Tokens.isUnquotedText(t) && !isUnquotedWhitespace(t)) {
|
||||||
|
throw parseError("Token not allowed in valid JSON: '"
|
||||||
|
+ Tokens.getUnquotedText(t) + "'");
|
||||||
|
} else if (Tokens.isSubstitution(t)) {
|
||||||
|
throw parseError("Substitutions (${} syntax) not allowed in JSON");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token nextTokenCollectingWhitespace(Collection<AbstractConfigNode> nodes) {
|
||||||
|
while (true) {
|
||||||
|
Token t = nextToken();
|
||||||
|
if (Tokens.isIgnoredWhitespace(t) || Tokens.isNewline(t) || isUnquotedWhitespace(t)) {
|
||||||
|
nodes.add(new ConfigNodeSingleToken(t));
|
||||||
|
if (Tokens.isNewline(t)) {
|
||||||
|
lineNumber = t.lineNumber() + 1;
|
||||||
|
}
|
||||||
|
} else if (Tokens.isComment(t)) {
|
||||||
|
nodes.add(new ConfigNodeComment(t));
|
||||||
|
} else {
|
||||||
|
int newNumber = t.lineNumber();
|
||||||
|
if (newNumber >= 0)
|
||||||
|
lineNumber = newNumber;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putBack(Token token) {
|
||||||
|
buffer.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In arrays and objects, comma can be omitted
|
||||||
|
// as long as there's at least one newline instead.
|
||||||
|
// this skips any newlines in front of a comma,
|
||||||
|
// skips the comma, and returns true if it found
|
||||||
|
// either a newline or a comma. The iterator
|
||||||
|
// is left just after the comma or the newline.
|
||||||
|
private boolean checkElementSeparator(Collection<AbstractConfigNode> nodes) {
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
Token t = nextTokenCollectingWhitespace(nodes);
|
||||||
|
if (t == Tokens.COMMA) {
|
||||||
|
nodes.add(new ConfigNodeSingleToken(t));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
putBack(t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boolean sawSeparatorOrNewline = false;
|
||||||
|
Token t = nextToken();
|
||||||
|
while (true) {
|
||||||
|
if (Tokens.isIgnoredWhitespace(t) || isUnquotedWhitespace(t)) {
|
||||||
|
nodes.add(new ConfigNodeSingleToken(t));
|
||||||
|
} else if (Tokens.isComment(t)) {
|
||||||
|
nodes.add(new ConfigNodeComment(t));
|
||||||
|
} else if (Tokens.isNewline(t)) {
|
||||||
|
sawSeparatorOrNewline = true;
|
||||||
|
lineNumber++;
|
||||||
|
nodes.add(new ConfigNodeSingleToken(t));
|
||||||
|
// we want to continue to also eat
|
||||||
|
// a comma if there is one.
|
||||||
|
} else if (t == Tokens.COMMA) {
|
||||||
|
nodes.add(new ConfigNodeSingleToken(t));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// non-newline-or-comma
|
||||||
|
putBack(t);
|
||||||
|
return sawSeparatorOrNewline;
|
||||||
|
}
|
||||||
|
t = nextToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse a concatenation. If there is no concatenation, return the next value
|
||||||
|
private AbstractConfigNodeValue consolidateValues(Collection<AbstractConfigNode> nodes) {
|
||||||
|
// this trick is not done in JSON
|
||||||
|
if (flavor == ConfigSyntax.JSON)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// create only if we have value tokens
|
||||||
|
ArrayList<AbstractConfigNode> values = new ArrayList<AbstractConfigNode>();
|
||||||
|
int valueCount = 0;
|
||||||
|
|
||||||
|
// ignore a newline up front
|
||||||
|
Token t = nextTokenCollectingWhitespace(nodes);
|
||||||
|
while (true) {
|
||||||
|
AbstractConfigNodeValue v = null;
|
||||||
|
if (Tokens.isIgnoredWhitespace(t)) {
|
||||||
|
values.add(new ConfigNodeSingleToken(t));
|
||||||
|
t = nextToken();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (Tokens.isValue(t) || Tokens.isUnquotedText(t)
|
||||||
|
|| Tokens.isSubstitution(t) || t == Tokens.OPEN_CURLY
|
||||||
|
|| t == Tokens.OPEN_SQUARE) {
|
||||||
|
// there may be newlines _within_ the objects and arrays
|
||||||
|
v = parseValue(t);
|
||||||
|
valueCount++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == null)
|
||||||
|
throw new ConfigException.BugOrBroken("no value");
|
||||||
|
|
||||||
|
values.add(v);
|
||||||
|
|
||||||
|
t = nextToken(); // but don't consolidate across a newline
|
||||||
|
}
|
||||||
|
|
||||||
|
putBack(t);
|
||||||
|
|
||||||
|
// No concatenation was seen, but a single value may have been parsed, so return it, and put back
|
||||||
|
// all succeeding tokens
|
||||||
|
if (valueCount < 2) {
|
||||||
|
AbstractConfigNodeValue value = null;
|
||||||
|
for (AbstractConfigNode node : values) {
|
||||||
|
if (node instanceof AbstractConfigNodeValue)
|
||||||
|
value = (AbstractConfigNodeValue)node;
|
||||||
|
else if (value == null)
|
||||||
|
nodes.add(node);
|
||||||
|
else
|
||||||
|
putBack((new ArrayList<Token>(node.tokens())).get(0));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put back any trailing whitespace, as the parent object is responsible for tracking
|
||||||
|
// any leading/trailing whitespace
|
||||||
|
for (int i = values.size() - 1; i >= 0; i--) {
|
||||||
|
if (values.get(i) instanceof ConfigNodeSingleToken) {
|
||||||
|
putBack(((ConfigNodeSingleToken) values.get(i)).token());
|
||||||
|
values.remove(i);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ConfigNodeConcatenation(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigException parseError(String message) {
|
||||||
|
return parseError(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigException parseError(String message, Throwable cause) {
|
||||||
|
return new ConfigException.Parse(baseOrigin.withLineNumber(lineNumber), message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addQuoteSuggestion(String badToken, String message) {
|
||||||
|
return addQuoteSuggestion(null, equalsCount > 0, badToken, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addQuoteSuggestion(Path lastPath, boolean insideEquals, String badToken,
|
||||||
|
String message) {
|
||||||
|
String previousFieldName = lastPath != null ? lastPath.render() : null;
|
||||||
|
|
||||||
|
String part;
|
||||||
|
if (badToken.equals(Tokens.END.toString())) {
|
||||||
|
// EOF requires special handling for the error to make sense.
|
||||||
|
if (previousFieldName != null)
|
||||||
|
part = message + " (if you intended '" + previousFieldName
|
||||||
|
+ "' to be part of a value, instead of a key, "
|
||||||
|
+ "try adding double quotes around the whole value";
|
||||||
|
else
|
||||||
|
return message;
|
||||||
|
} else {
|
||||||
|
if (previousFieldName != null) {
|
||||||
|
part = message + " (if you intended " + badToken
|
||||||
|
+ " to be part of the value for '" + previousFieldName + "', "
|
||||||
|
+ "try enclosing the value in double quotes";
|
||||||
|
} else {
|
||||||
|
part = message + " (if you intended " + badToken
|
||||||
|
+ " to be part of a key or string value, "
|
||||||
|
+ "try enclosing the key or value in double quotes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insideEquals)
|
||||||
|
return part
|
||||||
|
+ ", or you may be able to rename the file .properties rather than .conf)";
|
||||||
|
else
|
||||||
|
return part + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractConfigNodeValue parseValue(Token t) {
|
||||||
|
AbstractConfigNodeValue v = null;
|
||||||
|
int startingEqualsCount = equalsCount;
|
||||||
|
|
||||||
|
if (Tokens.isValue(t) || Tokens.isUnquotedText(t) || Tokens.isSubstitution(t)) {
|
||||||
|
v = new ConfigNodeSimpleValue(t);
|
||||||
|
} else if (t == Tokens.OPEN_CURLY) {
|
||||||
|
v = parseObject(true);
|
||||||
|
} else if (t== Tokens.OPEN_SQUARE) {
|
||||||
|
v = parseArray();
|
||||||
|
} else {
|
||||||
|
throw parseError(addQuoteSuggestion(t.toString(),
|
||||||
|
"Expecting a value but got wrong token: " + t));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (equalsCount != startingEqualsCount)
|
||||||
|
throw new ConfigException.BugOrBroken("Bug in config parser: unbalanced equals count");
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigNodePath parseKey(Token token) {
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
if (Tokens.isValueWithType(token, ConfigValueType.STRING)) {
|
||||||
|
return PathParser.parsePathNodeExpression(Collections.singletonList(token).iterator(),
|
||||||
|
baseOrigin.withLineNumber(lineNumber));
|
||||||
|
} else {
|
||||||
|
throw parseError("Expecting close brace } or a field name here, got "
|
||||||
|
+ token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<Token> expression = new ArrayList<Token>();
|
||||||
|
Token t = token;
|
||||||
|
while (Tokens.isValue(t) || Tokens.isUnquotedText(t)) {
|
||||||
|
expression.add(t);
|
||||||
|
t = nextToken(); // note: don't cross a newline
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression.isEmpty()) {
|
||||||
|
throw parseError(ExpectingClosingParenthesisError + t);
|
||||||
|
}
|
||||||
|
|
||||||
|
putBack(t); // put back the token we ended with
|
||||||
|
return PathParser.parsePathNodeExpression(expression.iterator(),
|
||||||
|
baseOrigin.withLineNumber(lineNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isIncludeKeyword(Token t) {
|
||||||
|
return Tokens.isUnquotedText(t)
|
||||||
|
&& Tokens.getUnquotedText(t).equals("include");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isUnquotedWhitespace(Token t) {
|
||||||
|
if (!Tokens.isUnquotedText(t))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String s = Tokens.getUnquotedText(t);
|
||||||
|
|
||||||
|
for (int i = 0; i < s.length(); ++i) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if (!ConfigImplUtil.isWhitespace(c))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isKeyValueSeparatorToken(Token t) {
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
return t == Tokens.COLON;
|
||||||
|
} else {
|
||||||
|
return t == Tokens.COLON || t == Tokens.EQUALS || t == Tokens.PLUS_EQUALS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String ExpectingClosingParenthesisError = "expecting a close parentheses ')' here, not: ";
|
||||||
|
|
||||||
|
private ConfigNodeInclude parseInclude(ArrayList<AbstractConfigNode> children) {
|
||||||
|
|
||||||
|
Token t = nextTokenCollectingWhitespace(children);
|
||||||
|
|
||||||
|
// we either have a 'required()' or a quoted string or the "file()" syntax
|
||||||
|
if (Tokens.isUnquotedText(t)) {
|
||||||
|
String kindText = Tokens.getUnquotedText(t);
|
||||||
|
|
||||||
|
if (kindText.startsWith("required(")) {
|
||||||
|
String r = kindText.replaceFirst("required\\(","");
|
||||||
|
if (r.length()>0) {
|
||||||
|
putBack(Tokens.newUnquotedText(t.origin(),r));
|
||||||
|
}
|
||||||
|
|
||||||
|
children.add(new ConfigNodeSingleToken(t));
|
||||||
|
//children.add(new ConfigNodeSingleToken(tOpen));
|
||||||
|
|
||||||
|
ConfigNodeInclude res = parseIncludeResource(children, true);
|
||||||
|
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
|
||||||
|
if (Tokens.isUnquotedText(t) && Tokens.getUnquotedText(t).equals(")")) {
|
||||||
|
// OK, close paren
|
||||||
|
} else {
|
||||||
|
throw parseError(ExpectingClosingParenthesisError + t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
putBack(t);
|
||||||
|
return parseIncludeResource(children, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
putBack(t);
|
||||||
|
return parseIncludeResource(children, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigNodeInclude parseIncludeResource(ArrayList<AbstractConfigNode> children, boolean isRequired) {
|
||||||
|
Token t = nextTokenCollectingWhitespace(children);
|
||||||
|
|
||||||
|
// we either have a quoted string or the "file()" syntax
|
||||||
|
if (Tokens.isUnquotedText(t)) {
|
||||||
|
// get foo(
|
||||||
|
String kindText = Tokens.getUnquotedText(t);
|
||||||
|
ConfigIncludeKind kind;
|
||||||
|
String prefix;
|
||||||
|
|
||||||
|
if (kindText.startsWith("url(")) {
|
||||||
|
kind = ConfigIncludeKind.URL;
|
||||||
|
prefix = "url(";
|
||||||
|
} else if (kindText.startsWith("file(")) {
|
||||||
|
kind = ConfigIncludeKind.FILE;
|
||||||
|
prefix = "file(";
|
||||||
|
} else if (kindText.startsWith("classpath(")) {
|
||||||
|
kind = ConfigIncludeKind.CLASSPATH;
|
||||||
|
prefix = "classpath(";
|
||||||
|
} else {
|
||||||
|
throw parseError("expecting include parameter to be quoted filename, file(), classpath(), or url(). No spaces are allowed before the open paren. Not expecting: "
|
||||||
|
+ t);
|
||||||
|
}
|
||||||
|
String r = kindText.replaceFirst("[^(]*\\(","");
|
||||||
|
if (r.length()>0) {
|
||||||
|
putBack(Tokens.newUnquotedText(t.origin(),r));
|
||||||
|
}
|
||||||
|
|
||||||
|
children.add(new ConfigNodeSingleToken(t));
|
||||||
|
|
||||||
|
// skip space inside parens
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
|
||||||
|
// quoted string
|
||||||
|
if (!Tokens.isValueWithType(t, ConfigValueType.STRING)) {
|
||||||
|
throw parseError("expecting include " + prefix + ") parameter to be a quoted string, rather than: " + t);
|
||||||
|
}
|
||||||
|
children.add(new ConfigNodeSimpleValue(t));
|
||||||
|
// skip space after string, inside parens
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
|
||||||
|
if (Tokens.isUnquotedText(t) && Tokens.getUnquotedText(t).startsWith(")")) {
|
||||||
|
String rest = Tokens.getUnquotedText(t).substring(1);
|
||||||
|
if (rest.length()>0) {
|
||||||
|
putBack(Tokens.newUnquotedText(t.origin(),rest));
|
||||||
|
}
|
||||||
|
// OK, close paren
|
||||||
|
} else {
|
||||||
|
throw parseError(ExpectingClosingParenthesisError + t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConfigNodeInclude(children, kind, isRequired);
|
||||||
|
} else if (Tokens.isValueWithType(t, ConfigValueType.STRING)) {
|
||||||
|
children.add(new ConfigNodeSimpleValue(t));
|
||||||
|
return new ConfigNodeInclude(children, ConfigIncludeKind.HEURISTIC, isRequired);
|
||||||
|
} else {
|
||||||
|
throw parseError("include keyword is not followed by a quoted string, but by: " + t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigNodeComplexValue parseObject(boolean hadOpenCurly) {
|
||||||
|
// invoked just after the OPEN_CURLY (or START, if !hadOpenCurly)
|
||||||
|
boolean afterComma = false;
|
||||||
|
Path lastPath = null;
|
||||||
|
boolean lastInsideEquals = false;
|
||||||
|
ArrayList<AbstractConfigNode> objectNodes = new ArrayList<AbstractConfigNode>();
|
||||||
|
ArrayList<AbstractConfigNode> keyValueNodes;
|
||||||
|
HashMap<String, Boolean> keys = new HashMap<String, Boolean>();
|
||||||
|
if (hadOpenCurly)
|
||||||
|
objectNodes.add(new ConfigNodeSingleToken(Tokens.OPEN_CURLY));
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
Token t = nextTokenCollectingWhitespace(objectNodes);
|
||||||
|
if (t == Tokens.CLOSE_CURLY) {
|
||||||
|
if (flavor == ConfigSyntax.JSON && afterComma) {
|
||||||
|
throw parseError(addQuoteSuggestion(t.toString(),
|
||||||
|
"expecting a field name after a comma, got a close brace } instead"));
|
||||||
|
} else if (!hadOpenCurly) {
|
||||||
|
throw parseError(addQuoteSuggestion(t.toString(),
|
||||||
|
"unbalanced close brace '}' with no open brace"));
|
||||||
|
}
|
||||||
|
objectNodes.add(new ConfigNodeSingleToken(Tokens.CLOSE_CURLY));
|
||||||
|
break;
|
||||||
|
} else if (t == Tokens.END && !hadOpenCurly) {
|
||||||
|
putBack(t);
|
||||||
|
break;
|
||||||
|
} else if (flavor != ConfigSyntax.JSON && isIncludeKeyword(t)) {
|
||||||
|
ArrayList<AbstractConfigNode> includeNodes = new ArrayList<AbstractConfigNode>();
|
||||||
|
includeNodes.add(new ConfigNodeSingleToken(t));
|
||||||
|
objectNodes.add(parseInclude(includeNodes));
|
||||||
|
afterComma = false;
|
||||||
|
} else {
|
||||||
|
keyValueNodes = new ArrayList<AbstractConfigNode>();
|
||||||
|
Token keyToken = t;
|
||||||
|
ConfigNodePath path = parseKey(keyToken);
|
||||||
|
keyValueNodes.add(path);
|
||||||
|
Token afterKey = nextTokenCollectingWhitespace(keyValueNodes);
|
||||||
|
boolean insideEquals = false;
|
||||||
|
|
||||||
|
AbstractConfigNodeValue nextValue;
|
||||||
|
if (flavor == ConfigSyntax.CONF && afterKey == Tokens.OPEN_CURLY) {
|
||||||
|
// can omit the ':' or '=' before an object value
|
||||||
|
nextValue = parseValue(afterKey);
|
||||||
|
} else {
|
||||||
|
if (!isKeyValueSeparatorToken(afterKey)) {
|
||||||
|
throw parseError(addQuoteSuggestion(afterKey.toString(),
|
||||||
|
"Key '" + path.render() + "' may not be followed by token: "
|
||||||
|
+ afterKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
keyValueNodes.add(new ConfigNodeSingleToken(afterKey));
|
||||||
|
|
||||||
|
if (afterKey == Tokens.EQUALS) {
|
||||||
|
insideEquals = true;
|
||||||
|
equalsCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextValue = consolidateValues(keyValueNodes);
|
||||||
|
if (nextValue == null) {
|
||||||
|
nextValue = parseValue(nextTokenCollectingWhitespace(keyValueNodes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyValueNodes.add(nextValue);
|
||||||
|
if (insideEquals) {
|
||||||
|
equalsCount -= 1;
|
||||||
|
}
|
||||||
|
lastInsideEquals = insideEquals;
|
||||||
|
|
||||||
|
String key = path.value().first();
|
||||||
|
Path remaining = path.value().remainder();
|
||||||
|
|
||||||
|
if (remaining == null) {
|
||||||
|
Boolean existing = keys.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
// In strict JSON, dups should be an error; while in
|
||||||
|
// our custom config language, they should be merged
|
||||||
|
// if the value is an object (or substitution that
|
||||||
|
// could become an object).
|
||||||
|
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
throw parseError("JSON does not allow duplicate fields: '"
|
||||||
|
+ key
|
||||||
|
+ "' was already seen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys.put(key, true);
|
||||||
|
} else {
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"somehow got multi-element path in JSON mode");
|
||||||
|
}
|
||||||
|
keys.put(key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterComma = false;
|
||||||
|
objectNodes.add(new ConfigNodeField(keyValueNodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkElementSeparator(objectNodes)) {
|
||||||
|
// continue looping
|
||||||
|
afterComma = true;
|
||||||
|
} else {
|
||||||
|
t = nextTokenCollectingWhitespace(objectNodes);
|
||||||
|
if (t == Tokens.CLOSE_CURLY) {
|
||||||
|
if (!hadOpenCurly) {
|
||||||
|
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||||
|
t.toString(), "unbalanced close brace '}' with no open brace"));
|
||||||
|
}
|
||||||
|
objectNodes.add(new ConfigNodeSingleToken(t));
|
||||||
|
break;
|
||||||
|
} else if (hadOpenCurly) {
|
||||||
|
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||||
|
t.toString(), "Expecting close brace } or a comma, got " + t));
|
||||||
|
} else {
|
||||||
|
if (t == Tokens.END) {
|
||||||
|
putBack(t);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||||
|
t.toString(), "Expecting end of input or a comma, got " + t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConfigNodeObject(objectNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigNodeComplexValue parseArray() {
|
||||||
|
ArrayList<AbstractConfigNode> children = new ArrayList<AbstractConfigNode>();
|
||||||
|
children.add(new ConfigNodeSingleToken(Tokens.OPEN_SQUARE));
|
||||||
|
// invoked just after the OPEN_SQUARE
|
||||||
|
Token t;
|
||||||
|
|
||||||
|
AbstractConfigNodeValue nextValue = consolidateValues(children);
|
||||||
|
if (nextValue != null) {
|
||||||
|
children.add(nextValue);
|
||||||
|
} else {
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
|
||||||
|
// special-case the first element
|
||||||
|
if (t == Tokens.CLOSE_SQUARE) {
|
||||||
|
children.add(new ConfigNodeSingleToken(t));
|
||||||
|
return new ConfigNodeArray(children);
|
||||||
|
} else if (Tokens.isValue(t) || t == Tokens.OPEN_CURLY
|
||||||
|
|| t == Tokens.OPEN_SQUARE || Tokens.isUnquotedText(t)
|
||||||
|
|| Tokens.isSubstitution(t)) {
|
||||||
|
nextValue = parseValue(t);
|
||||||
|
children.add(nextValue);
|
||||||
|
} else {
|
||||||
|
throw parseError("List should have ] or a first element after the open [, instead had token: "
|
||||||
|
+ t
|
||||||
|
+ " (if you want "
|
||||||
|
+ t
|
||||||
|
+ " to be part of a string value, then double-quote it)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now remaining elements
|
||||||
|
while (true) {
|
||||||
|
// just after a value
|
||||||
|
if (checkElementSeparator(children)) {
|
||||||
|
// comma (or newline equivalent) consumed
|
||||||
|
} else {
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
if (t == Tokens.CLOSE_SQUARE) {
|
||||||
|
children.add(new ConfigNodeSingleToken(t));
|
||||||
|
return new ConfigNodeArray(children);
|
||||||
|
} else {
|
||||||
|
throw parseError("List should have ended with ] or had a comma, instead had token: "
|
||||||
|
+ t
|
||||||
|
+ " (if you want "
|
||||||
|
+ t
|
||||||
|
+ " to be part of a string value, then double-quote it)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now just after a comma
|
||||||
|
nextValue = consolidateValues(children);
|
||||||
|
if (nextValue != null) {
|
||||||
|
children.add(nextValue);
|
||||||
|
} else {
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
if (Tokens.isValue(t) || t == Tokens.OPEN_CURLY
|
||||||
|
|| t == Tokens.OPEN_SQUARE || Tokens.isUnquotedText(t)
|
||||||
|
|| Tokens.isSubstitution(t)) {
|
||||||
|
nextValue = parseValue(t);
|
||||||
|
children.add(nextValue);
|
||||||
|
} else if (flavor != ConfigSyntax.JSON && t == Tokens.CLOSE_SQUARE) {
|
||||||
|
// we allow one trailing comma
|
||||||
|
putBack(t);
|
||||||
|
} else {
|
||||||
|
throw parseError("List should have had new element after a comma, instead had token: "
|
||||||
|
+ t
|
||||||
|
+ " (if you want the comma or "
|
||||||
|
+ t
|
||||||
|
+ " to be part of a string value, then double-quote it)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigNodeRoot parse() {
|
||||||
|
ArrayList<AbstractConfigNode> children = new ArrayList<AbstractConfigNode>();
|
||||||
|
Token t = nextToken();
|
||||||
|
if (t == Tokens.START) {
|
||||||
|
// OK
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"token stream did not begin with START, had " + t);
|
||||||
|
}
|
||||||
|
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
AbstractConfigNode result = null;
|
||||||
|
boolean missingCurly = false;
|
||||||
|
if (t == Tokens.OPEN_CURLY || t == Tokens.OPEN_SQUARE) {
|
||||||
|
result = parseValue(t);
|
||||||
|
} else {
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
if (t == Tokens.END) {
|
||||||
|
throw parseError("Empty document");
|
||||||
|
} else {
|
||||||
|
throw parseError("Document must have an object or array at root, unexpected token: "
|
||||||
|
+ t);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the root object can omit the surrounding braces.
|
||||||
|
// this token should be the first field's key, or part
|
||||||
|
// of it, so put it back.
|
||||||
|
putBack(t);
|
||||||
|
missingCurly = true;
|
||||||
|
result = parseObject(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Need to pull the children out of the resulting node so we can keep leading
|
||||||
|
// and trailing whitespace if this was a no-brace object. Otherwise, we need to add
|
||||||
|
// the result into the list of children.
|
||||||
|
if (result instanceof ConfigNodeObject && missingCurly) {
|
||||||
|
children.addAll(((ConfigNodeComplexValue) result).children());
|
||||||
|
} else {
|
||||||
|
children.add(result);
|
||||||
|
}
|
||||||
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
if (t == Tokens.END) {
|
||||||
|
if (missingCurly) {
|
||||||
|
// If there were no braces, the entire document should be treated as a single object
|
||||||
|
return new ConfigNodeRoot(Collections.singletonList((AbstractConfigNode)new ConfigNodeObject(children)), baseOrigin);
|
||||||
|
} else {
|
||||||
|
return new ConfigNodeRoot(children, baseOrigin);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw parseError("Document has trailing tokens after first object or array: "
|
||||||
|
+ t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a given input stream into a single value node. Used when doing a replace inside a ConfigDocument.
|
||||||
|
AbstractConfigNodeValue parseSingleValue() {
|
||||||
|
Token t = nextToken();
|
||||||
|
if (t == Tokens.START) {
|
||||||
|
// OK
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"token stream did not begin with START, had " + t);
|
||||||
|
}
|
||||||
|
|
||||||
|
t = nextToken();
|
||||||
|
if (Tokens.isIgnoredWhitespace(t) || Tokens.isNewline(t) || isUnquotedWhitespace(t) || Tokens.isComment(t)) {
|
||||||
|
throw parseError("The value from withValueText cannot have leading or trailing newlines, whitespace, or comments");
|
||||||
|
}
|
||||||
|
if (t == Tokens.END) {
|
||||||
|
throw parseError("Empty value");
|
||||||
|
}
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
AbstractConfigNodeValue node = parseValue(t);
|
||||||
|
t = nextToken();
|
||||||
|
if (t == Tokens.END) {
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
throw parseError("Parsing JSON and the value set in withValueText was either a concatenation or " +
|
||||||
|
"had trailing whitespace, newlines, or comments");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
putBack(t);
|
||||||
|
ArrayList<AbstractConfigNode> nodes = new ArrayList<AbstractConfigNode>();
|
||||||
|
AbstractConfigNodeValue node = consolidateValues(nodes);
|
||||||
|
t = nextToken();
|
||||||
|
if (t == Tokens.END) {
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
throw parseError("The value from withValueText cannot have leading or trailing newlines, whitespace, or comments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
final class ConfigDouble extends ConfigNumber implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
final private double value;
|
||||||
|
|
||||||
|
ConfigDouble(ConfigOrigin origin, double value, String originalText) {
|
||||||
|
super(origin, originalText);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double unwrapped() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String transformToString() {
|
||||||
|
String s = super.transformToString();
|
||||||
|
if (s == null)
|
||||||
|
return Double.toString(value);
|
||||||
|
else
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long longValue() {
|
||||||
|
return (long) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double doubleValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigDouble newCopy(ConfigOrigin origin) {
|
||||||
|
return new ConfigDouble(origin, value, originalText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.Config;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluder;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigMemorySize;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseable;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.impl.SimpleIncluder.NameSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation detail, not ABI stable, do not touch.
|
||||||
|
* For use only by the {@link com.typesafe.config} package.
|
||||||
|
*/
|
||||||
|
public class ConfigImpl {
|
||||||
|
|
||||||
|
private static class LoaderCache {
|
||||||
|
private Config currentSystemProperties;
|
||||||
|
private WeakReference<ClassLoader> currentLoader;
|
||||||
|
private Map<String, Config> cache;
|
||||||
|
|
||||||
|
LoaderCache() {
|
||||||
|
this.currentSystemProperties = null;
|
||||||
|
this.currentLoader = new WeakReference<ClassLoader>(null);
|
||||||
|
this.cache = new HashMap<String, Config>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// for now, caching as long as the loader remains the same,
|
||||||
|
// drop entire cache if it changes.
|
||||||
|
synchronized Config getOrElseUpdate(ClassLoader loader, String key, Callable<Config> updater) {
|
||||||
|
if (loader != currentLoader.get()) {
|
||||||
|
// reset the cache if we start using a different loader
|
||||||
|
cache.clear();
|
||||||
|
currentLoader = new WeakReference<ClassLoader>(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config systemProperties = systemPropertiesAsConfig();
|
||||||
|
if (systemProperties != currentSystemProperties) {
|
||||||
|
cache.clear();
|
||||||
|
currentSystemProperties = systemProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config config = cache.get(key);
|
||||||
|
if (config == null) {
|
||||||
|
try {
|
||||||
|
config = updater.call();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e; // this will include ConfigException
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConfigException.Generic(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
if (config == null)
|
||||||
|
throw new ConfigException.BugOrBroken("null config from cache updater");
|
||||||
|
cache.put(key, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LoaderCacheHolder {
|
||||||
|
static final LoaderCache cache = new LoaderCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config computeCachedConfig(ClassLoader loader, String key,
|
||||||
|
Callable<Config> updater) {
|
||||||
|
LoaderCache cache;
|
||||||
|
try {
|
||||||
|
cache = LoaderCacheHolder.cache;
|
||||||
|
} catch (ExceptionInInitializerError e) {
|
||||||
|
throw ConfigImplUtil.extractInitializerError(e);
|
||||||
|
}
|
||||||
|
return cache.getOrElseUpdate(loader, key, updater);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class FileNameSource implements SimpleIncluder.NameSource {
|
||||||
|
@Override
|
||||||
|
public ConfigParseable nameToParseable(String name, ConfigParseOptions parseOptions) {
|
||||||
|
return Parseable.newFile(new File(name), parseOptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static class ClasspathNameSource implements SimpleIncluder.NameSource {
|
||||||
|
@Override
|
||||||
|
public ConfigParseable nameToParseable(String name, ConfigParseOptions parseOptions) {
|
||||||
|
return Parseable.newResources(name, parseOptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static class ClasspathNameSourceWithClass implements SimpleIncluder.NameSource {
|
||||||
|
final private Class<?> klass;
|
||||||
|
|
||||||
|
public ClasspathNameSourceWithClass(Class<?> klass) {
|
||||||
|
this.klass = klass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigParseable nameToParseable(String name, ConfigParseOptions parseOptions) {
|
||||||
|
return Parseable.newResources(klass, name, parseOptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static ConfigObject parseResourcesAnySyntax(Class<?> klass, String resourceBasename,
|
||||||
|
ConfigParseOptions baseOptions) {
|
||||||
|
NameSource source = new ClasspathNameSourceWithClass(klass);
|
||||||
|
return SimpleIncluder.fromBasename(source, resourceBasename, baseOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigObject parseResourcesAnySyntax(String resourceBasename,
|
||||||
|
ConfigParseOptions baseOptions) {
|
||||||
|
NameSource source = new ClasspathNameSource();
|
||||||
|
return SimpleIncluder.fromBasename(source, resourceBasename, baseOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigObject parseFileAnySyntax(File basename, ConfigParseOptions baseOptions) {
|
||||||
|
NameSource source = new FileNameSource();
|
||||||
|
return SimpleIncluder.fromBasename(source, basename.getPath(), baseOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject emptyObject(String originDescription) {
|
||||||
|
ConfigOrigin origin = originDescription != null ? SimpleConfigOrigin
|
||||||
|
.newSimple(originDescription) : null;
|
||||||
|
return emptyObject(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config emptyConfig(String originDescription) {
|
||||||
|
return emptyObject(originDescription).toConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject empty(ConfigOrigin origin) {
|
||||||
|
return emptyObject(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// default origin for values created with fromAnyRef and no origin specified
|
||||||
|
final private static ConfigOrigin defaultValueOrigin = SimpleConfigOrigin
|
||||||
|
.newSimple("hardcoded value");
|
||||||
|
final private static ConfigBoolean defaultTrueValue = new ConfigBoolean(
|
||||||
|
defaultValueOrigin, true);
|
||||||
|
final private static ConfigBoolean defaultFalseValue = new ConfigBoolean(
|
||||||
|
defaultValueOrigin, false);
|
||||||
|
final private static ConfigNull defaultNullValue = new ConfigNull(
|
||||||
|
defaultValueOrigin);
|
||||||
|
final private static SimpleConfigList defaultEmptyList = new SimpleConfigList(
|
||||||
|
defaultValueOrigin, Collections.<AbstractConfigValue> emptyList());
|
||||||
|
final private static SimpleConfigObject defaultEmptyObject = SimpleConfigObject
|
||||||
|
.empty(defaultValueOrigin);
|
||||||
|
|
||||||
|
private static SimpleConfigList emptyList(ConfigOrigin origin) {
|
||||||
|
if (origin == null || origin == defaultValueOrigin)
|
||||||
|
return defaultEmptyList;
|
||||||
|
else
|
||||||
|
return new SimpleConfigList(origin,
|
||||||
|
Collections.<AbstractConfigValue> emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigObject emptyObject(ConfigOrigin origin) {
|
||||||
|
// we want null origin to go to SimpleConfigObject.empty() to get the
|
||||||
|
// origin "empty config" rather than "hardcoded value"
|
||||||
|
if (origin == defaultValueOrigin)
|
||||||
|
return defaultEmptyObject;
|
||||||
|
else
|
||||||
|
return SimpleConfigObject.empty(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfigOrigin valueOrigin(String originDescription) {
|
||||||
|
if (originDescription == null)
|
||||||
|
return defaultValueOrigin;
|
||||||
|
else
|
||||||
|
return SimpleConfigOrigin.newSimple(originDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigValue fromAnyRef(Object object, String originDescription) {
|
||||||
|
ConfigOrigin origin = valueOrigin(originDescription);
|
||||||
|
return fromAnyRef(object, origin, FromMapMode.KEYS_ARE_KEYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigObject fromPathMap(
|
||||||
|
Map<String, ? extends Object> pathMap, String originDescription) {
|
||||||
|
ConfigOrigin origin = valueOrigin(originDescription);
|
||||||
|
return (ConfigObject) fromAnyRef(pathMap, origin,
|
||||||
|
FromMapMode.KEYS_ARE_PATHS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigValue fromAnyRef(Object object, ConfigOrigin origin,
|
||||||
|
FromMapMode mapMode) {
|
||||||
|
if (origin == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"origin not supposed to be null");
|
||||||
|
|
||||||
|
if (object == null) {
|
||||||
|
if (origin != defaultValueOrigin)
|
||||||
|
return new ConfigNull(origin);
|
||||||
|
else
|
||||||
|
return defaultNullValue;
|
||||||
|
} else if(object instanceof AbstractConfigValue) {
|
||||||
|
return (AbstractConfigValue) object;
|
||||||
|
} else if (object instanceof Boolean) {
|
||||||
|
if (origin != defaultValueOrigin) {
|
||||||
|
return new ConfigBoolean(origin, (Boolean) object);
|
||||||
|
} else if ((Boolean) object) {
|
||||||
|
return defaultTrueValue;
|
||||||
|
} else {
|
||||||
|
return defaultFalseValue;
|
||||||
|
}
|
||||||
|
} else if (object instanceof String) {
|
||||||
|
return new ConfigString.Quoted(origin, (String) object);
|
||||||
|
} else if (object instanceof Number) {
|
||||||
|
// here we always keep the same type that was passed to us,
|
||||||
|
// rather than figuring out if a Long would fit in an Int
|
||||||
|
// or a Double has no fractional part. i.e. deliberately
|
||||||
|
// not using ConfigNumber.newNumber() when we have a
|
||||||
|
// Double, Integer, or Long.
|
||||||
|
if (object instanceof Double) {
|
||||||
|
return new ConfigDouble(origin, (Double) object, null);
|
||||||
|
} else if (object instanceof Integer) {
|
||||||
|
return new ConfigInt(origin, (Integer) object, null);
|
||||||
|
} else if (object instanceof Long) {
|
||||||
|
return new ConfigLong(origin, (Long) object, null);
|
||||||
|
} else {
|
||||||
|
return ConfigNumber.newNumber(origin,
|
||||||
|
((Number) object).doubleValue(), null);
|
||||||
|
}
|
||||||
|
} else if (object instanceof Duration) {
|
||||||
|
return new ConfigLong(origin, ((Duration) object).toMillis(), null);
|
||||||
|
} else if (object instanceof Map) {
|
||||||
|
if (((Map<?, ?>) object).isEmpty())
|
||||||
|
return emptyObject(origin);
|
||||||
|
|
||||||
|
if (mapMode == FromMapMode.KEYS_ARE_KEYS) {
|
||||||
|
Map<String, AbstractConfigValue> values = new HashMap<String, AbstractConfigValue>();
|
||||||
|
for (Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
|
||||||
|
Object key = entry.getKey();
|
||||||
|
if (!(key instanceof String))
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"bug in method caller: not valid to create ConfigObject from map with non-String key: "
|
||||||
|
+ key);
|
||||||
|
AbstractConfigValue value = fromAnyRef(entry.getValue(),
|
||||||
|
origin, mapMode);
|
||||||
|
values.put((String) key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleConfigObject(origin, values);
|
||||||
|
} else {
|
||||||
|
return PropertiesParser.fromPathMap(origin, (Map<?, ?>) object);
|
||||||
|
}
|
||||||
|
} else if (object instanceof Iterable) {
|
||||||
|
Iterator<?> i = ((Iterable<?>) object).iterator();
|
||||||
|
if (!i.hasNext())
|
||||||
|
return emptyList(origin);
|
||||||
|
|
||||||
|
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
|
||||||
|
while (i.hasNext()) {
|
||||||
|
AbstractConfigValue v = fromAnyRef(i.next(), origin, mapMode);
|
||||||
|
values.add(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleConfigList(origin, values);
|
||||||
|
} else if (object instanceof ConfigMemorySize) {
|
||||||
|
return new ConfigLong(origin, ((ConfigMemorySize) object).toBytes(), null);
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"bug in method caller: not valid to create ConfigValue from: "
|
||||||
|
+ object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DefaultIncluderHolder {
|
||||||
|
static final ConfigIncluder defaultIncluder = new SimpleIncluder(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigIncluder defaultIncluder() {
|
||||||
|
try {
|
||||||
|
return DefaultIncluderHolder.defaultIncluder;
|
||||||
|
} catch (ExceptionInInitializerError e) {
|
||||||
|
throw ConfigImplUtil.extractInitializerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Properties getSystemProperties() {
|
||||||
|
// Avoid ConcurrentModificationException due to parallel setting of system properties by copying properties
|
||||||
|
final Properties systemProperties = System.getProperties();
|
||||||
|
final Properties systemPropertiesCopy = new Properties();
|
||||||
|
synchronized (systemProperties) {
|
||||||
|
systemPropertiesCopy.putAll(systemProperties);
|
||||||
|
}
|
||||||
|
return systemPropertiesCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigObject loadSystemProperties() {
|
||||||
|
return (AbstractConfigObject) Parseable.newProperties(getSystemProperties(),
|
||||||
|
ConfigParseOptions.defaults().setOriginDescription("system properties")).parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SystemPropertiesHolder {
|
||||||
|
// this isn't final due to the reloadSystemPropertiesConfig() hack below
|
||||||
|
static volatile AbstractConfigObject systemProperties = loadSystemProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject systemPropertiesAsConfigObject() {
|
||||||
|
try {
|
||||||
|
return SystemPropertiesHolder.systemProperties;
|
||||||
|
} catch (ExceptionInInitializerError e) {
|
||||||
|
throw ConfigImplUtil.extractInitializerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config systemPropertiesAsConfig() {
|
||||||
|
return systemPropertiesAsConfigObject().toConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reloadSystemPropertiesConfig() {
|
||||||
|
// ConfigFactory.invalidateCaches() relies on this having the side
|
||||||
|
// effect that it drops all caches
|
||||||
|
SystemPropertiesHolder.systemProperties = loadSystemProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigObject loadEnvVariables() {
|
||||||
|
return PropertiesParser.fromStringMap(newSimpleOrigin("env variables"), System.getenv());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EnvVariablesHolder {
|
||||||
|
static volatile AbstractConfigObject envVariables = loadEnvVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject envVariablesAsConfigObject() {
|
||||||
|
try {
|
||||||
|
return EnvVariablesHolder.envVariables;
|
||||||
|
} catch (ExceptionInInitializerError e) {
|
||||||
|
throw ConfigImplUtil.extractInitializerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config envVariablesAsConfig() {
|
||||||
|
return envVariablesAsConfigObject().toConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reloadEnvVariablesConfig() {
|
||||||
|
// ConfigFactory.invalidateCaches() relies on this having the side
|
||||||
|
// effect that it drops all caches
|
||||||
|
EnvVariablesHolder.envVariables = loadEnvVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config defaultReference(final ClassLoader loader) {
|
||||||
|
return computeCachedConfig(loader, "defaultReference", new Callable<Config>() {
|
||||||
|
@Override
|
||||||
|
public Config call() {
|
||||||
|
Config unresolvedResources = Parseable
|
||||||
|
.newResources("reference.conf",
|
||||||
|
ConfigParseOptions.defaults().setClassLoader(loader))
|
||||||
|
.parse().toConfig();
|
||||||
|
return systemPropertiesAsConfig().withFallback(unresolvedResources).resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DebugHolder {
|
||||||
|
private static String LOADS = "loads";
|
||||||
|
private static String SUBSTITUTIONS = "substitutions";
|
||||||
|
|
||||||
|
private static Map<String, Boolean> loadDiagnostics() {
|
||||||
|
Map<String, Boolean> result = new HashMap<String, Boolean>();
|
||||||
|
result.put(LOADS, false);
|
||||||
|
result.put(SUBSTITUTIONS, false);
|
||||||
|
|
||||||
|
// People do -Dconfig.trace=foo,bar to enable tracing of different things
|
||||||
|
String s = System.getProperty("config.trace");
|
||||||
|
if (s == null) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
String[] keys = s.split(",");
|
||||||
|
for (String k : keys) {
|
||||||
|
if (k.equals(LOADS)) {
|
||||||
|
result.put(LOADS, true);
|
||||||
|
} else if (k.equals(SUBSTITUTIONS)) {
|
||||||
|
result.put(SUBSTITUTIONS, true);
|
||||||
|
} else {
|
||||||
|
System.err.println("config.trace property contains unknown trace topic '"
|
||||||
|
+ k + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<String, Boolean> diagnostics = loadDiagnostics();
|
||||||
|
|
||||||
|
private static final boolean traceLoadsEnabled = diagnostics.get(LOADS);
|
||||||
|
private static final boolean traceSubstitutionsEnabled = diagnostics.get(SUBSTITUTIONS);
|
||||||
|
|
||||||
|
static boolean traceLoadsEnabled() {
|
||||||
|
return traceLoadsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean traceSubstitutionsEnabled() {
|
||||||
|
return traceSubstitutionsEnabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean traceLoadsEnabled() {
|
||||||
|
try {
|
||||||
|
return DebugHolder.traceLoadsEnabled();
|
||||||
|
} catch (ExceptionInInitializerError e) {
|
||||||
|
throw ConfigImplUtil.extractInitializerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean traceSubstitutionsEnabled() {
|
||||||
|
try {
|
||||||
|
return DebugHolder.traceSubstitutionsEnabled();
|
||||||
|
} catch (ExceptionInInitializerError e) {
|
||||||
|
throw ConfigImplUtil.extractInitializerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void trace(String message) {
|
||||||
|
System.err.println(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void trace(int indentLevel, String message) {
|
||||||
|
while (indentLevel > 0) {
|
||||||
|
System.err.print(" ");
|
||||||
|
indentLevel -= 1;
|
||||||
|
}
|
||||||
|
System.err.println(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the basic idea here is to add the "what" and have a canonical
|
||||||
|
// toplevel error message. the "original" exception may however have extra
|
||||||
|
// detail about what happened. call this if you have a better "what" than
|
||||||
|
// further down on the stack.
|
||||||
|
static ConfigException.NotResolved improveNotResolved(Path what,
|
||||||
|
ConfigException.NotResolved original) {
|
||||||
|
String newMessage = what.render()
|
||||||
|
+ " has not been resolved, you need to call Config#resolve(),"
|
||||||
|
+ " see API docs for Config#resolve()";
|
||||||
|
if (newMessage.equals(original.getMessage()))
|
||||||
|
return original;
|
||||||
|
else
|
||||||
|
return new ConfigException.NotResolved(newMessage, original);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigOrigin newSimpleOrigin(String description) {
|
||||||
|
if (description == null) {
|
||||||
|
return defaultValueOrigin;
|
||||||
|
} else {
|
||||||
|
return SimpleConfigOrigin.newSimple(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigOrigin newFileOrigin(String filename) {
|
||||||
|
return SimpleConfigOrigin.newFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigOrigin newURLOrigin(URL url) {
|
||||||
|
return SimpleConfigOrigin.newURL(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
+236
@@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation detail, not ABI stable, do not touch.
|
||||||
|
* For use only by the {@link com.typesafe.config} package.
|
||||||
|
*/
|
||||||
|
final public class ConfigImplUtil {
|
||||||
|
static boolean equalsHandlingNull(Object a, Object b) {
|
||||||
|
if (a == null && b != null)
|
||||||
|
return false;
|
||||||
|
else if (a != null && b == null)
|
||||||
|
return false;
|
||||||
|
else if (a == b) // catches null == null plus optimizes identity case
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return a.equals(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isC0Control(int codepoint) {
|
||||||
|
return (codepoint >= 0x0000 && codepoint <= 0x001F);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String renderJsonString(String s) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append('"');
|
||||||
|
for (int i = 0; i < s.length(); ++i) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
switch (c) {
|
||||||
|
case '"':
|
||||||
|
sb.append("\\\"");
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
sb.append("\\\\");
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
sb.append("\\n");
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
sb.append("\\b");
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
sb.append("\\f");
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
sb.append("\\r");
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
sb.append("\\t");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (isC0Control(c))
|
||||||
|
sb.append(String.format("\\u%04x", (int) c));
|
||||||
|
else
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append('"');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String renderStringUnquotedIfPossible(String s) {
|
||||||
|
// this can quote unnecessarily as long as it never fails to quote when
|
||||||
|
// necessary
|
||||||
|
if (s.length() == 0)
|
||||||
|
return renderJsonString(s);
|
||||||
|
|
||||||
|
// if it starts with a hyphen or number, we have to quote
|
||||||
|
// to ensure we end up with a string and not a number
|
||||||
|
int first = s.codePointAt(0);
|
||||||
|
if (Character.isDigit(first) || first == '-')
|
||||||
|
return renderJsonString(s);
|
||||||
|
|
||||||
|
if (s.startsWith("include") || s.startsWith("true") || s.startsWith("false")
|
||||||
|
|| s.startsWith("null") || s.contains("//"))
|
||||||
|
return renderJsonString(s);
|
||||||
|
|
||||||
|
// only unquote if it's pure alphanumeric
|
||||||
|
for (int i = 0; i < s.length(); ++i) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if (!(Character.isLetter(c) || Character.isDigit(c) || c == '-'))
|
||||||
|
return renderJsonString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isWhitespace(int codepoint) {
|
||||||
|
switch (codepoint) {
|
||||||
|
// try to hit the most common ASCII ones first, then the nonbreaking
|
||||||
|
// spaces that Java brokenly leaves out of isWhitespace.
|
||||||
|
case ' ':
|
||||||
|
case '\n':
|
||||||
|
case '\u00A0':
|
||||||
|
case '\u2007':
|
||||||
|
case '\u202F':
|
||||||
|
// this one is the BOM, see
|
||||||
|
// http://www.unicode.org/faq/utf_bom.html#BOM
|
||||||
|
// we just accept it as a zero-width nonbreaking space.
|
||||||
|
case '\uFEFF':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return Character.isWhitespace(codepoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String unicodeTrim(String s) {
|
||||||
|
// this is dumb because it looks like there aren't any whitespace
|
||||||
|
// characters that need surrogate encoding. But, points for
|
||||||
|
// pedantic correctness! It's future-proof or something.
|
||||||
|
// String.trim() actually is broken, since there are plenty of
|
||||||
|
// non-ASCII whitespace characters.
|
||||||
|
final int length = s.length();
|
||||||
|
if (length == 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
while (start < length) {
|
||||||
|
char c = s.charAt(start);
|
||||||
|
if (c == ' ' || c == '\n') {
|
||||||
|
start += 1;
|
||||||
|
} else {
|
||||||
|
int cp = s.codePointAt(start);
|
||||||
|
if (isWhitespace(cp))
|
||||||
|
start += Character.charCount(cp);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int end = length;
|
||||||
|
while (end > start) {
|
||||||
|
char c = s.charAt(end - 1);
|
||||||
|
if (c == ' ' || c == '\n') {
|
||||||
|
--end;
|
||||||
|
} else {
|
||||||
|
int cp;
|
||||||
|
int delta;
|
||||||
|
if (Character.isLowSurrogate(c)) {
|
||||||
|
cp = s.codePointAt(end - 2);
|
||||||
|
delta = 2;
|
||||||
|
} else {
|
||||||
|
cp = s.codePointAt(end - 1);
|
||||||
|
delta = 1;
|
||||||
|
}
|
||||||
|
if (isWhitespace(cp))
|
||||||
|
end -= delta;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ConfigException extractInitializerError(ExceptionInInitializerError e) {
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause != null && cause instanceof ConfigException) {
|
||||||
|
return (ConfigException) cause;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static File urlToFile(URL url) {
|
||||||
|
// this isn't really right, clearly, but not sure what to do.
|
||||||
|
try {
|
||||||
|
// this will properly handle hex escapes, etc.
|
||||||
|
return new File(url.toURI());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
// this handles some stuff like file:///c:/Whatever/
|
||||||
|
// apparently but mangles handling of hex escapes
|
||||||
|
return new File(url.getPath());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// file://foo with double slash causes
|
||||||
|
// IllegalArgumentException "url has an authority component"
|
||||||
|
return new File(url.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String joinPath(String... elements) {
|
||||||
|
return (new Path(elements)).render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String joinPath(List<String> elements) {
|
||||||
|
return joinPath(elements.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> splitPath(String path) {
|
||||||
|
Path p = Path.newPath(path);
|
||||||
|
List<String> elements = new ArrayList<String>();
|
||||||
|
while (p != null) {
|
||||||
|
elements.add(p.first());
|
||||||
|
p = p.remainder();
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigOrigin readOrigin(ObjectInputStream in) throws IOException {
|
||||||
|
return SerializedConfigValue.readOrigin(in, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeOrigin(ObjectOutputStream out, ConfigOrigin origin) throws IOException {
|
||||||
|
SerializedConfigValue.writeOrigin(new DataOutputStream(out), (SimpleConfigOrigin) origin,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toCamelCase(String originalName) {
|
||||||
|
String[] words = originalName.split("-+");
|
||||||
|
StringBuilder nameBuilder = new StringBuilder(originalName.length());
|
||||||
|
for (String word : words) {
|
||||||
|
if (nameBuilder.length() == 0) {
|
||||||
|
nameBuilder.append(word);
|
||||||
|
} else {
|
||||||
|
nameBuilder.append(word.substring(0, 1).toUpperCase());
|
||||||
|
nameBuilder.append(word.substring(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nameBuilder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
enum ConfigIncludeKind {
|
||||||
|
URL, FILE, CLASSPATH, HEURISTIC
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
final class ConfigInt extends ConfigNumber implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
final private int value;
|
||||||
|
|
||||||
|
ConfigInt(ConfigOrigin origin, int value, String originalText) {
|
||||||
|
super(origin, originalText);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer unwrapped() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String transformToString() {
|
||||||
|
String s = super.transformToString();
|
||||||
|
if (s == null)
|
||||||
|
return Integer.toString(value);
|
||||||
|
else
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long longValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double doubleValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigInt newCopy(ConfigOrigin origin) {
|
||||||
|
return new ConfigInt(origin, value, originalText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
final class ConfigLong extends ConfigNumber implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
final private long value;
|
||||||
|
|
||||||
|
ConfigLong(ConfigOrigin origin, long value, String originalText) {
|
||||||
|
super(origin, originalText);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long unwrapped() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String transformToString() {
|
||||||
|
String s = super.transformToString();
|
||||||
|
if (s == null)
|
||||||
|
return Long.toString(value);
|
||||||
|
else
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long longValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double doubleValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigLong newCopy(ConfigOrigin origin) {
|
||||||
|
return new ConfigLong(origin, value, originalText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
final class ConfigNodeArray extends ConfigNodeComplexValue {
|
||||||
|
ConfigNodeArray(Collection<AbstractConfigNode> children) {
|
||||||
|
super(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigNodeArray newNode(Collection<AbstractConfigNode> nodes) {
|
||||||
|
return new ConfigNodeArray(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
final class ConfigNodeComment extends ConfigNodeSingleToken {
|
||||||
|
ConfigNodeComment(Token comment) {
|
||||||
|
super(comment);
|
||||||
|
if (!Tokens.isComment(super.token)) {
|
||||||
|
throw new ConfigException.BugOrBroken("Tried to create a ConfigNodeComment from a non-comment token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String commentText() {
|
||||||
|
return Tokens.getCommentText(super.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
abstract class ConfigNodeComplexValue extends AbstractConfigNodeValue {
|
||||||
|
final protected ArrayList<AbstractConfigNode> children;
|
||||||
|
|
||||||
|
ConfigNodeComplexValue(Collection<AbstractConfigNode> children) {
|
||||||
|
this.children = new ArrayList<AbstractConfigNode>(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public Collection<AbstractConfigNode> children() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Token> tokens() {
|
||||||
|
ArrayList<Token> tokens = new ArrayList<Token>();
|
||||||
|
for (AbstractConfigNode child : children) {
|
||||||
|
tokens.addAll(child.tokens());
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigNodeComplexValue indentText(AbstractConfigNode indentation) {
|
||||||
|
ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(children);
|
||||||
|
for (int i = 0; i < childrenCopy.size(); i++) {
|
||||||
|
AbstractConfigNode child = childrenCopy.get(i);
|
||||||
|
if (child instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isNewline(((ConfigNodeSingleToken) child).token())) {
|
||||||
|
childrenCopy.add(i + 1, indentation);
|
||||||
|
i++;
|
||||||
|
} else if (child instanceof ConfigNodeField) {
|
||||||
|
AbstractConfigNode value = ((ConfigNodeField) child).value();
|
||||||
|
if (value instanceof ConfigNodeComplexValue) {
|
||||||
|
childrenCopy.set(i, ((ConfigNodeField) child).replaceValue(((ConfigNodeComplexValue) value).indentText(indentation)));
|
||||||
|
}
|
||||||
|
} else if (child instanceof ConfigNodeComplexValue) {
|
||||||
|
childrenCopy.set(i, ((ConfigNodeComplexValue) child).indentText(indentation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newNode(childrenCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method will just call into the object's constructor, but it's needed
|
||||||
|
// for use in the indentText() method so we can avoid a gross if/else statement
|
||||||
|
// checking the type of this
|
||||||
|
abstract ConfigNodeComplexValue newNode(Collection<AbstractConfigNode> nodes);
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
final class ConfigNodeConcatenation extends ConfigNodeComplexValue {
|
||||||
|
ConfigNodeConcatenation(Collection<AbstractConfigNode> children) {
|
||||||
|
super(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigNodeConcatenation newNode(Collection<AbstractConfigNode> nodes) {
|
||||||
|
return new ConfigNodeConcatenation(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class ConfigNodeField extends AbstractConfigNode {
|
||||||
|
final private ArrayList<AbstractConfigNode> children;
|
||||||
|
|
||||||
|
public ConfigNodeField(Collection<AbstractConfigNode> children) {
|
||||||
|
this.children = new ArrayList<AbstractConfigNode>(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Token> tokens() {
|
||||||
|
ArrayList<Token> tokens = new ArrayList<Token>();
|
||||||
|
for (AbstractConfigNode child : children) {
|
||||||
|
tokens.addAll(child.tokens());
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigNodeField replaceValue(AbstractConfigNodeValue newValue) {
|
||||||
|
ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(children);
|
||||||
|
for (int i = 0; i < childrenCopy.size(); i++) {
|
||||||
|
if (childrenCopy.get(i) instanceof AbstractConfigNodeValue) {
|
||||||
|
childrenCopy.set(i, newValue);
|
||||||
|
return new ConfigNodeField(childrenCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("Field node doesn't have a value");
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractConfigNodeValue value() {
|
||||||
|
for (int i = 0; i < children.size(); i++) {
|
||||||
|
if (children.get(i) instanceof AbstractConfigNodeValue) {
|
||||||
|
return (AbstractConfigNodeValue)children.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("Field node doesn't have a value");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigNodePath path() {
|
||||||
|
for (int i = 0; i < children.size(); i++) {
|
||||||
|
if (children.get(i) instanceof ConfigNodePath) {
|
||||||
|
return (ConfigNodePath)children.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("Field node doesn't have a path");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Token separator() {
|
||||||
|
for (AbstractConfigNode child : children) {
|
||||||
|
if (child instanceof ConfigNodeSingleToken) {
|
||||||
|
Token t = ((ConfigNodeSingleToken) child).token();
|
||||||
|
if (t == Tokens.PLUS_EQUALS || t == Tokens.COLON || t == Tokens.EQUALS) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> comments() {
|
||||||
|
List<String> comments = new ArrayList<String>();
|
||||||
|
for (AbstractConfigNode child : children) {
|
||||||
|
if (child instanceof ConfigNodeComment) {
|
||||||
|
comments.add(((ConfigNodeComment) child).commentText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
final class ConfigNodeInclude extends AbstractConfigNode {
|
||||||
|
final private ArrayList<AbstractConfigNode> children;
|
||||||
|
final private ConfigIncludeKind kind;
|
||||||
|
final private boolean isRequired;
|
||||||
|
|
||||||
|
ConfigNodeInclude(Collection<AbstractConfigNode> children, ConfigIncludeKind kind, boolean isRequired) {
|
||||||
|
this.children = new ArrayList<AbstractConfigNode>(children);
|
||||||
|
this.kind = kind;
|
||||||
|
this.isRequired = isRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public Collection<AbstractConfigNode> children() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Token> tokens() {
|
||||||
|
ArrayList<Token> tokens = new ArrayList<Token>();
|
||||||
|
for (AbstractConfigNode child : children) {
|
||||||
|
tokens.addAll(child.tokens());
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigIncludeKind kind() {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isRequired() {
|
||||||
|
return isRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String name() {
|
||||||
|
for (AbstractConfigNode n : children) {
|
||||||
|
if (n instanceof ConfigNodeSimpleValue) {
|
||||||
|
return (String)Tokens.getValue(((ConfigNodeSimpleValue) n).token()).unwrapped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+281
@@ -0,0 +1,281 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
final class ConfigNodeObject extends ConfigNodeComplexValue {
|
||||||
|
ConfigNodeObject(Collection<AbstractConfigNode> children) {
|
||||||
|
super(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigNodeObject newNode(Collection<AbstractConfigNode> nodes) {
|
||||||
|
return new ConfigNodeObject(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(Path desiredPath) {
|
||||||
|
for (AbstractConfigNode node : children) {
|
||||||
|
if (node instanceof ConfigNodeField) {
|
||||||
|
ConfigNodeField field = (ConfigNodeField) node;
|
||||||
|
Path key = field.path().value();
|
||||||
|
if (key.equals(desiredPath) || key.startsWith(desiredPath)) {
|
||||||
|
return true;
|
||||||
|
} else if (desiredPath.startsWith(key)) {
|
||||||
|
if (field.value() instanceof ConfigNodeObject) {
|
||||||
|
ConfigNodeObject obj = (ConfigNodeObject) field.value();
|
||||||
|
Path remainingPath = desiredPath.subPath(key.length());
|
||||||
|
if (obj.hasValue(remainingPath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigNodeObject changeValueOnPath(Path desiredPath, AbstractConfigNodeValue value, ConfigSyntax flavor) {
|
||||||
|
ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(super.children);
|
||||||
|
boolean seenNonMatching = false;
|
||||||
|
// Copy the value so we can change it to null but not modify the original parameter
|
||||||
|
AbstractConfigNodeValue valueCopy = value;
|
||||||
|
for (int i = childrenCopy.size() - 1; i >= 0; i--) {
|
||||||
|
if (childrenCopy.get(i) instanceof ConfigNodeSingleToken) {
|
||||||
|
Token t = ((ConfigNodeSingleToken) childrenCopy.get(i)).token();
|
||||||
|
// Ensure that, when we are removing settings in JSON, we don't end up with a trailing comma
|
||||||
|
if (flavor == ConfigSyntax.JSON && !seenNonMatching && t == Tokens.COMMA) {
|
||||||
|
childrenCopy.remove(i);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (!(childrenCopy.get(i) instanceof ConfigNodeField)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ConfigNodeField node = (ConfigNodeField) childrenCopy.get(i);
|
||||||
|
Path key = node.path().value();
|
||||||
|
|
||||||
|
// Delete all multi-element paths that start with the desired path, since technically they are duplicates
|
||||||
|
if ((valueCopy == null && key.equals(desiredPath))|| (key.startsWith(desiredPath) && !key.equals(desiredPath))) {
|
||||||
|
childrenCopy.remove(i);
|
||||||
|
// Remove any whitespace or commas after the deleted setting
|
||||||
|
for (int j = i; j < childrenCopy.size(); j++) {
|
||||||
|
if (childrenCopy.get(j) instanceof ConfigNodeSingleToken) {
|
||||||
|
Token t = ((ConfigNodeSingleToken) childrenCopy.get(j)).token();
|
||||||
|
if (Tokens.isIgnoredWhitespace(t) || t == Tokens.COMMA) {
|
||||||
|
childrenCopy.remove(j);
|
||||||
|
j--;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key.equals(desiredPath)) {
|
||||||
|
seenNonMatching = true;
|
||||||
|
AbstractConfigNodeValue indentedValue;
|
||||||
|
AbstractConfigNode before = i - 1 > 0 ? childrenCopy.get(i - 1) : null;
|
||||||
|
if (value instanceof ConfigNodeComplexValue &&
|
||||||
|
before instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) before).token()))
|
||||||
|
indentedValue = ((ConfigNodeComplexValue) value).indentText(before);
|
||||||
|
else
|
||||||
|
indentedValue = value;
|
||||||
|
childrenCopy.set(i, node.replaceValue(indentedValue));
|
||||||
|
valueCopy = null;
|
||||||
|
} else if (desiredPath.startsWith(key)) {
|
||||||
|
seenNonMatching = true;
|
||||||
|
if (node.value() instanceof ConfigNodeObject) {
|
||||||
|
Path remainingPath = desiredPath.subPath(key.length());
|
||||||
|
childrenCopy.set(i, node.replaceValue(((ConfigNodeObject) node.value()).changeValueOnPath(remainingPath, valueCopy, flavor)));
|
||||||
|
if (valueCopy != null && !node.equals(super.children.get(i)))
|
||||||
|
valueCopy = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seenNonMatching = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ConfigNodeObject(childrenCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigNodeObject setValueOnPath(String desiredPath, AbstractConfigNodeValue value) {
|
||||||
|
return setValueOnPath(desiredPath, value, ConfigSyntax.CONF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigNodeObject setValueOnPath(String desiredPath, AbstractConfigNodeValue value, ConfigSyntax flavor) {
|
||||||
|
ConfigNodePath path = PathParser.parsePathNode(desiredPath, flavor);
|
||||||
|
return setValueOnPath(path, value, flavor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigNodeObject setValueOnPath(ConfigNodePath desiredPath, AbstractConfigNodeValue value, ConfigSyntax flavor) {
|
||||||
|
ConfigNodeObject node = changeValueOnPath(desiredPath.value(), value, flavor);
|
||||||
|
|
||||||
|
// If the desired Path did not exist, add it
|
||||||
|
if (!node.hasValue(desiredPath.value())) {
|
||||||
|
return node.addValueOnPath(desiredPath, value, flavor);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<AbstractConfigNode> indentation() {
|
||||||
|
boolean seenNewLine = false;
|
||||||
|
ArrayList<AbstractConfigNode> indentation = new ArrayList<AbstractConfigNode>();
|
||||||
|
if (children.isEmpty()) {
|
||||||
|
return indentation;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < children.size(); i++) {
|
||||||
|
if (!seenNewLine) {
|
||||||
|
if (children.get(i) instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isNewline(((ConfigNodeSingleToken) children.get(i)).token())) {
|
||||||
|
seenNewLine = true;
|
||||||
|
indentation.add(new ConfigNodeSingleToken(Tokens.newLine(null)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (children.get(i) instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) children.get(i)).token()) &&
|
||||||
|
i + 1 < children.size() && (children.get(i+1) instanceof ConfigNodeField ||
|
||||||
|
children.get(i+1) instanceof ConfigNodeInclude)) {
|
||||||
|
// Return the indentation of the first setting on its own line
|
||||||
|
indentation.add(children.get(i));
|
||||||
|
return indentation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (indentation.isEmpty()) {
|
||||||
|
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||||
|
} else {
|
||||||
|
// Calculate the indentation of the ending curly-brace to get the indentation of the root object
|
||||||
|
AbstractConfigNode last = children.get(children.size() - 1);
|
||||||
|
if (last instanceof ConfigNodeSingleToken && ((ConfigNodeSingleToken) last).token() == Tokens.CLOSE_CURLY) {
|
||||||
|
AbstractConfigNode beforeLast = children.get(children.size() - 2);
|
||||||
|
String indent = "";
|
||||||
|
if (beforeLast instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) beforeLast).token()))
|
||||||
|
indent = ((ConfigNodeSingleToken) beforeLast).token().tokenText();
|
||||||
|
indent += " ";
|
||||||
|
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, indent)));
|
||||||
|
return indentation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object has no curly braces and is at the root level, so don't indent
|
||||||
|
return indentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigNodeObject addValueOnPath(ConfigNodePath desiredPath, AbstractConfigNodeValue value, ConfigSyntax flavor) {
|
||||||
|
Path path = desiredPath.value();
|
||||||
|
ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(super.children);
|
||||||
|
ArrayList<AbstractConfigNode> indentation = new ArrayList<AbstractConfigNode>(indentation());
|
||||||
|
|
||||||
|
// If the value we're inserting is a complex value, we'll need to indent it for insertion
|
||||||
|
AbstractConfigNodeValue indentedValue;
|
||||||
|
if (value instanceof ConfigNodeComplexValue && !indentation.isEmpty()) {
|
||||||
|
indentedValue = ((ConfigNodeComplexValue) value).indentText(indentation.get(indentation.size() - 1));
|
||||||
|
} else {
|
||||||
|
indentedValue = value;
|
||||||
|
}
|
||||||
|
boolean sameLine = !(indentation.size() > 0 && indentation.get(0) instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isNewline(((ConfigNodeSingleToken) indentation.get(0)).token()));
|
||||||
|
|
||||||
|
// If the path is of length greater than one, see if the value needs to be added further down
|
||||||
|
if (path.length() > 1) {
|
||||||
|
for (int i = super.children.size() - 1; i >= 0; i--) {
|
||||||
|
if (!(super.children.get(i) instanceof ConfigNodeField)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ConfigNodeField node = (ConfigNodeField) super.children.get(i);
|
||||||
|
Path key = node.path().value();
|
||||||
|
if (path.startsWith(key) && node.value() instanceof ConfigNodeObject) {
|
||||||
|
ConfigNodePath remainingPath = desiredPath.subPath(key.length());
|
||||||
|
ConfigNodeObject newValue = (ConfigNodeObject) node.value();
|
||||||
|
childrenCopy.set(i, node.replaceValue(newValue.addValueOnPath(remainingPath, value, flavor)));
|
||||||
|
return new ConfigNodeObject(childrenCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, construct the new setting
|
||||||
|
boolean startsWithBrace = !super.children.isEmpty() && super.children.get(0) instanceof ConfigNodeSingleToken &&
|
||||||
|
((ConfigNodeSingleToken) super.children.get(0)).token() == Tokens.OPEN_CURLY;
|
||||||
|
ArrayList<AbstractConfigNode> newNodes = new ArrayList<AbstractConfigNode>();
|
||||||
|
newNodes.addAll(indentation);
|
||||||
|
newNodes.add(desiredPath.first());
|
||||||
|
newNodes.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||||
|
newNodes.add(new ConfigNodeSingleToken(Tokens.COLON));
|
||||||
|
newNodes.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||||
|
|
||||||
|
if (path.length() == 1) {
|
||||||
|
newNodes.add(indentedValue);
|
||||||
|
} else {
|
||||||
|
// If the path is of length greater than one add the required new objects along the path
|
||||||
|
ArrayList<AbstractConfigNode> newObjectNodes = new ArrayList<AbstractConfigNode>();
|
||||||
|
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.OPEN_CURLY));
|
||||||
|
if (indentation.isEmpty()) {
|
||||||
|
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.newLine(null)));
|
||||||
|
}
|
||||||
|
newObjectNodes.addAll(indentation);
|
||||||
|
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.CLOSE_CURLY));
|
||||||
|
ConfigNodeObject newObject = new ConfigNodeObject(newObjectNodes);
|
||||||
|
newNodes.add(newObject.addValueOnPath(desiredPath.subPath(1), indentedValue, flavor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine these two cases so that we only have to iterate once
|
||||||
|
if (flavor == ConfigSyntax.JSON || startsWithBrace || sameLine) {
|
||||||
|
for (int i = childrenCopy.size() - 1; i >= 0; i--) {
|
||||||
|
|
||||||
|
// If we are in JSON or are adding a setting on the same line, we need to add a comma to the
|
||||||
|
// last setting
|
||||||
|
if ((flavor == ConfigSyntax.JSON || sameLine) && childrenCopy.get(i) instanceof ConfigNodeField) {
|
||||||
|
if (i+1 >= childrenCopy.size() ||
|
||||||
|
!(childrenCopy.get(i+1) instanceof ConfigNodeSingleToken
|
||||||
|
&& ((ConfigNodeSingleToken) childrenCopy.get(i+1)).token() == Tokens.COMMA))
|
||||||
|
childrenCopy.add(i+1, new ConfigNodeSingleToken(Tokens.COMMA));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the value into the copy of the children map, keeping any whitespace/newlines
|
||||||
|
// before the close curly brace
|
||||||
|
if (startsWithBrace && childrenCopy.get(i) instanceof ConfigNodeSingleToken &&
|
||||||
|
((ConfigNodeSingleToken) childrenCopy.get(i)).token == Tokens.CLOSE_CURLY) {
|
||||||
|
AbstractConfigNode previous = childrenCopy.get(i - 1);
|
||||||
|
if (previous instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isNewline(((ConfigNodeSingleToken) previous).token())) {
|
||||||
|
childrenCopy.add(i - 1, new ConfigNodeField(newNodes));
|
||||||
|
i--;
|
||||||
|
} else if (previous instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) previous).token())) {
|
||||||
|
AbstractConfigNode beforePrevious = childrenCopy.get(i - 2);
|
||||||
|
if (sameLine) {
|
||||||
|
childrenCopy.add(i - 1, new ConfigNodeField(newNodes));
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
else if (beforePrevious instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isNewline(((ConfigNodeSingleToken) beforePrevious).token())) {
|
||||||
|
childrenCopy.add(i - 2, new ConfigNodeField(newNodes));
|
||||||
|
i -= 2;
|
||||||
|
} else {
|
||||||
|
childrenCopy.add(i, new ConfigNodeField(newNodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
childrenCopy.add(i, new ConfigNodeField(newNodes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!startsWithBrace) {
|
||||||
|
if (!childrenCopy.isEmpty() && childrenCopy.get(childrenCopy.size() - 1) instanceof ConfigNodeSingleToken &&
|
||||||
|
Tokens.isNewline(((ConfigNodeSingleToken) childrenCopy.get(childrenCopy.size() - 1)).token()))
|
||||||
|
childrenCopy.add(childrenCopy.size() - 1, new ConfigNodeField(newNodes));
|
||||||
|
else
|
||||||
|
childrenCopy.add(new ConfigNodeField(newNodes));
|
||||||
|
}
|
||||||
|
return new ConfigNodeObject(childrenCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigNodeObject removeValueOnPath(String desiredPath, ConfigSyntax flavor) {
|
||||||
|
Path path = PathParser.parsePathNode(desiredPath, flavor).value();
|
||||||
|
return changeValueOnPath(path, null, flavor);
|
||||||
|
}
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
final class ConfigNodePath extends AbstractConfigNode {
|
||||||
|
final private Path path;
|
||||||
|
final ArrayList<Token> tokens;
|
||||||
|
ConfigNodePath(Path path, Collection<Token> tokens) {
|
||||||
|
this.path = path;
|
||||||
|
this.tokens = new ArrayList<Token>(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Token> tokens() {
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path value() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigNodePath subPath(int toRemove) {
|
||||||
|
int periodCount = 0;
|
||||||
|
ArrayList<Token> tokensCopy = new ArrayList<Token>(tokens);
|
||||||
|
for (int i = 0; i < tokensCopy.size(); i++) {
|
||||||
|
if (Tokens.isUnquotedText(tokensCopy.get(i)) &&
|
||||||
|
tokensCopy.get(i).tokenText().equals("."))
|
||||||
|
periodCount++;
|
||||||
|
|
||||||
|
if (periodCount == toRemove) {
|
||||||
|
return new ConfigNodePath(path.subPath(toRemove), tokensCopy.subList(i + 1, tokensCopy.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("Tried to remove too many elements from a Path node");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigNodePath first() {
|
||||||
|
ArrayList<Token> tokensCopy = new ArrayList<Token>(tokens);
|
||||||
|
for (int i = 0; i < tokensCopy.size(); i++) {
|
||||||
|
if (Tokens.isUnquotedText(tokensCopy.get(i)) &&
|
||||||
|
tokensCopy.get(i).tokenText().equals("."))
|
||||||
|
return new ConfigNodePath(path.subPath(0, 1), tokensCopy.subList(0, i));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
final class ConfigNodeRoot extends ConfigNodeComplexValue {
|
||||||
|
final private ConfigOrigin origin;
|
||||||
|
|
||||||
|
ConfigNodeRoot(Collection<AbstractConfigNode> children, ConfigOrigin origin) {
|
||||||
|
super(children);
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigNodeRoot newNode(Collection<AbstractConfigNode> nodes) {
|
||||||
|
throw new ConfigException.BugOrBroken("Tried to indent the root object");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigNodeComplexValue value() {
|
||||||
|
for (AbstractConfigNode node : children) {
|
||||||
|
if (node instanceof ConfigNodeComplexValue) {
|
||||||
|
return (ConfigNodeComplexValue)node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("ConfigNodeRoot did not contain a value");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigNodeRoot setValue(String desiredPath, AbstractConfigNodeValue value, ConfigSyntax flavor) {
|
||||||
|
ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(children);
|
||||||
|
for (int i = 0; i < childrenCopy.size(); i++) {
|
||||||
|
AbstractConfigNode node = childrenCopy.get(i);
|
||||||
|
if (node instanceof ConfigNodeComplexValue) {
|
||||||
|
if (node instanceof ConfigNodeArray) {
|
||||||
|
throw new ConfigException.WrongType(origin, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array.");
|
||||||
|
} else if (node instanceof ConfigNodeObject) {
|
||||||
|
if (value == null) {
|
||||||
|
childrenCopy.set(i, ((ConfigNodeObject)node).removeValueOnPath(desiredPath, flavor));
|
||||||
|
} else {
|
||||||
|
childrenCopy.set(i, ((ConfigNodeObject) node).setValueOnPath(desiredPath, value, flavor));
|
||||||
|
}
|
||||||
|
return new ConfigNodeRoot(childrenCopy, origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("ConfigNodeRoot did not contain a value");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasValue(String desiredPath) {
|
||||||
|
Path path = PathParser.parsePath(desiredPath);
|
||||||
|
ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(children);
|
||||||
|
for (int i = 0; i < childrenCopy.size(); i++) {
|
||||||
|
AbstractConfigNode node = childrenCopy.get(i);
|
||||||
|
if (node instanceof ConfigNodeComplexValue) {
|
||||||
|
if (node instanceof ConfigNodeArray) {
|
||||||
|
throw new ConfigException.WrongType(origin, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array.");
|
||||||
|
} else if (node instanceof ConfigNodeObject) {
|
||||||
|
return ((ConfigNodeObject) node).hasValue(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("ConfigNodeRoot did not contain a value");
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class ConfigNodeSimpleValue extends AbstractConfigNodeValue {
|
||||||
|
final Token token;
|
||||||
|
ConfigNodeSimpleValue(Token value) {
|
||||||
|
token = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Token> tokens() {
|
||||||
|
return Collections.singletonList(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Token token() { return token; }
|
||||||
|
|
||||||
|
protected AbstractConfigValue value() {
|
||||||
|
if (Tokens.isValue(token))
|
||||||
|
return Tokens.getValue(token);
|
||||||
|
else if (Tokens.isUnquotedText(token))
|
||||||
|
return new ConfigString.Unquoted(token.origin(), Tokens.getUnquotedText(token));
|
||||||
|
else if (Tokens.isSubstitution(token)) {
|
||||||
|
List<Token> expression = Tokens.getSubstitutionPathExpression(token);
|
||||||
|
Path path = PathParser.parsePathExpression(expression.iterator(), token.origin());
|
||||||
|
boolean optional = Tokens.getSubstitutionOptional(token);
|
||||||
|
|
||||||
|
return new ConfigReference(token.origin(), new SubstitutionExpression(path, optional));
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("ConfigNodeSimpleValue did not contain a valid value token");
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
class ConfigNodeSingleToken extends AbstractConfigNode {
|
||||||
|
final Token token;
|
||||||
|
ConfigNodeSingleToken(Token t) {
|
||||||
|
token = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Token> tokens() {
|
||||||
|
return Collections.singletonList(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Token token() { return token; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exists because sometimes null is not the same as missing. Specifically,
|
||||||
|
* if a value is set to null we can give a better error message (indicating
|
||||||
|
* where it was set to null) in case someone asks for the value. Also, null
|
||||||
|
* overrides values set "earlier" in the search path, while missing values do
|
||||||
|
* not.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class ConfigNull extends AbstractConfigValue implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
ConfigNull(ConfigOrigin origin) {
|
||||||
|
super(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object unwrapped() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String transformToString() {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
sb.append("null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigNull newCopy(ConfigOrigin origin) {
|
||||||
|
return new ConfigNull(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
|
||||||
|
abstract class ConfigNumber extends AbstractConfigValue implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
// This is so when we concatenate a number into a string (say it appears in
|
||||||
|
// a sentence) we always have it exactly as the person typed it into the
|
||||||
|
// config file. It's purely cosmetic; equals/hashCode don't consider this
|
||||||
|
// for example.
|
||||||
|
final protected String originalText;
|
||||||
|
|
||||||
|
protected ConfigNumber(ConfigOrigin origin, String originalText) {
|
||||||
|
super(origin);
|
||||||
|
this.originalText = originalText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract Number unwrapped();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String transformToString() {
|
||||||
|
return originalText;
|
||||||
|
}
|
||||||
|
|
||||||
|
int intValueRangeChecked(String path) {
|
||||||
|
long l = longValue();
|
||||||
|
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
|
||||||
|
throw new ConfigException.WrongType(origin(), path, "32-bit integer",
|
||||||
|
"out-of-range value " + l);
|
||||||
|
}
|
||||||
|
return (int) l;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract long longValue();
|
||||||
|
|
||||||
|
protected abstract double doubleValue();
|
||||||
|
|
||||||
|
private boolean isWhole() {
|
||||||
|
long asLong = longValue();
|
||||||
|
return asLong == doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof ConfigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
if (other instanceof ConfigNumber && canEqual(other)) {
|
||||||
|
ConfigNumber n = (ConfigNumber) other;
|
||||||
|
if (isWhole()) {
|
||||||
|
return n.isWhole() && this.longValue() == n.longValue();
|
||||||
|
} else {
|
||||||
|
return (!n.isWhole()) && this.doubleValue() == n.doubleValue();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
|
||||||
|
// this matches what standard Long.hashCode and Double.hashCode
|
||||||
|
// do, though I don't think it really matters.
|
||||||
|
long asLong;
|
||||||
|
if (isWhole()) {
|
||||||
|
asLong = longValue();
|
||||||
|
} else {
|
||||||
|
asLong = Double.doubleToLongBits(doubleValue());
|
||||||
|
}
|
||||||
|
return (int) (asLong ^ (asLong >>> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigNumber newNumber(ConfigOrigin origin, long number,
|
||||||
|
String originalText) {
|
||||||
|
if (number <= Integer.MAX_VALUE && number >= Integer.MIN_VALUE)
|
||||||
|
return new ConfigInt(origin, (int) number, originalText);
|
||||||
|
else
|
||||||
|
return new ConfigLong(origin, number, originalText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigNumber newNumber(ConfigOrigin origin, double number,
|
||||||
|
String originalText) {
|
||||||
|
long asLong = (long) number;
|
||||||
|
if (asLong == number) {
|
||||||
|
return newNumber(origin, asLong, originalText);
|
||||||
|
} else {
|
||||||
|
return new ConfigDouble(origin, number, originalText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+426
@@ -0,0 +1,426 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncludeContext;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
|
||||||
|
final class ConfigParser {
|
||||||
|
static AbstractConfigValue parse(ConfigNodeRoot document,
|
||||||
|
ConfigOrigin origin, ConfigParseOptions options,
|
||||||
|
ConfigIncludeContext includeContext) {
|
||||||
|
ParseContext context = new ParseContext(options.getSyntax(), origin, document,
|
||||||
|
SimpleIncluder.makeFull(options.getIncluder()), includeContext);
|
||||||
|
return context.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
static private final class ParseContext {
|
||||||
|
private int lineNumber;
|
||||||
|
final private ConfigNodeRoot document;
|
||||||
|
final private FullIncluder includer;
|
||||||
|
final private ConfigIncludeContext includeContext;
|
||||||
|
final private ConfigSyntax flavor;
|
||||||
|
final private ConfigOrigin baseOrigin;
|
||||||
|
final private LinkedList<Path> pathStack;
|
||||||
|
|
||||||
|
// the number of lists we are inside; this is used to detect the "cannot
|
||||||
|
// generate a reference to a list element" problem, and once we fix that
|
||||||
|
// problem we should be able to get rid of this variable.
|
||||||
|
int arrayCount;
|
||||||
|
|
||||||
|
ParseContext(ConfigSyntax flavor, ConfigOrigin origin, ConfigNodeRoot document,
|
||||||
|
FullIncluder includer, ConfigIncludeContext includeContext) {
|
||||||
|
lineNumber = 1;
|
||||||
|
this.document = document;
|
||||||
|
this.flavor = flavor;
|
||||||
|
this.baseOrigin = origin;
|
||||||
|
this.includer = includer;
|
||||||
|
this.includeContext = includeContext;
|
||||||
|
this.pathStack = new LinkedList<Path>();
|
||||||
|
this.arrayCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge a bunch of adjacent values into one
|
||||||
|
// value; change unquoted text into a string
|
||||||
|
// value.
|
||||||
|
private AbstractConfigValue parseConcatenation(ConfigNodeConcatenation n) {
|
||||||
|
// this trick is not done in JSON
|
||||||
|
if (flavor == ConfigSyntax.JSON)
|
||||||
|
throw new ConfigException.BugOrBroken("Found a concatenation node in JSON");
|
||||||
|
|
||||||
|
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
|
||||||
|
|
||||||
|
for (AbstractConfigNode node : n.children()) {
|
||||||
|
AbstractConfigValue v = null;
|
||||||
|
if (node instanceof AbstractConfigNodeValue) {
|
||||||
|
v = parseValue((AbstractConfigNodeValue)node, null);
|
||||||
|
values.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConfigConcatenation.concatenate(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleConfigOrigin lineOrigin() {
|
||||||
|
return ((SimpleConfigOrigin) baseOrigin).withLineNumber(lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigException parseError(String message) {
|
||||||
|
return parseError(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigException parseError(String message, Throwable cause) {
|
||||||
|
return new ConfigException.Parse(lineOrigin(), message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path fullCurrentPath() {
|
||||||
|
// pathStack has top of stack at front
|
||||||
|
if (pathStack.isEmpty())
|
||||||
|
throw new ConfigException.BugOrBroken("Bug in parser; tried to get current path when at root");
|
||||||
|
else
|
||||||
|
return new Path(pathStack.descendingIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractConfigValue parseValue(AbstractConfigNodeValue n, List<String> comments) {
|
||||||
|
AbstractConfigValue v;
|
||||||
|
|
||||||
|
int startingArrayCount = arrayCount;
|
||||||
|
|
||||||
|
if (n instanceof ConfigNodeSimpleValue) {
|
||||||
|
v = ((ConfigNodeSimpleValue) n).value();
|
||||||
|
} else if (n instanceof ConfigNodeObject) {
|
||||||
|
v = parseObject((ConfigNodeObject)n);
|
||||||
|
} else if (n instanceof ConfigNodeArray) {
|
||||||
|
v = parseArray((ConfigNodeArray)n);
|
||||||
|
} else if (n instanceof ConfigNodeConcatenation) {
|
||||||
|
v = parseConcatenation((ConfigNodeConcatenation)n);
|
||||||
|
} else {
|
||||||
|
throw parseError("Expecting a value but got wrong node type: " + n.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comments != null && !comments.isEmpty()) {
|
||||||
|
v = v.withOrigin(v.origin().prependComments(new ArrayList<String>(comments)));
|
||||||
|
comments.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrayCount != startingArrayCount)
|
||||||
|
throw new ConfigException.BugOrBroken("Bug in config parser: unbalanced array count");
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigObject createValueUnderPath(Path path,
|
||||||
|
AbstractConfigValue value) {
|
||||||
|
// for path foo.bar, we are creating
|
||||||
|
// { "foo" : { "bar" : value } }
|
||||||
|
List<String> keys = new ArrayList<String>();
|
||||||
|
|
||||||
|
String key = path.first();
|
||||||
|
Path remaining = path.remainder();
|
||||||
|
while (key != null) {
|
||||||
|
keys.add(key);
|
||||||
|
if (remaining == null) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
key = remaining.first();
|
||||||
|
remaining = remaining.remainder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the withComments(null) is to ensure comments are only
|
||||||
|
// on the exact leaf node they apply to.
|
||||||
|
// a comment before "foo.bar" applies to the full setting
|
||||||
|
// "foo.bar" not also to "foo"
|
||||||
|
ListIterator<String> i = keys.listIterator(keys.size());
|
||||||
|
String deepest = i.previous();
|
||||||
|
AbstractConfigObject o = new SimpleConfigObject(value.origin().withComments(null),
|
||||||
|
Collections.<String, AbstractConfigValue> singletonMap(
|
||||||
|
deepest, value));
|
||||||
|
while (i.hasPrevious()) {
|
||||||
|
Map<String, AbstractConfigValue> m = Collections.<String, AbstractConfigValue> singletonMap(
|
||||||
|
i.previous(), o);
|
||||||
|
o = new SimpleConfigObject(value.origin().withComments(null), m);
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseInclude(Map<String, AbstractConfigValue> values, ConfigNodeInclude n) {
|
||||||
|
boolean isRequired = n.isRequired();
|
||||||
|
ConfigIncludeContext cic = includeContext.setParseOptions(includeContext.parseOptions().setAllowMissing(!isRequired));
|
||||||
|
|
||||||
|
AbstractConfigObject obj;
|
||||||
|
switch (n.kind()) {
|
||||||
|
case URL:
|
||||||
|
URL url;
|
||||||
|
try {
|
||||||
|
url = new URL(n.name());
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw parseError("include url() specifies an invalid URL: " + n.name(), e);
|
||||||
|
}
|
||||||
|
obj = (AbstractConfigObject) includer.includeURL(cic, url);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FILE:
|
||||||
|
obj = (AbstractConfigObject) includer.includeFile(cic,
|
||||||
|
new File(n.name()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CLASSPATH:
|
||||||
|
obj = (AbstractConfigObject) includer.includeResources(cic, n.name());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HEURISTIC:
|
||||||
|
obj = (AbstractConfigObject) includer
|
||||||
|
.include(cic, n.name());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ConfigException.BugOrBroken("should not be reached");
|
||||||
|
}
|
||||||
|
|
||||||
|
// we really should make this work, but for now throwing an
|
||||||
|
// exception is better than producing an incorrect result.
|
||||||
|
// See https://github.com/lightbend/config/issues/160
|
||||||
|
if (arrayCount > 0 && obj.resolveStatus() != ResolveStatus.RESOLVED)
|
||||||
|
throw parseError("Due to current limitations of the config parser, when an include statement is nested inside a list value, "
|
||||||
|
+ "${} substitutions inside the included file cannot be resolved correctly. Either move the include outside of the list value or "
|
||||||
|
+ "remove the ${} statements from the included file.");
|
||||||
|
|
||||||
|
if (!pathStack.isEmpty()) {
|
||||||
|
Path prefix = fullCurrentPath();
|
||||||
|
obj = obj.relativized(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String key : obj.keySet()) {
|
||||||
|
AbstractConfigValue v = obj.get(key);
|
||||||
|
AbstractConfigValue existing = values.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
values.put(key, v.withFallback(existing));
|
||||||
|
} else {
|
||||||
|
values.put(key, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractConfigObject parseObject(ConfigNodeObject n) {
|
||||||
|
Map<String, AbstractConfigValue> values = new HashMap<String, AbstractConfigValue>();
|
||||||
|
SimpleConfigOrigin objectOrigin = lineOrigin();
|
||||||
|
boolean lastWasNewline = false;
|
||||||
|
|
||||||
|
ArrayList<AbstractConfigNode> nodes = new ArrayList<AbstractConfigNode>(n.children());
|
||||||
|
List<String> comments = new ArrayList<String>();
|
||||||
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
|
AbstractConfigNode node = nodes.get(i);
|
||||||
|
if (node instanceof ConfigNodeComment) {
|
||||||
|
lastWasNewline = false;
|
||||||
|
comments.add(((ConfigNodeComment) node).commentText());
|
||||||
|
} else if (node instanceof ConfigNodeSingleToken && Tokens.isNewline(((ConfigNodeSingleToken) node).token())) {
|
||||||
|
lineNumber++;
|
||||||
|
if (lastWasNewline) {
|
||||||
|
// Drop all comments if there was a blank line and start a new comment block
|
||||||
|
comments.clear();
|
||||||
|
}
|
||||||
|
lastWasNewline = true;
|
||||||
|
} else if (flavor != ConfigSyntax.JSON && node instanceof ConfigNodeInclude) {
|
||||||
|
parseInclude(values, (ConfigNodeInclude)node);
|
||||||
|
lastWasNewline = false;
|
||||||
|
} else if (node instanceof ConfigNodeField) {
|
||||||
|
lastWasNewline = false;
|
||||||
|
Path path = ((ConfigNodeField) node).path().value();
|
||||||
|
comments.addAll(((ConfigNodeField) node).comments());
|
||||||
|
|
||||||
|
// path must be on-stack while we parse the value
|
||||||
|
pathStack.push(path);
|
||||||
|
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
||||||
|
// we really should make this work, but for now throwing
|
||||||
|
// an exception is better than producing an incorrect
|
||||||
|
// result. See
|
||||||
|
// https://github.com/lightbend/config/issues/160
|
||||||
|
if (arrayCount > 0)
|
||||||
|
throw parseError("Due to current limitations of the config parser, += does not work nested inside a list. "
|
||||||
|
+ "+= expands to a ${} substitution and the path in ${} cannot currently refer to list elements. "
|
||||||
|
+ "You might be able to move the += outside of the list and then refer to it from inside the list with ${}.");
|
||||||
|
|
||||||
|
// because we will put it in an array after the fact so
|
||||||
|
// we want this to be incremented during the parseValue
|
||||||
|
// below in order to throw the above exception.
|
||||||
|
arrayCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractConfigNodeValue valueNode;
|
||||||
|
AbstractConfigValue newValue;
|
||||||
|
|
||||||
|
valueNode = ((ConfigNodeField) node).value();
|
||||||
|
|
||||||
|
// comments from the key token go to the value token
|
||||||
|
newValue = parseValue(valueNode, comments);
|
||||||
|
|
||||||
|
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
||||||
|
arrayCount -= 1;
|
||||||
|
|
||||||
|
List<AbstractConfigValue> concat = new ArrayList<AbstractConfigValue>(2);
|
||||||
|
AbstractConfigValue previousRef = new ConfigReference(newValue.origin(),
|
||||||
|
new SubstitutionExpression(fullCurrentPath(), true /* optional */));
|
||||||
|
AbstractConfigValue list = new SimpleConfigList(newValue.origin(),
|
||||||
|
Collections.singletonList(newValue));
|
||||||
|
concat.add(previousRef);
|
||||||
|
concat.add(list);
|
||||||
|
newValue = ConfigConcatenation.concatenate(concat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab any trailing comments on the same line
|
||||||
|
if (i < nodes.size() - 1) {
|
||||||
|
i++;
|
||||||
|
while (i < nodes.size()) {
|
||||||
|
if (nodes.get(i) instanceof ConfigNodeComment) {
|
||||||
|
ConfigNodeComment comment = (ConfigNodeComment) nodes.get(i);
|
||||||
|
newValue = newValue.withOrigin(newValue.origin().appendComments(
|
||||||
|
Collections.singletonList(comment.commentText())));
|
||||||
|
break;
|
||||||
|
} else if (nodes.get(i) instanceof ConfigNodeSingleToken) {
|
||||||
|
ConfigNodeSingleToken curr = (ConfigNodeSingleToken) nodes.get(i);
|
||||||
|
if (curr.token() == Tokens.COMMA || Tokens.isIgnoredWhitespace(curr.token())) {
|
||||||
|
// keep searching, as there could still be a comment
|
||||||
|
} else {
|
||||||
|
i--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pathStack.pop();
|
||||||
|
|
||||||
|
String key = path.first();
|
||||||
|
Path remaining = path.remainder();
|
||||||
|
|
||||||
|
if (remaining == null) {
|
||||||
|
AbstractConfigValue existing = values.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
// In strict JSON, dups should be an error; while in
|
||||||
|
// our custom config language, they should be merged
|
||||||
|
// if the value is an object (or substitution that
|
||||||
|
// could become an object).
|
||||||
|
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
throw parseError("JSON does not allow duplicate fields: '"
|
||||||
|
+ key
|
||||||
|
+ "' was already seen at "
|
||||||
|
+ existing.origin().description());
|
||||||
|
} else {
|
||||||
|
newValue = newValue.withFallback(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.put(key, newValue);
|
||||||
|
} else {
|
||||||
|
if (flavor == ConfigSyntax.JSON) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"somehow got multi-element path in JSON mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractConfigObject obj = createValueUnderPath(
|
||||||
|
remaining, newValue);
|
||||||
|
AbstractConfigValue existing = values.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
obj = obj.withFallback(existing);
|
||||||
|
}
|
||||||
|
values.put(key, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleConfigObject(objectOrigin, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleConfigList parseArray(ConfigNodeArray n) {
|
||||||
|
arrayCount += 1;
|
||||||
|
|
||||||
|
SimpleConfigOrigin arrayOrigin = lineOrigin();
|
||||||
|
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
|
||||||
|
|
||||||
|
boolean lastWasNewLine = false;
|
||||||
|
List<String> comments = new ArrayList<String>();
|
||||||
|
|
||||||
|
AbstractConfigValue v = null;
|
||||||
|
|
||||||
|
for (AbstractConfigNode node : n.children()) {
|
||||||
|
if (node instanceof ConfigNodeComment) {
|
||||||
|
comments.add(((ConfigNodeComment) node).commentText());
|
||||||
|
lastWasNewLine = false;
|
||||||
|
} else if (node instanceof ConfigNodeSingleToken && Tokens.isNewline(((ConfigNodeSingleToken) node).token())) {
|
||||||
|
lineNumber++;
|
||||||
|
if (lastWasNewLine && v == null) {
|
||||||
|
comments.clear();
|
||||||
|
} else if (v != null) {
|
||||||
|
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
||||||
|
comments.clear();
|
||||||
|
v = null;
|
||||||
|
}
|
||||||
|
lastWasNewLine = true;
|
||||||
|
} else if (node instanceof AbstractConfigNodeValue) {
|
||||||
|
lastWasNewLine = false;
|
||||||
|
if (v != null) {
|
||||||
|
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
||||||
|
comments.clear();
|
||||||
|
}
|
||||||
|
v = parseValue((AbstractConfigNodeValue)node, comments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// There shouldn't be any comments at this point, but add them just in case
|
||||||
|
if (v != null) {
|
||||||
|
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
||||||
|
}
|
||||||
|
arrayCount -= 1;
|
||||||
|
return new SimpleConfigList(arrayOrigin, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractConfigValue parse() {
|
||||||
|
AbstractConfigValue result = null;
|
||||||
|
ArrayList<String> comments = new ArrayList<String>();
|
||||||
|
boolean lastWasNewLine = false;
|
||||||
|
for (AbstractConfigNode node : document.children()) {
|
||||||
|
if (node instanceof ConfigNodeComment) {
|
||||||
|
comments.add(((ConfigNodeComment) node).commentText());
|
||||||
|
lastWasNewLine = false;
|
||||||
|
} else if (node instanceof ConfigNodeSingleToken) {
|
||||||
|
Token t = ((ConfigNodeSingleToken) node).token();
|
||||||
|
if (Tokens.isNewline(t)) {
|
||||||
|
lineNumber++;
|
||||||
|
if (lastWasNewLine && result == null) {
|
||||||
|
comments.clear();
|
||||||
|
} else if (result != null) {
|
||||||
|
result = result.withOrigin(result.origin().appendComments(new ArrayList<String>(comments)));
|
||||||
|
comments.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastWasNewLine = true;
|
||||||
|
}
|
||||||
|
} else if (node instanceof ConfigNodeComplexValue) {
|
||||||
|
result = parseValue((ConfigNodeComplexValue)node, comments);
|
||||||
|
lastWasNewLine = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+161
@@ -0,0 +1,161 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigReference replaces ConfigReference (the older class kept for back
|
||||||
|
* compat) and represents the ${} substitution syntax. It can resolve to any
|
||||||
|
* kind of value.
|
||||||
|
*/
|
||||||
|
final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
||||||
|
|
||||||
|
final private SubstitutionExpression expr;
|
||||||
|
// the length of any prefixes added with relativized()
|
||||||
|
final private int prefixLength;
|
||||||
|
|
||||||
|
ConfigReference(ConfigOrigin origin, SubstitutionExpression expr) {
|
||||||
|
this(origin, expr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigReference(ConfigOrigin origin, SubstitutionExpression expr, int prefixLength) {
|
||||||
|
super(origin);
|
||||||
|
this.expr = expr;
|
||||||
|
this.prefixLength = prefixLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigException.NotResolved notResolved() {
|
||||||
|
return new ConfigException.NotResolved(
|
||||||
|
"need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: "
|
||||||
|
+ this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object unwrapped() {
|
||||||
|
throw notResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigReference newCopy(ConfigOrigin newOrigin) {
|
||||||
|
return new ConfigReference(newOrigin, expr, prefixLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean ignoresFallbacks() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigReference> unmergedValues() {
|
||||||
|
return Collections.singleton(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigReference should be a firewall against NotPossibleToResolve going
|
||||||
|
// further up the stack; it should convert everything to ConfigException.
|
||||||
|
// This way it's impossible for NotPossibleToResolve to "escape" since
|
||||||
|
// any failure to resolve has to start with a ConfigReference.
|
||||||
|
@Override
|
||||||
|
ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(ResolveContext context, ResolveSource source) {
|
||||||
|
ResolveContext newContext = context.addCycleMarker(this);
|
||||||
|
AbstractConfigValue v;
|
||||||
|
try {
|
||||||
|
ResolveSource.ResultWithPath resultWithPath = source.lookupSubst(newContext, expr, prefixLength);
|
||||||
|
newContext = resultWithPath.result.context;
|
||||||
|
|
||||||
|
if (resultWithPath.result.value != null) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(), "recursively resolving " + resultWithPath
|
||||||
|
+ " which was the resolution of " + expr + " against " + source);
|
||||||
|
|
||||||
|
ResolveSource recursiveResolveSource = (new ResolveSource(
|
||||||
|
(AbstractConfigObject) resultWithPath.pathFromRoot.last(), resultWithPath.pathFromRoot));
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(), "will recursively resolve against " + recursiveResolveSource);
|
||||||
|
|
||||||
|
ResolveResult<? extends AbstractConfigValue> result = newContext.resolve(resultWithPath.result.value,
|
||||||
|
recursiveResolveSource);
|
||||||
|
v = result.value;
|
||||||
|
newContext = result.context;
|
||||||
|
} else {
|
||||||
|
ConfigValue fallback = context.options().getResolver().lookup(expr.path().render());
|
||||||
|
v = (AbstractConfigValue) fallback;
|
||||||
|
}
|
||||||
|
} catch (NotPossibleToResolve e) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(newContext.depth(),
|
||||||
|
"not possible to resolve " + expr + ", cycle involved: " + e.traceString());
|
||||||
|
if (expr.optional())
|
||||||
|
v = null;
|
||||||
|
else
|
||||||
|
throw new ConfigException.UnresolvedSubstitution(origin(), expr
|
||||||
|
+ " was part of a cycle of substitutions involving " + e.traceString(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == null && !expr.optional()) {
|
||||||
|
if (newContext.options().getAllowUnresolved())
|
||||||
|
return ResolveResult.make(newContext.removeCycleMarker(this), this);
|
||||||
|
else
|
||||||
|
throw new ConfigException.UnresolvedSubstitution(origin(), expr.toString());
|
||||||
|
} else {
|
||||||
|
return ResolveResult.make(newContext.removeCycleMarker(this), v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveStatus resolveStatus() {
|
||||||
|
return ResolveStatus.UNRESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when you graft a substitution into another object,
|
||||||
|
// you have to prefix it with the location in that object
|
||||||
|
// where you grafted it; but save prefixLength so
|
||||||
|
// system property and env variable lookups don't get
|
||||||
|
// broken.
|
||||||
|
@Override
|
||||||
|
ConfigReference relativized(Path prefix) {
|
||||||
|
SubstitutionExpression newExpr = expr.changePath(expr.path().prepend(prefix));
|
||||||
|
return new ConfigReference(origin(), newExpr, prefixLength + prefix.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof ConfigReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
if (other instanceof ConfigReference) {
|
||||||
|
return canEqual(other) && this.expr.equals(((ConfigReference) other).expr);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
return expr.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
sb.append(expr.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
SubstitutionExpression expression() {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
abstract class ConfigString extends AbstractConfigValue implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
final protected String value;
|
||||||
|
|
||||||
|
protected ConfigString(ConfigOrigin origin, String value) {
|
||||||
|
super(origin);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final static class Quoted extends ConfigString {
|
||||||
|
Quoted(ConfigOrigin origin, String value) {
|
||||||
|
super(origin, value);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected Quoted newCopy(ConfigOrigin origin) {
|
||||||
|
return new Quoted(origin, value);
|
||||||
|
}
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is sort of a hack; we want to preserve whether whitespace
|
||||||
|
// was quoted until we process substitutions, so we can ignore
|
||||||
|
// unquoted whitespace when concatenating lists or objects.
|
||||||
|
// We dump this distinction when serializing and deserializing,
|
||||||
|
// but that's OK because it isn't in equals/hashCode, and we
|
||||||
|
// don't allow serializing unresolved objects which is where
|
||||||
|
// quoted-ness matters. If we later make ConfigOrigin point
|
||||||
|
// to the original token range, we could use that to implement
|
||||||
|
// wasQuoted()
|
||||||
|
final static class Unquoted extends ConfigString {
|
||||||
|
Unquoted(ConfigOrigin origin, String value) {
|
||||||
|
super(origin, value);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected Unquoted newCopy(ConfigOrigin origin) {
|
||||||
|
return new Unquoted(origin, value);
|
||||||
|
}
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean wasQuoted() {
|
||||||
|
return (this instanceof Quoted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String unwrapped() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String transformToString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
String rendered;
|
||||||
|
if (options.getJson())
|
||||||
|
rendered = ConfigImplUtil.renderJsonString(value);
|
||||||
|
else
|
||||||
|
rendered = ConfigImplUtil.renderStringUnquotedIfPossible(value);
|
||||||
|
sb.append(rendered);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2014 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An AbstractConfigValue which contains other values. Java has no way to
|
||||||
|
* express "this has to be an AbstractConfigValue also" other than making
|
||||||
|
* AbstractConfigValue an interface which would be aggravating. But we can say
|
||||||
|
* we are a ConfigValue.
|
||||||
|
*/
|
||||||
|
interface Container extends ConfigValue {
|
||||||
|
/**
|
||||||
|
* Replace a child of this value. CAUTION if replacement is null, delete the
|
||||||
|
* child, which may also delete the parent, or make the parent into a
|
||||||
|
* non-container.
|
||||||
|
*/
|
||||||
|
AbstractConfigValue replaceChild(AbstractConfigValue child, AbstractConfigValue replacement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Super-expensive full traversal to see if descendant is anywhere
|
||||||
|
* underneath this container.
|
||||||
|
*/
|
||||||
|
boolean hasDescendant(AbstractConfigValue descendant);
|
||||||
|
}
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default automatic type transformations.
|
||||||
|
*/
|
||||||
|
final class DefaultTransformer {
|
||||||
|
|
||||||
|
static AbstractConfigValue transform(AbstractConfigValue value,
|
||||||
|
ConfigValueType requested) {
|
||||||
|
if (value.valueType() == ConfigValueType.STRING) {
|
||||||
|
String s = (String) value.unwrapped();
|
||||||
|
switch (requested) {
|
||||||
|
case NUMBER:
|
||||||
|
try {
|
||||||
|
Long v = Long.parseLong(s);
|
||||||
|
return new ConfigLong(value.origin(), v, s);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// try Double
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Double v = Double.parseDouble(s);
|
||||||
|
return new ConfigDouble(value.origin(), v, s);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// oh well.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NULL:
|
||||||
|
if (s.equals("null"))
|
||||||
|
return new ConfigNull(value.origin());
|
||||||
|
break;
|
||||||
|
case BOOLEAN:
|
||||||
|
if (s.equals("true") || s.equals("yes") || s.equals("on")) {
|
||||||
|
return new ConfigBoolean(value.origin(), true);
|
||||||
|
} else if (s.equals("false") || s.equals("no")
|
||||||
|
|| s.equals("off")) {
|
||||||
|
return new ConfigBoolean(value.origin(), false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LIST:
|
||||||
|
// can't go STRING to LIST automatically
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
// can't go STRING to OBJECT automatically
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
// no-op STRING to STRING
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (requested == ConfigValueType.STRING) {
|
||||||
|
// if we converted null to string here, then you wouldn't properly
|
||||||
|
// get a missing-value error if you tried to get a null value
|
||||||
|
// as a string.
|
||||||
|
switch (value.valueType()) {
|
||||||
|
case NUMBER: // FALL THROUGH
|
||||||
|
case BOOLEAN:
|
||||||
|
return new ConfigString.Quoted(value.origin(),
|
||||||
|
value.transformToString());
|
||||||
|
case NULL:
|
||||||
|
// want to be sure this throws instead of returning "null" as a
|
||||||
|
// string
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
// no OBJECT to STRING automatically
|
||||||
|
break;
|
||||||
|
case LIST:
|
||||||
|
// no LIST to STRING automatically
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
// no-op STRING to STRING
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (requested == ConfigValueType.LIST && value.valueType() == ConfigValueType.OBJECT) {
|
||||||
|
// attempt to convert an array-like (numeric indices) object to a
|
||||||
|
// list. This would be used with .properties syntax for example:
|
||||||
|
// -Dfoo.0=bar -Dfoo.1=baz
|
||||||
|
// To ensure we still throw type errors for objects treated
|
||||||
|
// as lists in most cases, we'll refuse to convert if the object
|
||||||
|
// does not contain any numeric keys. This means we don't allow
|
||||||
|
// empty objects here though :-/
|
||||||
|
AbstractConfigObject o = (AbstractConfigObject) value;
|
||||||
|
Map<Integer, AbstractConfigValue> values = new HashMap<Integer, AbstractConfigValue>();
|
||||||
|
for (String key : o.keySet()) {
|
||||||
|
int i;
|
||||||
|
try {
|
||||||
|
i = Integer.parseInt(key, 10);
|
||||||
|
if (i < 0)
|
||||||
|
continue;
|
||||||
|
values.put(i, o.get(key));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
ArrayList<Map.Entry<Integer, AbstractConfigValue>> entryList = new ArrayList<Map.Entry<Integer, AbstractConfigValue>>(
|
||||||
|
values.entrySet());
|
||||||
|
// sort by numeric index
|
||||||
|
Collections.sort(entryList,
|
||||||
|
new Comparator<Map.Entry<Integer, AbstractConfigValue>>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Map.Entry<Integer, AbstractConfigValue> a,
|
||||||
|
Map.Entry<Integer, AbstractConfigValue> b) {
|
||||||
|
return Integer.compare(a.getKey(), b.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// drop the indices (we allow gaps in the indices, for better or
|
||||||
|
// worse)
|
||||||
|
ArrayList<AbstractConfigValue> list = new ArrayList<AbstractConfigValue>();
|
||||||
|
for (Map.Entry<Integer, AbstractConfigValue> entry : entryList) {
|
||||||
|
list.add(entry.getValue());
|
||||||
|
}
|
||||||
|
return new SimpleConfigList(value.origin(), list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
enum FromMapMode {
|
||||||
|
KEYS_ARE_PATHS, KEYS_ARE_KEYS
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluder;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluderClasspath;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluderFile;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluderURL;
|
||||||
|
|
||||||
|
interface FullIncluder extends ConfigIncluder, ConfigIncluderFile, ConfigIncluderURL,
|
||||||
|
ConfigIncluderClasspath {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
/** The key used to memoize already-traversed nodes when resolving substitutions */
|
||||||
|
final class MemoKey {
|
||||||
|
MemoKey(AbstractConfigValue value, Path restrictToChildOrNull) {
|
||||||
|
this.value = value;
|
||||||
|
this.restrictToChildOrNull = restrictToChildOrNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private AbstractConfigValue value;
|
||||||
|
final private Path restrictToChildOrNull;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
int h = System.identityHashCode(value);
|
||||||
|
if (restrictToChildOrNull != null) {
|
||||||
|
return h + 41 * (41 + restrictToChildOrNull.hashCode());
|
||||||
|
} else {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean equals(Object other) {
|
||||||
|
if (other instanceof MemoKey) {
|
||||||
|
MemoKey o = (MemoKey) other;
|
||||||
|
if (o.value != this.value)
|
||||||
|
return false;
|
||||||
|
else if (o.restrictToChildOrNull == this.restrictToChildOrNull)
|
||||||
|
return true;
|
||||||
|
else if (o.restrictToChildOrNull == null || this.restrictToChildOrNull == null)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return o.restrictToChildOrNull.equals(this.restrictToChildOrNull);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return "MemoKey(" + value + "@" + System.identityHashCode(value) + "," + restrictToChildOrNull + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigMergeable;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
|
||||||
|
interface MergeableValue extends ConfigMergeable {
|
||||||
|
// converts a Config to its root object and a ConfigValue to itself
|
||||||
|
ConfigValue toFallbackValue();
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
// caution: ordinals used in serialization
|
||||||
|
enum OriginType {
|
||||||
|
GENERIC,
|
||||||
|
FILE,
|
||||||
|
URL,
|
||||||
|
RESOURCE
|
||||||
|
}
|
||||||
@@ -0,0 +1,891 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FilterReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncludeContext;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseable;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.parser.ConfigDocument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation detail, not ABI stable, do not touch.
|
||||||
|
* For use only by the {@link com.typesafe.config} package.
|
||||||
|
* The point of this class is to avoid "propagating" each
|
||||||
|
* overload on "thing which can be parsed" through multiple
|
||||||
|
* interfaces. Most interfaces can have just one overload that
|
||||||
|
* takes a Parseable. Also it's used as an abstract "resource
|
||||||
|
* handle" in the ConfigIncluder interface.
|
||||||
|
*/
|
||||||
|
public abstract class Parseable implements ConfigParseable {
|
||||||
|
private ConfigIncludeContext includeContext;
|
||||||
|
private ConfigParseOptions initialOptions;
|
||||||
|
private ConfigOrigin initialOrigin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation detail, not ABI stable, do not touch.
|
||||||
|
*/
|
||||||
|
protected interface Relativizer {
|
||||||
|
ConfigParseable relativeTo(String filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ThreadLocal<LinkedList<Parseable>> parseStack = new ThreadLocal<LinkedList<Parseable>>() {
|
||||||
|
@Override
|
||||||
|
protected LinkedList<Parseable> initialValue() {
|
||||||
|
return new LinkedList<Parseable>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int MAX_INCLUDE_DEPTH = 50;
|
||||||
|
|
||||||
|
protected Parseable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigParseOptions fixupOptions(ConfigParseOptions baseOptions) {
|
||||||
|
ConfigSyntax syntax = baseOptions.getSyntax();
|
||||||
|
if (syntax == null) {
|
||||||
|
syntax = guessSyntax();
|
||||||
|
}
|
||||||
|
if (syntax == null) {
|
||||||
|
syntax = ConfigSyntax.CONF;
|
||||||
|
}
|
||||||
|
ConfigParseOptions modified = baseOptions.setSyntax(syntax);
|
||||||
|
|
||||||
|
// make sure the app-provided includer falls back to default
|
||||||
|
modified = modified.appendIncluder(ConfigImpl.defaultIncluder());
|
||||||
|
// make sure the app-provided includer is complete
|
||||||
|
modified = modified.setIncluder(SimpleIncluder.makeFull(modified.getIncluder()));
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void postConstruct(ConfigParseOptions baseOptions) {
|
||||||
|
this.initialOptions = fixupOptions(baseOptions);
|
||||||
|
|
||||||
|
this.includeContext = new SimpleIncludeContext(this);
|
||||||
|
|
||||||
|
if (initialOptions.getOriginDescription() != null)
|
||||||
|
initialOrigin = SimpleConfigOrigin.newSimple(initialOptions.getOriginDescription());
|
||||||
|
else
|
||||||
|
initialOrigin = createOrigin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// the general idea is that any work should be in here, not in the
|
||||||
|
// constructor, so that exceptions are thrown from the public parse()
|
||||||
|
// function and not from the creation of the Parseable.
|
||||||
|
// Essentially this is a lazy field. The parser should close the
|
||||||
|
// reader when it's done with it.
|
||||||
|
// ALSO, IMPORTANT: if the file or URL is not found, this must throw.
|
||||||
|
// to support the "allow missing" feature.
|
||||||
|
protected abstract Reader reader() throws IOException;
|
||||||
|
|
||||||
|
protected Reader reader(ConfigParseOptions options) throws IOException {
|
||||||
|
return reader();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void trace(String message) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled()) {
|
||||||
|
ConfigImpl.trace(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSyntax guessSyntax() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSyntax contentType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigParseable relativeTo(String filename) {
|
||||||
|
// fall back to classpath; we treat the "filename" as absolute
|
||||||
|
// (don't add a package name in front),
|
||||||
|
// if it starts with "/" then remove the "/", for consistency
|
||||||
|
// with ParseableResources.relativeTo
|
||||||
|
String resource = filename;
|
||||||
|
if (filename.startsWith("/"))
|
||||||
|
resource = filename.substring(1);
|
||||||
|
return newResources(resource, options().setOriginDescription(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigIncludeContext includeContext() {
|
||||||
|
return includeContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject forceParsedToObject(ConfigValue value) {
|
||||||
|
if (value instanceof AbstractConfigObject) {
|
||||||
|
return (AbstractConfigObject) value;
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.WrongType(value.origin(), "", "object at file root", value
|
||||||
|
.valueType().name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject parse(ConfigParseOptions baseOptions) {
|
||||||
|
|
||||||
|
LinkedList<Parseable> stack = parseStack.get();
|
||||||
|
if (stack.size() >= MAX_INCLUDE_DEPTH) {
|
||||||
|
throw new ConfigException.Parse(initialOrigin, "include statements nested more than "
|
||||||
|
+ MAX_INCLUDE_DEPTH
|
||||||
|
+ " times, you probably have a cycle in your includes. Trace: " + stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.addFirst(this);
|
||||||
|
try {
|
||||||
|
return forceParsedToObject(parseValue(baseOptions));
|
||||||
|
} finally {
|
||||||
|
stack.removeFirst();
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
parseStack.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final AbstractConfigValue parseValue(ConfigParseOptions baseOptions) {
|
||||||
|
// note that we are NOT using our "initialOptions",
|
||||||
|
// but using the ones from the passed-in options. The idea is that
|
||||||
|
// callers can get our original options and then parse with different
|
||||||
|
// ones if they want.
|
||||||
|
ConfigParseOptions options = fixupOptions(baseOptions);
|
||||||
|
|
||||||
|
// passed-in options can override origin
|
||||||
|
ConfigOrigin origin;
|
||||||
|
if (options.getOriginDescription() != null)
|
||||||
|
origin = SimpleConfigOrigin.newSimple(options.getOriginDescription());
|
||||||
|
else
|
||||||
|
origin = initialOrigin;
|
||||||
|
return parseValue(origin, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
final private AbstractConfigValue parseValue(ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) {
|
||||||
|
try {
|
||||||
|
return rawParseValue(origin, finalOptions);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (finalOptions.getAllowMissing()) {
|
||||||
|
trace(e.getMessage() + ". Allowing Missing File, this can be turned off by setting" +
|
||||||
|
" ConfigParseOptions.allowMissing = false");
|
||||||
|
return SimpleConfigObject.emptyMissing(origin);
|
||||||
|
} else {
|
||||||
|
trace("exception loading " + origin.description() + ": " + e.getClass().getName()
|
||||||
|
+ ": " + e.getMessage());
|
||||||
|
throw new ConfigException.IO(origin,
|
||||||
|
e.getClass().getName() + ": " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ConfigDocument parseDocument(ConfigParseOptions baseOptions) {
|
||||||
|
// note that we are NOT using our "initialOptions",
|
||||||
|
// but using the ones from the passed-in options. The idea is that
|
||||||
|
// callers can get our original options and then parse with different
|
||||||
|
// ones if they want.
|
||||||
|
ConfigParseOptions options = fixupOptions(baseOptions);
|
||||||
|
|
||||||
|
// passed-in options can override origin
|
||||||
|
ConfigOrigin origin;
|
||||||
|
if (options.getOriginDescription() != null)
|
||||||
|
origin = SimpleConfigOrigin.newSimple(options.getOriginDescription());
|
||||||
|
else
|
||||||
|
origin = initialOrigin;
|
||||||
|
return parseDocument(origin, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
final private ConfigDocument parseDocument(ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) {
|
||||||
|
try {
|
||||||
|
return rawParseDocument(origin, finalOptions);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (finalOptions.getAllowMissing()) {
|
||||||
|
ArrayList<AbstractConfigNode> children = new ArrayList<AbstractConfigNode>();
|
||||||
|
children.add(new ConfigNodeObject(new ArrayList<AbstractConfigNode>()));
|
||||||
|
return new SimpleConfigDocument(new ConfigNodeRoot(children, origin), finalOptions);
|
||||||
|
} else {
|
||||||
|
trace("exception loading " + origin.description() + ": " + e.getClass().getName()
|
||||||
|
+ ": " + e.getMessage());
|
||||||
|
throw new ConfigException.IO(origin,
|
||||||
|
e.getClass().getName() + ": " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is parseValue without post-processing the IOException or handling
|
||||||
|
// options.getAllowMissing()
|
||||||
|
protected AbstractConfigValue rawParseValue(ConfigOrigin origin, ConfigParseOptions finalOptions)
|
||||||
|
throws IOException {
|
||||||
|
Reader reader = reader(finalOptions);
|
||||||
|
|
||||||
|
// after reader() we will have loaded the Content-Type.
|
||||||
|
ConfigSyntax contentType = contentType();
|
||||||
|
|
||||||
|
ConfigParseOptions optionsWithContentType;
|
||||||
|
if (contentType != null) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled() && finalOptions.getSyntax() != null)
|
||||||
|
trace("Overriding syntax " + finalOptions.getSyntax()
|
||||||
|
+ " with Content-Type which specified " + contentType);
|
||||||
|
|
||||||
|
optionsWithContentType = finalOptions.setSyntax(contentType);
|
||||||
|
} else {
|
||||||
|
optionsWithContentType = finalOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return rawParseValue(reader, origin, optionsWithContentType);
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractConfigValue rawParseValue(Reader reader, ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) throws IOException {
|
||||||
|
if (finalOptions.getSyntax() == ConfigSyntax.PROPERTIES) {
|
||||||
|
return PropertiesParser.parse(reader, origin);
|
||||||
|
} else {
|
||||||
|
Iterator<Token> tokens = Tokenizer.tokenize(origin, reader, finalOptions.getSyntax());
|
||||||
|
ConfigNodeRoot document = ConfigDocumentParser.parse(tokens, origin, finalOptions);
|
||||||
|
return ConfigParser.parse(document, origin, finalOptions, includeContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is parseDocument without post-processing the IOException or handling
|
||||||
|
// options.getAllowMissing()
|
||||||
|
protected ConfigDocument rawParseDocument(ConfigOrigin origin, ConfigParseOptions finalOptions)
|
||||||
|
throws IOException {
|
||||||
|
Reader reader = reader(finalOptions);
|
||||||
|
|
||||||
|
// after reader() we will have loaded the Content-Type.
|
||||||
|
ConfigSyntax contentType = contentType();
|
||||||
|
|
||||||
|
ConfigParseOptions optionsWithContentType;
|
||||||
|
if (contentType != null) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled() && finalOptions.getSyntax() != null)
|
||||||
|
trace("Overriding syntax " + finalOptions.getSyntax()
|
||||||
|
+ " with Content-Type which specified " + contentType);
|
||||||
|
|
||||||
|
optionsWithContentType = finalOptions.setSyntax(contentType);
|
||||||
|
} else {
|
||||||
|
optionsWithContentType = finalOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return rawParseDocument(reader, origin, optionsWithContentType);
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigDocument rawParseDocument(Reader reader, ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) throws IOException {
|
||||||
|
Iterator<Token> tokens = Tokenizer.tokenize(origin, reader, finalOptions.getSyntax());
|
||||||
|
return new SimpleConfigDocument(ConfigDocumentParser.parse(tokens, origin, finalOptions), finalOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigObject parse() {
|
||||||
|
return forceParsedToObject(parseValue(options()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigDocument parseConfigDocument() {
|
||||||
|
return parseDocument(options());
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractConfigValue parseValue() {
|
||||||
|
return parseValue(options());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ConfigOrigin origin() {
|
||||||
|
return initialOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract ConfigOrigin createOrigin();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigParseOptions options() {
|
||||||
|
return initialOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfigSyntax syntaxFromExtension(String name) {
|
||||||
|
if (name.endsWith(".json"))
|
||||||
|
return ConfigSyntax.JSON;
|
||||||
|
else if (name.endsWith(".conf"))
|
||||||
|
return ConfigSyntax.CONF;
|
||||||
|
else if (name.endsWith(".properties"))
|
||||||
|
return ConfigSyntax.PROPERTIES;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Reader readerFromStream(InputStream input) {
|
||||||
|
return readerFromStream(input, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Reader readerFromStream(InputStream input, String encoding) {
|
||||||
|
try {
|
||||||
|
// well, this is messed up. If we aren't going to close
|
||||||
|
// the passed-in InputStream then we have no way to
|
||||||
|
// close these readers. So maybe we should not have an
|
||||||
|
// InputStream version, only a Reader version.
|
||||||
|
Reader reader = new InputStreamReader(input, encoding);
|
||||||
|
return new BufferedReader(reader);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new ConfigException.BugOrBroken("Java runtime does not support UTF-8", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Reader doNotClose(Reader input) {
|
||||||
|
return new FilterReader(input) {
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// NOTHING.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static URL relativeTo(URL url, String filename) {
|
||||||
|
// I'm guessing this completely fails on Windows, help wanted
|
||||||
|
if (new File(filename).isAbsolute())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URI siblingURI = url.toURI();
|
||||||
|
URI relative = new URI(filename);
|
||||||
|
|
||||||
|
// this seems wrong, but it's documented that the last
|
||||||
|
// element of the path in siblingURI gets stripped out,
|
||||||
|
// so to get something in the same directory as
|
||||||
|
// siblingURI we just call resolve().
|
||||||
|
URL resolved = siblingURI.resolve(relative).toURL();
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
return null;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static File relativeTo(File file, String filename) {
|
||||||
|
File child = new File(filename);
|
||||||
|
|
||||||
|
if (child.isAbsolute())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
File parent = file.getParentFile();
|
||||||
|
|
||||||
|
if (parent == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return new File(parent, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a parseable that doesn't exist and just throws when you try to
|
||||||
|
// parse it
|
||||||
|
private final static class ParseableNotFound extends Parseable {
|
||||||
|
final private String what;
|
||||||
|
final private String message;
|
||||||
|
|
||||||
|
ParseableNotFound(String what, String message, ConfigParseOptions options) {
|
||||||
|
this.what = what;
|
||||||
|
this.message = message;
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader() throws IOException {
|
||||||
|
throw new FileNotFoundException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newSimple(what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newNotFound(String whatNotFound, String message,
|
||||||
|
ConfigParseOptions options) {
|
||||||
|
return new ParseableNotFound(whatNotFound, message, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static class ParseableReader extends Parseable {
|
||||||
|
final private Reader reader;
|
||||||
|
|
||||||
|
ParseableReader(Reader reader, ConfigParseOptions options) {
|
||||||
|
this.reader = reader;
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader() {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("Loading config from reader " + reader);
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newSimple("Reader");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note that we will never close this reader; you have to do it when parsing
|
||||||
|
// is complete.
|
||||||
|
public static Parseable newReader(Reader reader, ConfigParseOptions options) {
|
||||||
|
|
||||||
|
return new ParseableReader(doNotClose(reader), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static class ParseableString extends Parseable {
|
||||||
|
final private String input;
|
||||||
|
|
||||||
|
ParseableString(String input, ConfigParseOptions options) {
|
||||||
|
this.input = input;
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader() {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("Loading config from a String " + input);
|
||||||
|
return new StringReader(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newSimple("String");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(" + input + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newString(String input, ConfigParseOptions options) {
|
||||||
|
return new ParseableString(input, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String jsonContentType = "application/json";
|
||||||
|
private static final String propertiesContentType = "text/x-java-properties";
|
||||||
|
private static final String hoconContentType = "application/hocon";
|
||||||
|
|
||||||
|
private static class ParseableURL extends Parseable {
|
||||||
|
final protected URL input;
|
||||||
|
private String contentType = null;
|
||||||
|
|
||||||
|
protected ParseableURL(URL input) {
|
||||||
|
this.input = input;
|
||||||
|
// does not postConstruct (subclass does it)
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseableURL(URL input, ConfigParseOptions options) {
|
||||||
|
this(input);
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader() throws IOException {
|
||||||
|
throw new ConfigException.BugOrBroken("reader() without options should not be called on ParseableURL");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String acceptContentType(ConfigParseOptions options) {
|
||||||
|
if (options.getSyntax() == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
switch (options.getSyntax()) {
|
||||||
|
case JSON:
|
||||||
|
return jsonContentType;
|
||||||
|
case CONF:
|
||||||
|
return hoconContentType;
|
||||||
|
case PROPERTIES:
|
||||||
|
return propertiesContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not sure this is reachable but javac thinks it is
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader(ConfigParseOptions options) throws IOException {
|
||||||
|
try {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("Loading config from a URL: " + input.toExternalForm());
|
||||||
|
URLConnection connection = input.openConnection();
|
||||||
|
|
||||||
|
// allow server to serve multiple types from one URL
|
||||||
|
String acceptContent = acceptContentType(options);
|
||||||
|
if (acceptContent != null) {
|
||||||
|
connection.setRequestProperty("Accept", acceptContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
// save content type for later
|
||||||
|
contentType = connection.getContentType();
|
||||||
|
if (contentType != null) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("URL sets Content-Type: '" + contentType + "'");
|
||||||
|
contentType = contentType.trim();
|
||||||
|
int semi = contentType.indexOf(';');
|
||||||
|
if (semi >= 0)
|
||||||
|
contentType = contentType.substring(0, semi);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream stream = connection.getInputStream();
|
||||||
|
|
||||||
|
return readerFromStream(stream);
|
||||||
|
} catch (FileNotFoundException fnf) {
|
||||||
|
// If the resource is not found (HTTP response
|
||||||
|
// code 404 or something alike), then it's fine to
|
||||||
|
// treat it according to the allowMissing setting
|
||||||
|
// and "include" spec. But if we have something
|
||||||
|
// like HTTP 503 it seems to be better to fail
|
||||||
|
// early, because this may be a sign of broken
|
||||||
|
// environment. Java throws FileNotFoundException
|
||||||
|
// if it sees 404 or 410.
|
||||||
|
throw fnf;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ConfigException.BugOrBroken("Cannot load config from URL: " + input.toExternalForm(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigSyntax guessSyntax() {
|
||||||
|
return syntaxFromExtension(input.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigSyntax contentType() {
|
||||||
|
if (contentType != null) {
|
||||||
|
if (contentType.equals(jsonContentType))
|
||||||
|
return ConfigSyntax.JSON;
|
||||||
|
else if (contentType.equals(propertiesContentType))
|
||||||
|
return ConfigSyntax.PROPERTIES;
|
||||||
|
else if (contentType.equals(hoconContentType))
|
||||||
|
return ConfigSyntax.CONF;
|
||||||
|
else {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("'" + contentType + "' isn't a known content type");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigParseable relativeTo(String filename) {
|
||||||
|
URL url = relativeTo(input, filename);
|
||||||
|
if (url == null)
|
||||||
|
return null;
|
||||||
|
return newURL(url, options().setOriginDescription(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newURL(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(" + input.toExternalForm() + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newURL(URL input, ConfigParseOptions options) {
|
||||||
|
// we want file: URLs and files to always behave the same, so switch
|
||||||
|
// to a file if it's a file: URL
|
||||||
|
if (input.getProtocol().equals("file")) {
|
||||||
|
return newFile(ConfigImplUtil.urlToFile(input), options);
|
||||||
|
} else {
|
||||||
|
return new ParseableURL(input, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static class ParseableFile extends Parseable {
|
||||||
|
final private File input;
|
||||||
|
|
||||||
|
ParseableFile(File input, ConfigParseOptions options) {
|
||||||
|
this.input = input;
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader() throws IOException {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("Loading config from a file: " + input);
|
||||||
|
InputStream stream = new FileInputStream(input);
|
||||||
|
return readerFromStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigSyntax guessSyntax() {
|
||||||
|
return syntaxFromExtension(input.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigParseable relativeTo(String filename) {
|
||||||
|
File sibling;
|
||||||
|
if ((new File(filename)).isAbsolute()) {
|
||||||
|
sibling = new File(filename);
|
||||||
|
} else {
|
||||||
|
// this may return null
|
||||||
|
sibling = relativeTo(input, filename);
|
||||||
|
}
|
||||||
|
if (sibling == null)
|
||||||
|
return null;
|
||||||
|
if (sibling.exists()) {
|
||||||
|
trace(sibling + " exists, so loading it as a file");
|
||||||
|
return newFile(sibling, options().setOriginDescription(null));
|
||||||
|
} else {
|
||||||
|
trace(sibling + " does not exist, so trying it as a classpath resource");
|
||||||
|
return super.relativeTo(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newFile(input.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(" + input.getPath() + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newFile(File input, ConfigParseOptions options) {
|
||||||
|
return new ParseableFile(input, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final static class ParseableResourceURL extends ParseableURL {
|
||||||
|
|
||||||
|
private final Relativizer relativizer;
|
||||||
|
private final String resource;
|
||||||
|
|
||||||
|
ParseableResourceURL(URL input, ConfigParseOptions options, String resource, Relativizer relativizer) {
|
||||||
|
super(input);
|
||||||
|
this.relativizer = relativizer;
|
||||||
|
this.resource = resource;
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newResource(resource, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigParseable relativeTo(String filename) {
|
||||||
|
return relativizer.relativeTo(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Parseable newResourceURL(URL input, ConfigParseOptions options, String resource, Relativizer relativizer) {
|
||||||
|
return new ParseableResourceURL(input, options, resource, relativizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static class ParseableResources extends Parseable implements Relativizer {
|
||||||
|
final private String resource;
|
||||||
|
|
||||||
|
ParseableResources(String resource, ConfigParseOptions options) {
|
||||||
|
this.resource = resource;
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader() throws IOException {
|
||||||
|
throw new ConfigException.BugOrBroken("reader() should not be called on resources");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigObject rawParseValue(ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) throws IOException {
|
||||||
|
ClassLoader loader = finalOptions.getClassLoader();
|
||||||
|
if (loader == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"null class loader; pass in a class loader or use Thread.currentThread().setContextClassLoader()");
|
||||||
|
Enumeration<URL> e = loader.getResources(resource);
|
||||||
|
if (!e.hasMoreElements()) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("Loading config from class loader " + loader
|
||||||
|
+ " but there were no resources called " + resource);
|
||||||
|
throw new IOException("resource not found on classpath: " + resource);
|
||||||
|
}
|
||||||
|
AbstractConfigObject merged = SimpleConfigObject.empty(origin);
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
URL url = e.nextElement();
|
||||||
|
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("Loading config from resource '" + resource + "' URL " + url.toExternalForm() + " from class loader "
|
||||||
|
+ loader);
|
||||||
|
|
||||||
|
Parseable element = newResourceURL(url, finalOptions, resource, this);
|
||||||
|
|
||||||
|
AbstractConfigValue v = element.parseValue();
|
||||||
|
|
||||||
|
merged = merged.withFallback(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigSyntax guessSyntax() {
|
||||||
|
return syntaxFromExtension(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String parent(String resource) {
|
||||||
|
// the "resource" is not supposed to begin with a "/"
|
||||||
|
// because it's supposed to be the raw resource
|
||||||
|
// (ClassLoader#getResource), not the
|
||||||
|
// resource "syntax" (Class#getResource)
|
||||||
|
int i = resource.lastIndexOf('/');
|
||||||
|
if (i < 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return resource.substring(0, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigParseable relativeTo(String sibling) {
|
||||||
|
if (sibling.startsWith("/")) {
|
||||||
|
// if it starts with "/" then don't make it relative to
|
||||||
|
// the including resource
|
||||||
|
return newResources(sibling.substring(1), options().setOriginDescription(null));
|
||||||
|
} else {
|
||||||
|
// here we want to build a new resource name and let
|
||||||
|
// the class loader have it, rather than getting the
|
||||||
|
// url with getResource() and relativizing to that url.
|
||||||
|
// This is needed in case the class loader is going to
|
||||||
|
// search a classpath.
|
||||||
|
String parent = parent(resource);
|
||||||
|
if (parent == null)
|
||||||
|
return newResources(sibling, options().setOriginDescription(null));
|
||||||
|
else
|
||||||
|
return newResources(parent + "/" + sibling, options()
|
||||||
|
.setOriginDescription(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newResource(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(" + resource + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newResources(Class<?> klass, String resource, ConfigParseOptions options) {
|
||||||
|
return newResources(convertResourceName(klass, resource),
|
||||||
|
options.setClassLoader(klass.getClassLoader()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function is supposed to emulate the difference
|
||||||
|
// between Class.getResource and ClassLoader.getResource
|
||||||
|
// (unfortunately there doesn't seem to be public API for it).
|
||||||
|
// We're using it because the Class API is more limited,
|
||||||
|
// for example it lacks getResources(). So we want to be able to
|
||||||
|
// use ClassLoader directly.
|
||||||
|
private static String convertResourceName(Class<?> klass, String resource) {
|
||||||
|
if (resource.startsWith("/")) {
|
||||||
|
// "absolute" resource, chop the slash
|
||||||
|
return resource.substring(1);
|
||||||
|
} else {
|
||||||
|
String className = klass.getName();
|
||||||
|
int i = className.lastIndexOf('.');
|
||||||
|
if (i < 0) {
|
||||||
|
// no package
|
||||||
|
return resource;
|
||||||
|
} else {
|
||||||
|
// need to be relative to the package
|
||||||
|
String packageName = className.substring(0, i);
|
||||||
|
String packagePath = packageName.replace('.', '/');
|
||||||
|
return packagePath + "/" + resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newResources(String resource, ConfigParseOptions options) {
|
||||||
|
if (options.getClassLoader() == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"null class loader; pass in a class loader or use Thread.currentThread().setContextClassLoader()");
|
||||||
|
return new ParseableResources(resource, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static class ParseableProperties extends Parseable {
|
||||||
|
final private Properties props;
|
||||||
|
|
||||||
|
ParseableProperties(Properties props, ConfigParseOptions options) {
|
||||||
|
this.props = props;
|
||||||
|
postConstruct(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Reader reader() throws IOException {
|
||||||
|
throw new ConfigException.BugOrBroken("reader() should not be called on props");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigObject rawParseValue(ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
trace("Loading config from properties " + props);
|
||||||
|
return PropertiesParser.fromProperties(origin, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigSyntax guessSyntax() {
|
||||||
|
return ConfigSyntax.PROPERTIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConfigOrigin createOrigin() {
|
||||||
|
return SimpleConfigOrigin.newSimple("properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(" + props.size() + " props)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newProperties(Properties properties, ConfigParseOptions options) {
|
||||||
|
return new ParseableProperties(properties, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
final class Path {
|
||||||
|
|
||||||
|
final private String first;
|
||||||
|
final private Path remainder;
|
||||||
|
|
||||||
|
Path(String first, Path remainder) {
|
||||||
|
this.first = first;
|
||||||
|
this.remainder = remainder;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path(String... elements) {
|
||||||
|
if (elements.length == 0)
|
||||||
|
throw new ConfigException.BugOrBroken("empty path");
|
||||||
|
this.first = elements[0];
|
||||||
|
if (elements.length > 1) {
|
||||||
|
PathBuilder pb = new PathBuilder();
|
||||||
|
for (int i = 1; i < elements.length; ++i) {
|
||||||
|
pb.appendKey(elements[i]);
|
||||||
|
}
|
||||||
|
this.remainder = pb.result();
|
||||||
|
} else {
|
||||||
|
this.remainder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// append all the paths in the list together into one path
|
||||||
|
Path(List<Path> pathsToConcat) {
|
||||||
|
this(pathsToConcat.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
// append all the paths in the iterator together into one path
|
||||||
|
Path(Iterator<Path> i) {
|
||||||
|
if (!i.hasNext())
|
||||||
|
throw new ConfigException.BugOrBroken("empty path");
|
||||||
|
|
||||||
|
Path firstPath = i.next();
|
||||||
|
this.first = firstPath.first;
|
||||||
|
|
||||||
|
PathBuilder pb = new PathBuilder();
|
||||||
|
if (firstPath.remainder != null) {
|
||||||
|
pb.appendPath(firstPath.remainder);
|
||||||
|
}
|
||||||
|
while (i.hasNext()) {
|
||||||
|
pb.appendPath(i.next());
|
||||||
|
}
|
||||||
|
this.remainder = pb.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
String first() {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return path minus the first element or null if no more elements
|
||||||
|
*/
|
||||||
|
Path remainder() {
|
||||||
|
return remainder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return path minus the last element or null if we have just one element
|
||||||
|
*/
|
||||||
|
Path parent() {
|
||||||
|
if (remainder == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
PathBuilder pb = new PathBuilder();
|
||||||
|
Path p = this;
|
||||||
|
while (p.remainder != null) {
|
||||||
|
pb.appendKey(p.first);
|
||||||
|
p = p.remainder;
|
||||||
|
}
|
||||||
|
return pb.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return last element in the path
|
||||||
|
*/
|
||||||
|
String last() {
|
||||||
|
Path p = this;
|
||||||
|
while (p.remainder != null) {
|
||||||
|
p = p.remainder;
|
||||||
|
}
|
||||||
|
return p.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path prepend(Path toPrepend) {
|
||||||
|
PathBuilder pb = new PathBuilder();
|
||||||
|
pb.appendPath(toPrepend);
|
||||||
|
pb.appendPath(this);
|
||||||
|
return pb.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
int length() {
|
||||||
|
int count = 1;
|
||||||
|
Path p = remainder;
|
||||||
|
while (p != null) {
|
||||||
|
count += 1;
|
||||||
|
p = p.remainder;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path subPath(int removeFromFront) {
|
||||||
|
int count = removeFromFront;
|
||||||
|
Path p = this;
|
||||||
|
while (p != null && count > 0) {
|
||||||
|
count -= 1;
|
||||||
|
p = p.remainder;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path subPath(int firstIndex, int lastIndex) {
|
||||||
|
if (lastIndex < firstIndex)
|
||||||
|
throw new ConfigException.BugOrBroken("bad call to subPath");
|
||||||
|
|
||||||
|
Path from = subPath(firstIndex);
|
||||||
|
PathBuilder pb = new PathBuilder();
|
||||||
|
int count = lastIndex - firstIndex;
|
||||||
|
while (count > 0) {
|
||||||
|
count -= 1;
|
||||||
|
pb.appendKey(from.first());
|
||||||
|
from = from.remainder();
|
||||||
|
if (from == null)
|
||||||
|
throw new ConfigException.BugOrBroken("subPath lastIndex out of range " + lastIndex);
|
||||||
|
}
|
||||||
|
return pb.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean startsWith(Path other) {
|
||||||
|
Path myRemainder = this;
|
||||||
|
Path otherRemainder = other;
|
||||||
|
if (otherRemainder.length() <= myRemainder.length()) {
|
||||||
|
while(otherRemainder != null) {
|
||||||
|
if (!otherRemainder.first().equals(myRemainder.first()))
|
||||||
|
return false;
|
||||||
|
myRemainder = myRemainder.remainder();
|
||||||
|
otherRemainder = otherRemainder.remainder();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof Path) {
|
||||||
|
Path that = (Path) other;
|
||||||
|
return this.first.equals(that.first)
|
||||||
|
&& ConfigImplUtil.equalsHandlingNull(this.remainder,
|
||||||
|
that.remainder);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 41 * (41 + first.hashCode())
|
||||||
|
+ (remainder == null ? 0 : remainder.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// this doesn't have a very precise meaning, just to reduce
|
||||||
|
// noise from quotes in the rendered path for average cases
|
||||||
|
static boolean hasFunkyChars(String s) {
|
||||||
|
int length = s.length();
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
|
||||||
|
if (Character.isLetterOrDigit(c) || c == '-' || c == '_')
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendToStringBuilder(StringBuilder sb) {
|
||||||
|
if (hasFunkyChars(first) || first.isEmpty())
|
||||||
|
sb.append(ConfigImplUtil.renderJsonString(first));
|
||||||
|
else
|
||||||
|
sb.append(first);
|
||||||
|
if (remainder != null) {
|
||||||
|
sb.append(".");
|
||||||
|
remainder.appendToStringBuilder(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Path(");
|
||||||
|
appendToStringBuilder(sb);
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toString() is a debugging-oriented version while this is an
|
||||||
|
* error-message-oriented human-readable one.
|
||||||
|
*/
|
||||||
|
String render() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
appendToStringBuilder(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Path newKey(String key) {
|
||||||
|
return new Path(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Path newPath(String path) {
|
||||||
|
return PathParser.parsePath(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
final class PathBuilder {
|
||||||
|
// the keys are kept "backward" (top of stack is end of path)
|
||||||
|
final private Stack<String> keys;
|
||||||
|
private Path result;
|
||||||
|
|
||||||
|
PathBuilder() {
|
||||||
|
keys = new Stack<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCanAppend() {
|
||||||
|
if (result != null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"Adding to PathBuilder after getting result");
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendKey(String key) {
|
||||||
|
checkCanAppend();
|
||||||
|
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendPath(Path path) {
|
||||||
|
checkCanAppend();
|
||||||
|
|
||||||
|
String first = path.first();
|
||||||
|
Path remainder = path.remainder();
|
||||||
|
while (true) {
|
||||||
|
keys.push(first);
|
||||||
|
if (remainder != null) {
|
||||||
|
first = remainder.first();
|
||||||
|
remainder = remainder.remainder();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Path result() {
|
||||||
|
// note: if keys is empty, we want to return null, which is a valid
|
||||||
|
// empty path
|
||||||
|
if (result == null) {
|
||||||
|
Path remainder = null;
|
||||||
|
while (!keys.isEmpty()) {
|
||||||
|
String key = keys.pop();
|
||||||
|
remainder = new Path(key, remainder);
|
||||||
|
}
|
||||||
|
result = remainder;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2015 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
final class PathParser {
|
||||||
|
static class Element {
|
||||||
|
StringBuilder sb;
|
||||||
|
// an element can be empty if it has a quoted empty string "" in it
|
||||||
|
boolean canBeEmpty;
|
||||||
|
|
||||||
|
Element(String initial, boolean canBeEmpty) {
|
||||||
|
this.canBeEmpty = canBeEmpty;
|
||||||
|
this.sb = new StringBuilder(initial);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Element(" + sb.toString() + "," + canBeEmpty + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigOrigin apiOrigin = SimpleConfigOrigin.newSimple("path parameter");
|
||||||
|
|
||||||
|
static ConfigNodePath parsePathNode(String path) {
|
||||||
|
return parsePathNode(path, ConfigSyntax.CONF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigNodePath parsePathNode(String path, ConfigSyntax flavor) {
|
||||||
|
StringReader reader = new StringReader(path);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Iterator<Token> tokens = Tokenizer.tokenize(apiOrigin, reader,
|
||||||
|
flavor);
|
||||||
|
tokens.next(); // drop START
|
||||||
|
return parsePathNodeExpression(tokens, apiOrigin, path, flavor);
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Path parsePath(String path) {
|
||||||
|
Path speculated = speculativeFastParsePath(path);
|
||||||
|
if (speculated != null)
|
||||||
|
return speculated;
|
||||||
|
|
||||||
|
StringReader reader = new StringReader(path);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Iterator<Token> tokens = Tokenizer.tokenize(apiOrigin, reader,
|
||||||
|
ConfigSyntax.CONF);
|
||||||
|
tokens.next(); // drop START
|
||||||
|
return parsePathExpression(tokens, apiOrigin, path);
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Path parsePathExpression(Iterator<Token> expression,
|
||||||
|
ConfigOrigin origin) {
|
||||||
|
return parsePathExpression(expression, origin, null, null, ConfigSyntax.CONF);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Path parsePathExpression(Iterator<Token> expression,
|
||||||
|
ConfigOrigin origin, String originalText) {
|
||||||
|
return parsePathExpression(expression, origin, originalText, null, ConfigSyntax.CONF);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static ConfigNodePath parsePathNodeExpression(Iterator<Token> expression,
|
||||||
|
ConfigOrigin origin) {
|
||||||
|
return parsePathNodeExpression(expression, origin, null, ConfigSyntax.CONF);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static ConfigNodePath parsePathNodeExpression(Iterator<Token> expression,
|
||||||
|
ConfigOrigin origin, String originalText, ConfigSyntax flavor) {
|
||||||
|
ArrayList<Token> pathTokens = new ArrayList<Token>();
|
||||||
|
Path path = parsePathExpression(expression, origin, originalText, pathTokens, flavor);
|
||||||
|
return new ConfigNodePath(path, pathTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
// originalText may be null if not available
|
||||||
|
protected static Path parsePathExpression(Iterator<Token> expression,
|
||||||
|
ConfigOrigin origin, String originalText,
|
||||||
|
ArrayList<Token> pathTokens,
|
||||||
|
ConfigSyntax flavor) {
|
||||||
|
// each builder in "buf" is an element in the path.
|
||||||
|
List<Element> buf = new ArrayList<Element>();
|
||||||
|
buf.add(new Element("", false));
|
||||||
|
|
||||||
|
if (!expression.hasNext()) {
|
||||||
|
throw new ConfigException.BadPath(origin, originalText,
|
||||||
|
"Expecting a field name or path here, but got nothing");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (expression.hasNext()) {
|
||||||
|
Token t = expression.next();
|
||||||
|
|
||||||
|
if (pathTokens != null)
|
||||||
|
pathTokens.add(t);
|
||||||
|
|
||||||
|
// Ignore all IgnoredWhitespace tokens
|
||||||
|
if (Tokens.isIgnoredWhitespace(t))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Tokens.isValueWithType(t, ConfigValueType.STRING)) {
|
||||||
|
AbstractConfigValue v = Tokens.getValue(t);
|
||||||
|
// this is a quoted string; so any periods
|
||||||
|
// in here don't count as path separators
|
||||||
|
String s = v.transformToString();
|
||||||
|
|
||||||
|
addPathText(buf, true, s);
|
||||||
|
} else if (t == Tokens.END) {
|
||||||
|
// ignore this; when parsing a file, it should not happen
|
||||||
|
// since we're parsing a token list rather than the main
|
||||||
|
// token iterator, and when parsing a path expression from the
|
||||||
|
// API, it's expected to have an END.
|
||||||
|
} else {
|
||||||
|
// any periods outside of a quoted string count as
|
||||||
|
// separators
|
||||||
|
String text;
|
||||||
|
if (Tokens.isValue(t)) {
|
||||||
|
// appending a number here may add
|
||||||
|
// a period, but we _do_ count those as path
|
||||||
|
// separators, because we basically want
|
||||||
|
// "foo 3.0bar" to parse as a string even
|
||||||
|
// though there's a number in it. The fact that
|
||||||
|
// we tokenize non-string values is largely an
|
||||||
|
// implementation detail.
|
||||||
|
AbstractConfigValue v = Tokens.getValue(t);
|
||||||
|
|
||||||
|
// We need to split the tokens on a . so that we can get sub-paths but still preserve
|
||||||
|
// the original path text when doing an insertion
|
||||||
|
if (pathTokens != null) {
|
||||||
|
pathTokens.remove(pathTokens.size() - 1);
|
||||||
|
pathTokens.addAll(splitTokenOnPeriod(t, flavor));
|
||||||
|
}
|
||||||
|
text = v.transformToString();
|
||||||
|
} else if (Tokens.isUnquotedText(t)) {
|
||||||
|
// We need to split the tokens on a . so that we can get sub-paths but still preserve
|
||||||
|
// the original path text when doing an insertion on ConfigNodeObjects
|
||||||
|
if (pathTokens != null) {
|
||||||
|
pathTokens.remove(pathTokens.size() - 1);
|
||||||
|
pathTokens.addAll(splitTokenOnPeriod(t, flavor));
|
||||||
|
}
|
||||||
|
text = Tokens.getUnquotedText(t);
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BadPath(
|
||||||
|
origin,
|
||||||
|
originalText,
|
||||||
|
"Token not allowed in path expression: "
|
||||||
|
+ t
|
||||||
|
+ " (you can double-quote this token if you really want it here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
addPathText(buf, false, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathBuilder pb = new PathBuilder();
|
||||||
|
for (Element e : buf) {
|
||||||
|
if (e.sb.length() == 0 && !e.canBeEmpty) {
|
||||||
|
throw new ConfigException.BadPath(
|
||||||
|
origin,
|
||||||
|
originalText,
|
||||||
|
"path has a leading, trailing, or two adjacent period '.' (use quoted \"\" empty string if you want an empty element)");
|
||||||
|
} else {
|
||||||
|
pb.appendKey(e.sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<Token> splitTokenOnPeriod(Token t, ConfigSyntax flavor) {
|
||||||
|
String tokenText = t.tokenText();
|
||||||
|
if (tokenText.equals(".")) {
|
||||||
|
return Collections.singletonList(t);
|
||||||
|
}
|
||||||
|
String[] splitToken = tokenText.split("\\.");
|
||||||
|
ArrayList<Token> splitTokens = new ArrayList<Token>();
|
||||||
|
for (String s : splitToken) {
|
||||||
|
if (flavor == ConfigSyntax.CONF)
|
||||||
|
splitTokens.add(Tokens.newUnquotedText(t.origin(), s));
|
||||||
|
else
|
||||||
|
splitTokens.add(Tokens.newString(t.origin(), s, "\"" + s + "\""));
|
||||||
|
splitTokens.add(Tokens.newUnquotedText(t.origin(), "."));
|
||||||
|
}
|
||||||
|
if (tokenText.charAt(tokenText.length() - 1) != '.')
|
||||||
|
splitTokens.remove(splitTokens.size() - 1);
|
||||||
|
return splitTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addPathText(List<Element> buf, boolean wasQuoted,
|
||||||
|
String newText) {
|
||||||
|
int i = wasQuoted ? -1 : newText.indexOf('.');
|
||||||
|
Element current = buf.get(buf.size() - 1);
|
||||||
|
if (i < 0) {
|
||||||
|
// add to current path element
|
||||||
|
current.sb.append(newText);
|
||||||
|
// any empty quoted string means this element can
|
||||||
|
// now be empty.
|
||||||
|
if (wasQuoted && current.sb.length() == 0)
|
||||||
|
current.canBeEmpty = true;
|
||||||
|
} else {
|
||||||
|
// "buf" plus up to the period is an element
|
||||||
|
current.sb.append(newText.substring(0, i));
|
||||||
|
// then start a new element
|
||||||
|
buf.add(new Element("", false));
|
||||||
|
// recurse to consume remainder of newText
|
||||||
|
addPathText(buf, false, newText.substring(i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the idea is to see if the string has any chars or features
|
||||||
|
// that might require the full parser to deal with.
|
||||||
|
private static boolean looksUnsafeForFastParser(String s) {
|
||||||
|
boolean lastWasDot = true; // start of path is also a "dot"
|
||||||
|
int len = s.length();
|
||||||
|
if (s.isEmpty())
|
||||||
|
return true;
|
||||||
|
if (s.charAt(0) == '.')
|
||||||
|
return true;
|
||||||
|
if (s.charAt(len - 1) == '.')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') {
|
||||||
|
lastWasDot = false;
|
||||||
|
continue;
|
||||||
|
} else if (c == '.') {
|
||||||
|
if (lastWasDot)
|
||||||
|
return true; // ".." means we need to throw an error
|
||||||
|
lastWasDot = true;
|
||||||
|
} else if (c == '-') {
|
||||||
|
if (lastWasDot)
|
||||||
|
return true;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastWasDot)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path fastPathBuild(Path tail, String s, int end) {
|
||||||
|
// lastIndexOf takes last index it should look at, end - 1 not end
|
||||||
|
int splitAt = s.lastIndexOf('.', end - 1);
|
||||||
|
ArrayList<Token> tokens = new ArrayList<Token>();
|
||||||
|
tokens.add(Tokens.newUnquotedText(null, s));
|
||||||
|
// this works even if splitAt is -1; then we start the substring at 0
|
||||||
|
Path withOneMoreElement = new Path(s.substring(splitAt + 1, end), tail);
|
||||||
|
if (splitAt < 0) {
|
||||||
|
return withOneMoreElement;
|
||||||
|
} else {
|
||||||
|
return fastPathBuild(withOneMoreElement, s, splitAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do something much faster than the full parser if
|
||||||
|
// we just have something like "foo" or "foo.bar"
|
||||||
|
private static Path speculativeFastParsePath(String path) {
|
||||||
|
String s = ConfigImplUtil.unicodeTrim(path);
|
||||||
|
if (looksUnsafeForFastParser(s))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return fastPathBuild(null, s, s.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
+210
@@ -0,0 +1,210 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
|
||||||
|
final class PropertiesParser {
|
||||||
|
static AbstractConfigObject parse(Reader reader,
|
||||||
|
ConfigOrigin origin) throws IOException {
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.load(reader);
|
||||||
|
return fromProperties(origin, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String lastElement(String path) {
|
||||||
|
int i = path.lastIndexOf('.');
|
||||||
|
if (i < 0)
|
||||||
|
return path;
|
||||||
|
else
|
||||||
|
return path.substring(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String exceptLastElement(String path) {
|
||||||
|
int i = path.lastIndexOf('.');
|
||||||
|
if (i < 0)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return path.substring(0, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Path pathFromPropertyKey(String key) {
|
||||||
|
String last = lastElement(key);
|
||||||
|
String exceptLast = exceptLastElement(key);
|
||||||
|
Path path = new Path(last, null);
|
||||||
|
while (exceptLast != null) {
|
||||||
|
last = lastElement(exceptLast);
|
||||||
|
exceptLast = exceptLastElement(exceptLast);
|
||||||
|
path = new Path(last, path);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject fromProperties(ConfigOrigin origin,
|
||||||
|
Properties props) {
|
||||||
|
return fromEntrySet(origin, props.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <K, V> AbstractConfigObject fromEntrySet(ConfigOrigin origin, Set<Map.Entry<K, V>> entries) {
|
||||||
|
final Map<Path, Object> pathMap = getPathMap(entries);
|
||||||
|
return fromPathMap(origin, pathMap, true /* from properties */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <K, V> Map<Path, Object> getPathMap(Set<Map.Entry<K, V>> entries) {
|
||||||
|
Map<Path, Object> pathMap = new HashMap<Path, Object>();
|
||||||
|
for (Map.Entry<K, V> entry : entries) {
|
||||||
|
Object key = entry.getKey();
|
||||||
|
if (key instanceof String) {
|
||||||
|
Path path = pathFromPropertyKey((String) key);
|
||||||
|
pathMap.put(path, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pathMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject fromStringMap(ConfigOrigin origin, Map<String, String> stringMap) {
|
||||||
|
return fromEntrySet(origin, stringMap.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject fromPathMap(ConfigOrigin origin,
|
||||||
|
Map<?, ?> pathExpressionMap) {
|
||||||
|
Map<Path, Object> pathMap = new HashMap<Path, Object>();
|
||||||
|
for (Map.Entry<?, ?> entry : pathExpressionMap.entrySet()) {
|
||||||
|
Object keyObj = entry.getKey();
|
||||||
|
if (!(keyObj instanceof String)) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"Map has a non-string as a key, expecting a path expression as a String");
|
||||||
|
}
|
||||||
|
Path path = Path.newPath((String) keyObj);
|
||||||
|
pathMap.put(path, entry.getValue());
|
||||||
|
}
|
||||||
|
return fromPathMap(origin, pathMap, false /* from properties */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigObject fromPathMap(ConfigOrigin origin,
|
||||||
|
Map<Path, Object> pathMap, boolean convertedFromProperties) {
|
||||||
|
/*
|
||||||
|
* First, build a list of paths that will have values, either string or
|
||||||
|
* object values.
|
||||||
|
*/
|
||||||
|
Set<Path> scopePaths = new HashSet<Path>();
|
||||||
|
Set<Path> valuePaths = new HashSet<Path>();
|
||||||
|
for (Path path : pathMap.keySet()) {
|
||||||
|
// add value's path
|
||||||
|
valuePaths.add(path);
|
||||||
|
|
||||||
|
// all parent paths are objects
|
||||||
|
Path next = path.parent();
|
||||||
|
while (next != null) {
|
||||||
|
scopePaths.add(next);
|
||||||
|
next = next.parent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convertedFromProperties) {
|
||||||
|
/*
|
||||||
|
* If any string values are also objects containing other values,
|
||||||
|
* drop those string values - objects "win".
|
||||||
|
*/
|
||||||
|
valuePaths.removeAll(scopePaths);
|
||||||
|
} else {
|
||||||
|
/* If we didn't start out as properties, then this is an error. */
|
||||||
|
for (Path path : valuePaths) {
|
||||||
|
if (scopePaths.contains(path)) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"In the map, path '"
|
||||||
|
+ path.render()
|
||||||
|
+ "' occurs as both the parent object of a value and as a value. "
|
||||||
|
+ "Because Map has no defined ordering, this is a broken situation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create maps for the object-valued values.
|
||||||
|
*/
|
||||||
|
Map<String, AbstractConfigValue> root = new HashMap<String, AbstractConfigValue>();
|
||||||
|
Map<Path, Map<String, AbstractConfigValue>> scopes = new HashMap<Path, Map<String, AbstractConfigValue>>();
|
||||||
|
|
||||||
|
for (Path path : scopePaths) {
|
||||||
|
Map<String, AbstractConfigValue> scope = new HashMap<String, AbstractConfigValue>();
|
||||||
|
scopes.put(path, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store string values in the associated scope maps */
|
||||||
|
for (Path path : valuePaths) {
|
||||||
|
Path parentPath = path.parent();
|
||||||
|
Map<String, AbstractConfigValue> parent = parentPath != null ? scopes
|
||||||
|
.get(parentPath) : root;
|
||||||
|
|
||||||
|
String last = path.last();
|
||||||
|
Object rawValue = pathMap.get(path);
|
||||||
|
AbstractConfigValue value;
|
||||||
|
if (convertedFromProperties) {
|
||||||
|
if (rawValue instanceof String) {
|
||||||
|
value = new ConfigString.Quoted(origin, (String) rawValue);
|
||||||
|
} else {
|
||||||
|
// silently ignore non-string values in Properties
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = ConfigImpl.fromAnyRef(pathMap.get(path), origin,
|
||||||
|
FromMapMode.KEYS_ARE_PATHS);
|
||||||
|
}
|
||||||
|
if (value != null)
|
||||||
|
parent.put(last, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a list of scope paths from longest to shortest, so children go
|
||||||
|
* before parents.
|
||||||
|
*/
|
||||||
|
List<Path> sortedScopePaths = new ArrayList<Path>();
|
||||||
|
sortedScopePaths.addAll(scopePaths);
|
||||||
|
// sort descending by length
|
||||||
|
Collections.sort(sortedScopePaths, new Comparator<Path>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Path a, Path b) {
|
||||||
|
// Path.length() is O(n) so in theory this sucks
|
||||||
|
// but in practice we can make Path precompute length
|
||||||
|
// if it ever matters.
|
||||||
|
return b.length() - a.length();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create ConfigObject for each scope map, working from children to
|
||||||
|
* parents to avoid modifying any already-created ConfigObject. This is
|
||||||
|
* where we need the sorted list.
|
||||||
|
*/
|
||||||
|
for (Path scopePath : sortedScopePaths) {
|
||||||
|
Map<String, AbstractConfigValue> scope = scopes.get(scopePath);
|
||||||
|
|
||||||
|
Path parentPath = scopePath.parent();
|
||||||
|
Map<String, AbstractConfigValue> parent = parentPath != null ? scopes
|
||||||
|
.get(parentPath) : root;
|
||||||
|
|
||||||
|
AbstractConfigObject o = new SimpleConfigObject(origin, scope,
|
||||||
|
ResolveStatus.RESOLVED, false /* ignoresFallbacks */);
|
||||||
|
parent.put(scopePath.last(), o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return root config object
|
||||||
|
return new SimpleConfigObject(origin, root, ResolveStatus.RESOLVED,
|
||||||
|
false /* ignoresFallbacks */);
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implemented by a merge stack (ConfigDelayedMerge, ConfigDelayedMergeObject)
|
||||||
|
* that replaces itself during substitution resolution in order to implement
|
||||||
|
* "look backwards only" semantics.
|
||||||
|
*/
|
||||||
|
interface ReplaceableMergeStack extends Container {
|
||||||
|
/**
|
||||||
|
* Make a replacement for this object skipping the given number of elements
|
||||||
|
* which are lower in merge priority.
|
||||||
|
*/
|
||||||
|
AbstractConfigValue makeReplacement(ResolveContext context, int skipping);
|
||||||
|
}
|
||||||
+237
@@ -0,0 +1,237 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigResolveOptions;
|
||||||
|
|
||||||
|
final class ResolveContext {
|
||||||
|
final private ResolveMemos memos;
|
||||||
|
|
||||||
|
final private ConfigResolveOptions options;
|
||||||
|
// the current path restriction, used to ensure lazy
|
||||||
|
// resolution and avoid gratuitous cycles. without this,
|
||||||
|
// any sibling of an object we're traversing could
|
||||||
|
// cause a cycle "by side effect"
|
||||||
|
// CAN BE NULL for a full resolve.
|
||||||
|
final private Path restrictToChild;
|
||||||
|
|
||||||
|
// This is used for tracing and debugging and nice error messages;
|
||||||
|
// contains every node as we call resolve on it.
|
||||||
|
final private List<AbstractConfigValue> resolveStack;
|
||||||
|
|
||||||
|
final private Set<AbstractConfigValue> cycleMarkers;
|
||||||
|
|
||||||
|
ResolveContext(ResolveMemos memos, ConfigResolveOptions options, Path restrictToChild,
|
||||||
|
List<AbstractConfigValue> resolveStack, Set<AbstractConfigValue> cycleMarkers) {
|
||||||
|
this.memos = memos;
|
||||||
|
this.options = options;
|
||||||
|
this.restrictToChild = restrictToChild;
|
||||||
|
this.resolveStack = Collections.unmodifiableList(resolveStack);
|
||||||
|
this.cycleMarkers = Collections.unmodifiableSet(cycleMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<AbstractConfigValue> newCycleMarkers() {
|
||||||
|
return Collections.newSetFromMap(new IdentityHashMap<AbstractConfigValue, Boolean>());
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveContext(ConfigResolveOptions options, Path restrictToChild) {
|
||||||
|
// LinkedHashSet keeps the traversal order which is at least useful
|
||||||
|
// in error messages if nothing else
|
||||||
|
this(new ResolveMemos(), options, restrictToChild, new ArrayList<AbstractConfigValue>(), newCycleMarkers());
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "ResolveContext restrict to child " + restrictToChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveContext addCycleMarker(AbstractConfigValue value) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "++ Cycle marker " + value + "@" + System.identityHashCode(value));
|
||||||
|
if (cycleMarkers.contains(value))
|
||||||
|
throw new ConfigException.BugOrBroken("Added cycle marker twice " + value);
|
||||||
|
Set<AbstractConfigValue> copy = newCycleMarkers();
|
||||||
|
copy.addAll(cycleMarkers);
|
||||||
|
copy.add(value);
|
||||||
|
return new ResolveContext(memos, options, restrictToChild, resolveStack, copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveContext removeCycleMarker(AbstractConfigValue value) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "-- Cycle marker " + value + "@" + System.identityHashCode(value));
|
||||||
|
|
||||||
|
Set<AbstractConfigValue> copy = newCycleMarkers();
|
||||||
|
copy.addAll(cycleMarkers);
|
||||||
|
copy.remove(value);
|
||||||
|
return new ResolveContext(memos, options, restrictToChild, resolveStack, copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResolveContext memoize(MemoKey key, AbstractConfigValue value) {
|
||||||
|
ResolveMemos changed = memos.put(key, value);
|
||||||
|
return new ResolveContext(changed, options, restrictToChild, resolveStack, cycleMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigResolveOptions options() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isRestrictedToChild() {
|
||||||
|
return restrictToChild != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path restrictToChild() {
|
||||||
|
return restrictToChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
// restrictTo may be null to unrestrict
|
||||||
|
ResolveContext restrict(Path restrictTo) {
|
||||||
|
if (restrictTo == restrictToChild)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ResolveContext(memos, options, restrictTo, resolveStack, cycleMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveContext unrestricted() {
|
||||||
|
return restrict(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
String traceString() {
|
||||||
|
String separator = ", ";
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (AbstractConfigValue value : resolveStack) {
|
||||||
|
if (value instanceof ConfigReference) {
|
||||||
|
sb.append(((ConfigReference) value).expression().toString());
|
||||||
|
sb.append(separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sb.length() > 0)
|
||||||
|
sb.setLength(sb.length() - separator.length());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResolveContext pushTrace(AbstractConfigValue value) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "pushing trace " + value);
|
||||||
|
List<AbstractConfigValue> copy = new ArrayList<AbstractConfigValue>(resolveStack);
|
||||||
|
copy.add(value);
|
||||||
|
return new ResolveContext(memos, options, restrictToChild, copy, cycleMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveContext popTrace() {
|
||||||
|
List<AbstractConfigValue> copy = new ArrayList<AbstractConfigValue>(resolveStack);
|
||||||
|
AbstractConfigValue old = copy.remove(resolveStack.size() - 1);
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth() - 1, "popped trace " + old);
|
||||||
|
return new ResolveContext(memos, options, restrictToChild, copy, cycleMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
int depth() {
|
||||||
|
if (resolveStack.size() > 30)
|
||||||
|
throw new ConfigException.BugOrBroken("resolve getting too deep");
|
||||||
|
return resolveStack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveResult<? extends AbstractConfigValue> resolve(AbstractConfigValue original, ResolveSource source)
|
||||||
|
throws AbstractConfigValue.NotPossibleToResolve {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl
|
||||||
|
.trace(depth(), "resolving " + original + " restrictToChild=" + restrictToChild + " in " + source);
|
||||||
|
return pushTrace(original).realResolve(original, source).popTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResolveResult<? extends AbstractConfigValue> realResolve(AbstractConfigValue original, ResolveSource source)
|
||||||
|
throws AbstractConfigValue.NotPossibleToResolve {
|
||||||
|
// a fully-resolved (no restrictToChild) object can satisfy a
|
||||||
|
// request for a restricted object, so always check that first.
|
||||||
|
final MemoKey fullKey = new MemoKey(original, null);
|
||||||
|
MemoKey restrictedKey = null;
|
||||||
|
|
||||||
|
AbstractConfigValue cached = memos.get(fullKey);
|
||||||
|
|
||||||
|
// but if there was no fully-resolved object cached, we'll only
|
||||||
|
// compute the restrictToChild object so use a more limited
|
||||||
|
// memo key
|
||||||
|
if (cached == null && isRestrictedToChild()) {
|
||||||
|
restrictedKey = new MemoKey(original, restrictToChild());
|
||||||
|
cached = memos.get(restrictedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cached != null) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "using cached resolution " + cached + " for " + original
|
||||||
|
+ " restrictToChild " + restrictToChild());
|
||||||
|
return ResolveResult.make(this, cached);
|
||||||
|
} else {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(),
|
||||||
|
"not found in cache, resolving " + original + "@" + System.identityHashCode(original));
|
||||||
|
|
||||||
|
if (cycleMarkers.contains(original)) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(),
|
||||||
|
"Cycle detected, can't resolve; " + original + "@" + System.identityHashCode(original));
|
||||||
|
throw new AbstractConfigValue.NotPossibleToResolve(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveResult<? extends AbstractConfigValue> result = original.resolveSubstitutions(this, source);
|
||||||
|
AbstractConfigValue resolved = result.value;
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "resolved to " + resolved + "@" + System.identityHashCode(resolved)
|
||||||
|
+ " from " + original + "@" + System.identityHashCode(resolved));
|
||||||
|
|
||||||
|
ResolveContext withMemo = result.context;
|
||||||
|
|
||||||
|
if (resolved == null || resolved.resolveStatus() == ResolveStatus.RESOLVED) {
|
||||||
|
// if the resolved object is fully resolved by resolving
|
||||||
|
// only the restrictToChildOrNull, then it can be cached
|
||||||
|
// under fullKey since the child we were restricted to
|
||||||
|
// turned out to be the only unresolved thing.
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved);
|
||||||
|
|
||||||
|
withMemo = withMemo.memoize(fullKey, resolved);
|
||||||
|
} else {
|
||||||
|
// if we have an unresolved object then either we did a
|
||||||
|
// partial resolve restricted to a certain child, or we are
|
||||||
|
// allowing incomplete resolution, or it's a bug.
|
||||||
|
if (isRestrictedToChild()) {
|
||||||
|
if (restrictedKey == null) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"restrictedKey should not be null here");
|
||||||
|
}
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "caching " + restrictedKey + " result " + resolved);
|
||||||
|
|
||||||
|
withMemo = withMemo.memoize(restrictedKey, resolved);
|
||||||
|
} else if (options().getAllowUnresolved()) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved);
|
||||||
|
|
||||||
|
withMemo = withMemo.memoize(fullKey, resolved);
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"resolveSubstitutions() did not give us a resolved object");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveResult.make(withMemo, resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigValue resolve(AbstractConfigValue value, AbstractConfigObject root,
|
||||||
|
ConfigResolveOptions options) {
|
||||||
|
ResolveSource source = new ResolveSource(root);
|
||||||
|
ResolveContext context = new ResolveContext(options, null /* restrictToChild */);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return context.resolve(value, source).value;
|
||||||
|
} catch (AbstractConfigValue.NotPossibleToResolve e) {
|
||||||
|
// ConfigReference was supposed to catch NotPossibleToResolve
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"NotPossibleToResolve was thrown from an outermost resolve", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exists because we have to memoize resolved substitutions as we go
|
||||||
|
* through the config tree; otherwise we could end up creating multiple copies
|
||||||
|
* of values or whole trees of values as we follow chains of substitutions.
|
||||||
|
*/
|
||||||
|
final class ResolveMemos {
|
||||||
|
// note that we can resolve things to undefined (represented as Java null,
|
||||||
|
// rather than ConfigNull) so this map can have null values.
|
||||||
|
final private Map<MemoKey, AbstractConfigValue> memos;
|
||||||
|
|
||||||
|
private ResolveMemos(Map<MemoKey, AbstractConfigValue> memos) {
|
||||||
|
this.memos = memos;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveMemos() {
|
||||||
|
this(new HashMap<MemoKey, AbstractConfigValue>());
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractConfigValue get(MemoKey key) {
|
||||||
|
return memos.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveMemos put(MemoKey key, AbstractConfigValue value) {
|
||||||
|
// completely inefficient, but so far nobody cares about resolve()
|
||||||
|
// performance, we can clean it up someday...
|
||||||
|
Map<MemoKey, AbstractConfigValue> copy = new HashMap<MemoKey, AbstractConfigValue>(memos);
|
||||||
|
copy.put(key, value);
|
||||||
|
return new ResolveMemos(copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
// value is allowed to be null
|
||||||
|
final class ResolveResult<V extends AbstractConfigValue> {
|
||||||
|
public final ResolveContext context;
|
||||||
|
public final V value;
|
||||||
|
|
||||||
|
private ResolveResult(ResolveContext context, V value) {
|
||||||
|
this.context = context;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V extends AbstractConfigValue> ResolveResult<V> make(ResolveContext context, V value) {
|
||||||
|
return new ResolveResult<V>(context, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// better option? we don't have variance
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ResolveResult<AbstractConfigObject> asObjectResult() {
|
||||||
|
if (!(value instanceof AbstractConfigObject))
|
||||||
|
throw new ConfigException.BugOrBroken("Expecting a resolve result to be an object, but it was " + value);
|
||||||
|
Object o = this;
|
||||||
|
return (ResolveResult<AbstractConfigObject>) o;
|
||||||
|
}
|
||||||
|
|
||||||
|
// better option? we don't have variance
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ResolveResult<AbstractConfigValue> asValueResult() {
|
||||||
|
Object o = this;
|
||||||
|
return (ResolveResult<AbstractConfigValue>) o;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveResult<V> popTrace() {
|
||||||
|
return make(context.popTrace(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ResolveResult(" + value + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
+349
@@ -0,0 +1,349 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is the source for values for a substitution like ${foo}.
|
||||||
|
*/
|
||||||
|
final class ResolveSource {
|
||||||
|
|
||||||
|
final AbstractConfigObject root;
|
||||||
|
// This is used for knowing the chain of parents we used to get here.
|
||||||
|
// null if we should assume we are not a descendant of the root.
|
||||||
|
// the root itself should be a node in this if non-null.
|
||||||
|
final Node<Container> pathFromRoot;
|
||||||
|
|
||||||
|
ResolveSource(AbstractConfigObject root, Node<Container> pathFromRoot) {
|
||||||
|
this.root = root;
|
||||||
|
this.pathFromRoot = pathFromRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveSource(AbstractConfigObject root) {
|
||||||
|
this.root = root;
|
||||||
|
this.pathFromRoot = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we replace the root with a non-object, use an empty
|
||||||
|
// object with nothing in it instead.
|
||||||
|
private AbstractConfigObject rootMustBeObj(Container value) {
|
||||||
|
if (value instanceof AbstractConfigObject) {
|
||||||
|
return (AbstractConfigObject) value;
|
||||||
|
} else {
|
||||||
|
return SimpleConfigObject.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// as a side effect, findInObject() will have to resolve all parents of the
|
||||||
|
// child being peeked, but NOT the child itself. Caller has to resolve
|
||||||
|
// the child itself if needed. ValueWithPath.value can be null but
|
||||||
|
// the ValueWithPath instance itself should not be.
|
||||||
|
static private ResultWithPath findInObject(AbstractConfigObject obj, ResolveContext context, Path path)
|
||||||
|
throws AbstractConfigValue.NotPossibleToResolve {
|
||||||
|
// resolve ONLY portions of the object which are along our path
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace("*** finding '" + path + "' in " + obj);
|
||||||
|
Path restriction = context.restrictToChild();
|
||||||
|
ResolveResult<? extends AbstractConfigValue> partiallyResolved = context.restrict(path).resolve(obj,
|
||||||
|
new ResolveSource(obj));
|
||||||
|
ResolveContext newContext = partiallyResolved.context.restrict(restriction);
|
||||||
|
if (partiallyResolved.value instanceof AbstractConfigObject) {
|
||||||
|
ValueWithPath pair = findInObject((AbstractConfigObject) partiallyResolved.value, path);
|
||||||
|
return new ResultWithPath(ResolveResult.make(newContext, pair.value), pair.pathFromRoot);
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("resolved object to non-object " + obj + " to " + partiallyResolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private ValueWithPath findInObject(AbstractConfigObject obj, Path path) {
|
||||||
|
try {
|
||||||
|
// we'll fail if anything along the path can't
|
||||||
|
// be looked at without resolving.
|
||||||
|
return findInObject(obj, path, null);
|
||||||
|
} catch (ConfigException.NotResolved e) {
|
||||||
|
throw ConfigImpl.improveNotResolved(path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private ValueWithPath findInObject(AbstractConfigObject obj, Path path, Node<Container> parents) {
|
||||||
|
String key = path.first();
|
||||||
|
Path next = path.remainder();
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace("*** looking up '" + key + "' in " + obj);
|
||||||
|
AbstractConfigValue v = obj.attemptPeekWithPartialResolve(key);
|
||||||
|
Node<Container> newParents = parents == null ? new Node<Container>(obj) : parents.prepend(obj);
|
||||||
|
|
||||||
|
if (next == null) {
|
||||||
|
return new ValueWithPath(v, newParents);
|
||||||
|
} else {
|
||||||
|
if (v instanceof AbstractConfigObject) {
|
||||||
|
return findInObject((AbstractConfigObject) v, next, newParents);
|
||||||
|
} else {
|
||||||
|
return new ValueWithPath(null, newParents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultWithPath lookupSubst(ResolveContext context, SubstitutionExpression subst,
|
||||||
|
int prefixLength)
|
||||||
|
throws AbstractConfigValue.NotPossibleToResolve {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(context.depth(), "searching for " + subst);
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(context.depth(), subst + " - looking up relative to file it occurred in");
|
||||||
|
// First we look up the full path, which means relative to the
|
||||||
|
// included file if we were not a root file
|
||||||
|
ResultWithPath result = findInObject(root, context, subst.path());
|
||||||
|
|
||||||
|
if (result.result.value == null) {
|
||||||
|
// Then we want to check relative to the root file. We don't
|
||||||
|
// want the prefix we were included at to be used when looking
|
||||||
|
// up env variables either.
|
||||||
|
Path unprefixed = subst.path().subPath(prefixLength);
|
||||||
|
|
||||||
|
if (prefixLength > 0) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(result.result.context.depth(), unprefixed
|
||||||
|
+ " - looking up relative to parent file");
|
||||||
|
result = findInObject(root, result.result.context, unprefixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.result.value == null && result.result.context.options().getUseSystemEnvironment()) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(result.result.context.depth(), unprefixed + " - looking up in system environment");
|
||||||
|
result = findInObject(ConfigImpl.envVariablesAsConfigObject(), context, unprefixed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace(result.result.context.depth(), "resolved to " + result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveSource pushParent(Container parent) {
|
||||||
|
if (parent == null)
|
||||||
|
throw new ConfigException.BugOrBroken("can't push null parent");
|
||||||
|
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace("pushing parent " + parent + " ==root " + (parent == root) + " onto " + this);
|
||||||
|
|
||||||
|
if (pathFromRoot == null) {
|
||||||
|
if (parent == root) {
|
||||||
|
return new ResolveSource(root, new Node<Container>(parent));
|
||||||
|
} else {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||||
|
// this hasDescendant check is super-expensive so it's a
|
||||||
|
// trace message rather than an assertion
|
||||||
|
if (root.hasDescendant((AbstractConfigValue) parent))
|
||||||
|
ConfigImpl.trace("***** BUG ***** tried to push parent " + parent
|
||||||
|
+ " without having a path to it in " + this);
|
||||||
|
}
|
||||||
|
// ignore parents if we aren't proceeding from the
|
||||||
|
// root
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Container parentParent = pathFromRoot.head();
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||||
|
// this hasDescendant check is super-expensive so it's a
|
||||||
|
// trace message rather than an assertion
|
||||||
|
if (parentParent != null && !parentParent.hasDescendant((AbstractConfigValue) parent))
|
||||||
|
ConfigImpl.trace("***** BUG ***** trying to push non-child of " + parentParent + ", non-child was "
|
||||||
|
+ parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResolveSource(root, pathFromRoot.prepend(parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveSource resetParents() {
|
||||||
|
if (pathFromRoot == null)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new ResolveSource(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns null if the replacement results in deleting all the nodes.
|
||||||
|
private static Node<Container> replace(Node<Container> list, Container old, AbstractConfigValue replacement) {
|
||||||
|
Container child = list.head();
|
||||||
|
if (child != old)
|
||||||
|
throw new ConfigException.BugOrBroken("Can only replace() the top node we're resolving; had " + child
|
||||||
|
+ " on top and tried to replace " + old + " overall list was " + list);
|
||||||
|
Container parent = list.tail() == null ? null : list.tail().head();
|
||||||
|
if (replacement == null || !(replacement instanceof Container)) {
|
||||||
|
if (parent == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* we are deleting the child from the stack of containers
|
||||||
|
* because it's either going away or not a container
|
||||||
|
*/
|
||||||
|
AbstractConfigValue newParent = parent.replaceChild((AbstractConfigValue) old, null);
|
||||||
|
|
||||||
|
return replace(list.tail(), parent, newParent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* we replaced the container with another container */
|
||||||
|
if (parent == null) {
|
||||||
|
return new Node<Container>((Container) replacement);
|
||||||
|
} else {
|
||||||
|
AbstractConfigValue newParent = parent.replaceChild((AbstractConfigValue) old, replacement);
|
||||||
|
Node<Container> newTail = replace(list.tail(), parent, newParent);
|
||||||
|
if (newTail != null)
|
||||||
|
return newTail.prepend((Container) replacement);
|
||||||
|
else
|
||||||
|
return new Node<Container>((Container) replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveSource replaceCurrentParent(Container old, Container replacement) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace("replaceCurrentParent old " + old + "@" + System.identityHashCode(old) + " replacement "
|
||||||
|
+ replacement + "@" + System.identityHashCode(old) + " in " + this);
|
||||||
|
if (old == replacement) {
|
||||||
|
return this;
|
||||||
|
} else if (pathFromRoot != null) {
|
||||||
|
Node<Container> newPath = replace(pathFromRoot, old, (AbstractConfigValue) replacement);
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||||
|
ConfigImpl.trace("replaced " + old + " with " + replacement + " in " + this);
|
||||||
|
ConfigImpl.trace("path was: " + pathFromRoot + " is now " + newPath);
|
||||||
|
}
|
||||||
|
// if we end up nuking the root object itself, we replace it with an
|
||||||
|
// empty root
|
||||||
|
if (newPath != null)
|
||||||
|
return new ResolveSource((AbstractConfigObject) newPath.last(), newPath);
|
||||||
|
else
|
||||||
|
return new ResolveSource(SimpleConfigObject.empty());
|
||||||
|
} else {
|
||||||
|
if (old == root) {
|
||||||
|
return new ResolveSource(rootMustBeObj(replacement));
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("attempt to replace root " + root + " with " + replacement);
|
||||||
|
// return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replacement may be null to delete
|
||||||
|
ResolveSource replaceWithinCurrentParent(AbstractConfigValue old, AbstractConfigValue replacement) {
|
||||||
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
ConfigImpl.trace("replaceWithinCurrentParent old " + old + "@" + System.identityHashCode(old)
|
||||||
|
+ " replacement " + replacement + "@" + System.identityHashCode(old) + " in " + this);
|
||||||
|
if (old == replacement) {
|
||||||
|
return this;
|
||||||
|
} else if (pathFromRoot != null) {
|
||||||
|
Container parent = pathFromRoot.head();
|
||||||
|
AbstractConfigValue newParent = parent.replaceChild(old, replacement);
|
||||||
|
return replaceCurrentParent(parent, (newParent instanceof Container) ? (Container) newParent : null);
|
||||||
|
} else {
|
||||||
|
if (old == root && replacement instanceof Container) {
|
||||||
|
return new ResolveSource(rootMustBeObj((Container) replacement));
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("replace in parent not possible " + old + " with " + replacement
|
||||||
|
+ " in " + this);
|
||||||
|
// return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ResolveSource(root=" + root + ", pathFromRoot=" + pathFromRoot + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
// a persistent list
|
||||||
|
static final class Node<T> {
|
||||||
|
final T value;
|
||||||
|
final Node<T> next;
|
||||||
|
|
||||||
|
Node(T value, Node<T> next) {
|
||||||
|
this.value = value;
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node(T value) {
|
||||||
|
this(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> prepend(T value) {
|
||||||
|
return new Node<T>(value, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
T head() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> tail() {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
T last() {
|
||||||
|
Node<T> i = this;
|
||||||
|
while (i.next != null)
|
||||||
|
i = i.next;
|
||||||
|
return i.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> reverse() {
|
||||||
|
if (next == null) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
Node<T> reversed = new Node<T>(value);
|
||||||
|
Node<T> i = next;
|
||||||
|
while (i != null) {
|
||||||
|
reversed = reversed.prepend(i.value);
|
||||||
|
i = i.next;
|
||||||
|
}
|
||||||
|
return reversed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append("[");
|
||||||
|
Node<T> toAppendValue = this.reverse();
|
||||||
|
while (toAppendValue != null) {
|
||||||
|
sb.append(toAppendValue.value.toString());
|
||||||
|
if (toAppendValue.next != null)
|
||||||
|
sb.append(" <= ");
|
||||||
|
toAppendValue = toAppendValue.next;
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// value is allowed to be null
|
||||||
|
static final class ValueWithPath {
|
||||||
|
final AbstractConfigValue value;
|
||||||
|
final Node<Container> pathFromRoot;
|
||||||
|
|
||||||
|
ValueWithPath(AbstractConfigValue value, Node<Container> pathFromRoot) {
|
||||||
|
this.value = value;
|
||||||
|
this.pathFromRoot = pathFromRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ValueWithPath(value=" + value + ", pathFromRoot=" + pathFromRoot + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class ResultWithPath {
|
||||||
|
final ResolveResult<? extends AbstractConfigValue> result;
|
||||||
|
final Node<Container> pathFromRoot;
|
||||||
|
|
||||||
|
ResultWithPath(ResolveResult<? extends AbstractConfigValue> result, Node<Container> pathFromRoot) {
|
||||||
|
this.result = result;
|
||||||
|
this.pathFromRoot = pathFromRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ResultWithPath(result=" + result + ", pathFromRoot=" + pathFromRoot + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status of substitution resolution.
|
||||||
|
*/
|
||||||
|
enum ResolveStatus {
|
||||||
|
UNRESOLVED, RESOLVED;
|
||||||
|
|
||||||
|
final static ResolveStatus fromValues(
|
||||||
|
Collection<? extends AbstractConfigValue> values) {
|
||||||
|
for (AbstractConfigValue v : values) {
|
||||||
|
if (v.resolveStatus() == ResolveStatus.UNRESOLVED)
|
||||||
|
return ResolveStatus.UNRESOLVED;
|
||||||
|
}
|
||||||
|
return ResolveStatus.RESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
final static ResolveStatus fromBoolean(boolean resolved) {
|
||||||
|
return resolved ? ResolveStatus.RESOLVED : ResolveStatus.UNRESOLVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
+534
@@ -0,0 +1,534 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.Externalizable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.NotSerializableException;
|
||||||
|
import java.io.ObjectInput;
|
||||||
|
import java.io.ObjectOutput;
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.Config;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigList;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliberately shoving all the serialization code into this class instead of
|
||||||
|
* doing it OO-style with each subclass. Seems better to have it all in one
|
||||||
|
* place. This class implements a lame serialization format that supports
|
||||||
|
* skipping unknown fields, so it's moderately more extensible than the default
|
||||||
|
* Java serialization format.
|
||||||
|
*/
|
||||||
|
class SerializedConfigValue extends AbstractConfigValue implements Externalizable {
|
||||||
|
|
||||||
|
// this is the version used by Java serialization, if it increments it's
|
||||||
|
// essentially an ABI break and bad
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// this is how we try to be extensible
|
||||||
|
static enum SerializedField {
|
||||||
|
// represents a field code we didn't recognize
|
||||||
|
UNKNOWN,
|
||||||
|
|
||||||
|
// end of a list of fields
|
||||||
|
END_MARKER,
|
||||||
|
|
||||||
|
// Fields at the root
|
||||||
|
ROOT_VALUE,
|
||||||
|
ROOT_WAS_CONFIG,
|
||||||
|
|
||||||
|
// Fields that make up a value
|
||||||
|
VALUE_DATA,
|
||||||
|
VALUE_ORIGIN,
|
||||||
|
|
||||||
|
// Fields that make up an origin
|
||||||
|
ORIGIN_DESCRIPTION,
|
||||||
|
ORIGIN_LINE_NUMBER,
|
||||||
|
ORIGIN_END_LINE_NUMBER,
|
||||||
|
ORIGIN_TYPE,
|
||||||
|
ORIGIN_URL,
|
||||||
|
ORIGIN_COMMENTS,
|
||||||
|
ORIGIN_NULL_URL,
|
||||||
|
ORIGIN_NULL_COMMENTS,
|
||||||
|
ORIGIN_RESOURCE,
|
||||||
|
ORIGIN_NULL_RESOURCE;
|
||||||
|
|
||||||
|
static SerializedField forInt(int b) {
|
||||||
|
if (b < values().length)
|
||||||
|
return values()[b];
|
||||||
|
else
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static enum SerializedValueType {
|
||||||
|
// the ordinals here are in the wire format, caution
|
||||||
|
NULL(ConfigValueType.NULL),
|
||||||
|
BOOLEAN(ConfigValueType.BOOLEAN),
|
||||||
|
INT(ConfigValueType.NUMBER),
|
||||||
|
LONG(ConfigValueType.NUMBER),
|
||||||
|
DOUBLE(ConfigValueType.NUMBER),
|
||||||
|
STRING(ConfigValueType.STRING),
|
||||||
|
LIST(ConfigValueType.LIST),
|
||||||
|
OBJECT(ConfigValueType.OBJECT);
|
||||||
|
|
||||||
|
ConfigValueType configType;
|
||||||
|
|
||||||
|
SerializedValueType(ConfigValueType configType) {
|
||||||
|
this.configType = configType;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SerializedValueType forInt(int b) {
|
||||||
|
if (b < values().length)
|
||||||
|
return values()[b];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SerializedValueType forValue(ConfigValue value) {
|
||||||
|
ConfigValueType t = value.valueType();
|
||||||
|
if (t == ConfigValueType.NUMBER) {
|
||||||
|
if (value instanceof ConfigInt)
|
||||||
|
return INT;
|
||||||
|
else if (value instanceof ConfigLong)
|
||||||
|
return LONG;
|
||||||
|
else if (value instanceof ConfigDouble)
|
||||||
|
return DOUBLE;
|
||||||
|
} else {
|
||||||
|
for (SerializedValueType st : values()) {
|
||||||
|
if (st.configType == t)
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ConfigException.BugOrBroken("don't know how to serialize " + value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private ConfigValue value;
|
||||||
|
private boolean wasConfig;
|
||||||
|
|
||||||
|
// this has to be public for the Java deserializer
|
||||||
|
public SerializedConfigValue() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedConfigValue(ConfigValue value) {
|
||||||
|
this();
|
||||||
|
this.value = value;
|
||||||
|
this.wasConfig = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedConfigValue(Config conf) {
|
||||||
|
this(conf.root());
|
||||||
|
this.wasConfig = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when Java deserializer reads this object, return the contained
|
||||||
|
// object instead.
|
||||||
|
private Object readResolve() throws ObjectStreamException {
|
||||||
|
if (wasConfig)
|
||||||
|
return ((ConfigObject) value).toConfig();
|
||||||
|
else
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldOut {
|
||||||
|
final SerializedField code;
|
||||||
|
final ByteArrayOutputStream bytes;
|
||||||
|
final DataOutput data;
|
||||||
|
|
||||||
|
FieldOut(SerializedField code) {
|
||||||
|
this.code = code;
|
||||||
|
this.bytes = new ByteArrayOutputStream();
|
||||||
|
this.data = new DataOutputStream(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a separate function to prevent bugs writing to the
|
||||||
|
// outer stream instead of field.data
|
||||||
|
private static void writeOriginField(DataOutput out, SerializedField code, Object v)
|
||||||
|
throws IOException {
|
||||||
|
switch (code) {
|
||||||
|
case ORIGIN_DESCRIPTION:
|
||||||
|
out.writeUTF((String) v);
|
||||||
|
break;
|
||||||
|
case ORIGIN_LINE_NUMBER:
|
||||||
|
out.writeInt((Integer) v);
|
||||||
|
break;
|
||||||
|
case ORIGIN_END_LINE_NUMBER:
|
||||||
|
out.writeInt((Integer) v);
|
||||||
|
break;
|
||||||
|
case ORIGIN_TYPE:
|
||||||
|
out.writeByte((Integer) v);
|
||||||
|
break;
|
||||||
|
case ORIGIN_URL:
|
||||||
|
out.writeUTF((String) v);
|
||||||
|
break;
|
||||||
|
case ORIGIN_RESOURCE:
|
||||||
|
out.writeUTF((String) v);
|
||||||
|
break;
|
||||||
|
case ORIGIN_COMMENTS:
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<String> list = (List<String>) v;
|
||||||
|
int size = list.size();
|
||||||
|
out.writeInt(size);
|
||||||
|
for (String s : list) {
|
||||||
|
out.writeUTF(s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ORIGIN_NULL_URL: // FALL THRU
|
||||||
|
case ORIGIN_NULL_RESOURCE: // FALL THRU
|
||||||
|
case ORIGIN_NULL_COMMENTS:
|
||||||
|
// nothing to write out besides code and length
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IOException("Unhandled field from origin: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not private because we use it to serialize ConfigException
|
||||||
|
static void writeOrigin(DataOutput out, SimpleConfigOrigin origin,
|
||||||
|
SimpleConfigOrigin baseOrigin) throws IOException {
|
||||||
|
Map<SerializedField, Object> m;
|
||||||
|
// to serialize a null origin, we write out no fields at all
|
||||||
|
if (origin != null)
|
||||||
|
m = origin.toFieldsDelta(baseOrigin);
|
||||||
|
else
|
||||||
|
m = Collections.emptyMap();
|
||||||
|
for (Map.Entry<SerializedField, Object> e : m.entrySet()) {
|
||||||
|
FieldOut field = new FieldOut(e.getKey());
|
||||||
|
Object v = e.getValue();
|
||||||
|
writeOriginField(field.data, field.code, v);
|
||||||
|
writeField(out, field);
|
||||||
|
}
|
||||||
|
writeEndMarker(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// not private because we use it to deserialize ConfigException
|
||||||
|
static SimpleConfigOrigin readOrigin(DataInput in, SimpleConfigOrigin baseOrigin)
|
||||||
|
throws IOException {
|
||||||
|
Map<SerializedField, Object> m = new EnumMap<SerializedField, Object>(SerializedField.class);
|
||||||
|
while (true) {
|
||||||
|
Object v = null;
|
||||||
|
SerializedField field = readCode(in);
|
||||||
|
switch (field) {
|
||||||
|
case END_MARKER:
|
||||||
|
return SimpleConfigOrigin.fromBase(baseOrigin, m);
|
||||||
|
case ORIGIN_DESCRIPTION:
|
||||||
|
in.readInt(); // discard length
|
||||||
|
v = in.readUTF();
|
||||||
|
break;
|
||||||
|
case ORIGIN_LINE_NUMBER:
|
||||||
|
in.readInt(); // discard length
|
||||||
|
v = in.readInt();
|
||||||
|
break;
|
||||||
|
case ORIGIN_END_LINE_NUMBER:
|
||||||
|
in.readInt(); // discard length
|
||||||
|
v = in.readInt();
|
||||||
|
break;
|
||||||
|
case ORIGIN_TYPE:
|
||||||
|
in.readInt(); // discard length
|
||||||
|
v = in.readUnsignedByte();
|
||||||
|
break;
|
||||||
|
case ORIGIN_URL:
|
||||||
|
in.readInt(); // discard length
|
||||||
|
v = in.readUTF();
|
||||||
|
break;
|
||||||
|
case ORIGIN_RESOURCE:
|
||||||
|
in.readInt(); // discard length
|
||||||
|
v = in.readUTF();
|
||||||
|
break;
|
||||||
|
case ORIGIN_COMMENTS:
|
||||||
|
in.readInt(); // discard length
|
||||||
|
int size = in.readInt();
|
||||||
|
List<String> list = new ArrayList<String>(size);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
list.add(in.readUTF());
|
||||||
|
}
|
||||||
|
v = list;
|
||||||
|
break;
|
||||||
|
case ORIGIN_NULL_URL: // FALL THRU
|
||||||
|
case ORIGIN_NULL_RESOURCE: // FALL THRU
|
||||||
|
case ORIGIN_NULL_COMMENTS:
|
||||||
|
// nothing to read besides code and length
|
||||||
|
in.readInt(); // discard length
|
||||||
|
v = ""; // just something non-null to put in the map
|
||||||
|
break;
|
||||||
|
case ROOT_VALUE:
|
||||||
|
case ROOT_WAS_CONFIG:
|
||||||
|
case VALUE_DATA:
|
||||||
|
case VALUE_ORIGIN:
|
||||||
|
throw new IOException("Not expecting this field here: " + field);
|
||||||
|
case UNKNOWN:
|
||||||
|
// skip unknown field
|
||||||
|
skipField(in);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (v != null)
|
||||||
|
m.put(field, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeValueData(DataOutput out, ConfigValue value) throws IOException {
|
||||||
|
SerializedValueType st = SerializedValueType.forValue(value);
|
||||||
|
out.writeByte(st.ordinal());
|
||||||
|
switch (st) {
|
||||||
|
case BOOLEAN:
|
||||||
|
out.writeBoolean(((ConfigBoolean) value).unwrapped());
|
||||||
|
break;
|
||||||
|
case NULL:
|
||||||
|
break;
|
||||||
|
case INT:
|
||||||
|
// saving numbers as both string and binary is redundant but easy
|
||||||
|
out.writeInt(((ConfigInt) value).unwrapped());
|
||||||
|
out.writeUTF(((ConfigNumber) value).transformToString());
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
out.writeLong(((ConfigLong) value).unwrapped());
|
||||||
|
out.writeUTF(((ConfigNumber) value).transformToString());
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
out.writeDouble(((ConfigDouble) value).unwrapped());
|
||||||
|
out.writeUTF(((ConfigNumber) value).transformToString());
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
out.writeUTF(((ConfigString) value).unwrapped());
|
||||||
|
break;
|
||||||
|
case LIST:
|
||||||
|
ConfigList list = (ConfigList) value;
|
||||||
|
out.writeInt(list.size());
|
||||||
|
for (ConfigValue v : list) {
|
||||||
|
writeValue(out, v, (SimpleConfigOrigin) list.origin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
ConfigObject obj = (ConfigObject) value;
|
||||||
|
out.writeInt(obj.size());
|
||||||
|
for (Map.Entry<String, ConfigValue> e : obj.entrySet()) {
|
||||||
|
out.writeUTF(e.getKey());
|
||||||
|
writeValue(out, e.getValue(), (SimpleConfigOrigin) obj.origin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigValue readValueData(DataInput in, SimpleConfigOrigin origin)
|
||||||
|
throws IOException {
|
||||||
|
int stb = in.readUnsignedByte();
|
||||||
|
SerializedValueType st = SerializedValueType.forInt(stb);
|
||||||
|
if (st == null)
|
||||||
|
throw new IOException("Unknown serialized value type: " + stb);
|
||||||
|
switch (st) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return new ConfigBoolean(origin, in.readBoolean());
|
||||||
|
case NULL:
|
||||||
|
return new ConfigNull(origin);
|
||||||
|
case INT:
|
||||||
|
int vi = in.readInt();
|
||||||
|
String si = in.readUTF();
|
||||||
|
return new ConfigInt(origin, vi, si);
|
||||||
|
case LONG:
|
||||||
|
long vl = in.readLong();
|
||||||
|
String sl = in.readUTF();
|
||||||
|
return new ConfigLong(origin, vl, sl);
|
||||||
|
case DOUBLE:
|
||||||
|
double vd = in.readDouble();
|
||||||
|
String sd = in.readUTF();
|
||||||
|
return new ConfigDouble(origin, vd, sd);
|
||||||
|
case STRING:
|
||||||
|
return new ConfigString.Quoted(origin, in.readUTF());
|
||||||
|
case LIST:
|
||||||
|
int listSize = in.readInt();
|
||||||
|
List<AbstractConfigValue> list = new ArrayList<AbstractConfigValue>(listSize);
|
||||||
|
for (int i = 0; i < listSize; ++i) {
|
||||||
|
AbstractConfigValue v = readValue(in, origin);
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
return new SimpleConfigList(origin, list);
|
||||||
|
case OBJECT:
|
||||||
|
int mapSize = in.readInt();
|
||||||
|
Map<String, AbstractConfigValue> map = new HashMap<String, AbstractConfigValue>(mapSize);
|
||||||
|
for (int i = 0; i < mapSize; ++i) {
|
||||||
|
String key = in.readUTF();
|
||||||
|
AbstractConfigValue v = readValue(in, origin);
|
||||||
|
map.put(key, v);
|
||||||
|
}
|
||||||
|
return new SimpleConfigObject(origin, map);
|
||||||
|
}
|
||||||
|
throw new IOException("Unhandled serialized value type: " + st);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeValue(DataOutput out, ConfigValue value, SimpleConfigOrigin baseOrigin)
|
||||||
|
throws IOException {
|
||||||
|
FieldOut origin = new FieldOut(SerializedField.VALUE_ORIGIN);
|
||||||
|
writeOrigin(origin.data, (SimpleConfigOrigin) value.origin(),
|
||||||
|
baseOrigin);
|
||||||
|
writeField(out, origin);
|
||||||
|
|
||||||
|
FieldOut data = new FieldOut(SerializedField.VALUE_DATA);
|
||||||
|
writeValueData(data.data, value);
|
||||||
|
writeField(out, data);
|
||||||
|
|
||||||
|
writeEndMarker(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigValue readValue(DataInput in, SimpleConfigOrigin baseOrigin)
|
||||||
|
throws IOException {
|
||||||
|
AbstractConfigValue value = null;
|
||||||
|
SimpleConfigOrigin origin = null;
|
||||||
|
while (true) {
|
||||||
|
SerializedField code = readCode(in);
|
||||||
|
if (code == SerializedField.END_MARKER) {
|
||||||
|
if (value == null)
|
||||||
|
throw new IOException("No value data found in serialization of value");
|
||||||
|
return value;
|
||||||
|
} else if (code == SerializedField.VALUE_DATA) {
|
||||||
|
if (origin == null)
|
||||||
|
throw new IOException("Origin must be stored before value data");
|
||||||
|
in.readInt(); // discard length
|
||||||
|
value = readValueData(in, origin);
|
||||||
|
} else if (code == SerializedField.VALUE_ORIGIN) {
|
||||||
|
in.readInt(); // discard length
|
||||||
|
origin = readOrigin(in, baseOrigin);
|
||||||
|
} else {
|
||||||
|
// ignore unknown field
|
||||||
|
skipField(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeField(DataOutput out, FieldOut field) throws IOException {
|
||||||
|
byte[] bytes = field.bytes.toByteArray();
|
||||||
|
out.writeByte(field.code.ordinal());
|
||||||
|
out.writeInt(bytes.length);
|
||||||
|
out.write(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeEndMarker(DataOutput out) throws IOException {
|
||||||
|
out.writeByte(SerializedField.END_MARKER.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SerializedField readCode(DataInput in) throws IOException {
|
||||||
|
int c = in.readUnsignedByte();
|
||||||
|
if (c == SerializedField.UNKNOWN.ordinal())
|
||||||
|
throw new IOException("field code " + c + " is not supposed to be on the wire");
|
||||||
|
return SerializedField.forInt(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void skipField(DataInput in) throws IOException {
|
||||||
|
int len = in.readInt();
|
||||||
|
// skipBytes doesn't have to block
|
||||||
|
int skipped = in.skipBytes(len);
|
||||||
|
if (skipped < len) {
|
||||||
|
// wastefully use readFully() if skipBytes didn't work
|
||||||
|
byte[] bytes = new byte[(len - skipped)];
|
||||||
|
in.readFully(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeExternal(ObjectOutput out) throws IOException {
|
||||||
|
if (((AbstractConfigValue) value).resolveStatus() != ResolveStatus.RESOLVED)
|
||||||
|
throw new NotSerializableException(
|
||||||
|
"tried to serialize a value with unresolved substitutions, need to Config#resolve() first, see API docs");
|
||||||
|
FieldOut field = new FieldOut(SerializedField.ROOT_VALUE);
|
||||||
|
writeValue(field.data, value, null /* baseOrigin */);
|
||||||
|
writeField(out, field);
|
||||||
|
|
||||||
|
field = new FieldOut(SerializedField.ROOT_WAS_CONFIG);
|
||||||
|
field.data.writeBoolean(wasConfig);
|
||||||
|
writeField(out, field);
|
||||||
|
|
||||||
|
writeEndMarker(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||||
|
while (true) {
|
||||||
|
SerializedField code = readCode(in);
|
||||||
|
if (code == SerializedField.END_MARKER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataInput input = fieldIn(in);
|
||||||
|
if (code == SerializedField.ROOT_VALUE) {
|
||||||
|
this.value = readValue(input, null /* baseOrigin */);
|
||||||
|
} else if (code == SerializedField.ROOT_WAS_CONFIG) {
|
||||||
|
this.wasConfig = input.readBoolean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataInput fieldIn(ObjectInput in) throws IOException {
|
||||||
|
byte[] bytes = new byte[in.readInt()];
|
||||||
|
in.readFully(bytes);
|
||||||
|
return new DataInputStream(new ByteArrayInputStream(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfigException shouldNotBeUsed() {
|
||||||
|
return new ConfigException.BugOrBroken(SerializedConfigValue.class.getName()
|
||||||
|
+ " should not exist outside of serialization");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
throw shouldNotBeUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object unwrapped() {
|
||||||
|
throw shouldNotBeUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SerializedConfigValue newCopy(ConfigOrigin origin) {
|
||||||
|
throw shouldNotBeUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return getClass().getSimpleName() + "(value=" + value + ",wasConfig=" + wasConfig + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// there's no reason we will ever call this equals(), but
|
||||||
|
// the one in AbstractConfigValue would explode due to
|
||||||
|
// calling unwrapped() above, so we just give some
|
||||||
|
// safe-to-call implementation to avoid breaking the
|
||||||
|
// contract of java.lang.Object
|
||||||
|
if (other instanceof SerializedConfigValue) {
|
||||||
|
return canEqual(other)
|
||||||
|
&& (this.wasConfig == ((SerializedConfigValue) other).wasConfig)
|
||||||
|
&& (this.value.equals(((SerializedConfigValue) other).value));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = 41 * (41 + value.hashCode());
|
||||||
|
h = 41 * (h + (wasConfig ? 1 : 0));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1168
File diff suppressed because it is too large
Load Diff
+66
@@ -0,0 +1,66 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.parser.ConfigDocument;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
final class SimpleConfigDocument implements ConfigDocument {
|
||||||
|
private ConfigNodeRoot configNodeTree;
|
||||||
|
private ConfigParseOptions parseOptions;
|
||||||
|
|
||||||
|
SimpleConfigDocument(ConfigNodeRoot parsedNode, ConfigParseOptions parseOptions) {
|
||||||
|
configNodeTree = parsedNode;
|
||||||
|
this.parseOptions = parseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigDocument withValueText(String path, String newValue) {
|
||||||
|
if (newValue == null)
|
||||||
|
throw new ConfigException.BugOrBroken("null value for " + path + " passed to withValueText");
|
||||||
|
SimpleConfigOrigin origin = SimpleConfigOrigin.newSimple("single value parsing");
|
||||||
|
StringReader reader = new StringReader(newValue);
|
||||||
|
Iterator<Token> tokens = Tokenizer.tokenize(origin, reader, parseOptions.getSyntax());
|
||||||
|
AbstractConfigNodeValue parsedValue = ConfigDocumentParser.parseValue(tokens, origin, parseOptions);
|
||||||
|
reader.close();
|
||||||
|
|
||||||
|
return new SimpleConfigDocument(configNodeTree.setValue(path, parsedValue, parseOptions.getSyntax()), parseOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigDocument withValue(String path, ConfigValue newValue) {
|
||||||
|
if (newValue == null)
|
||||||
|
throw new ConfigException.BugOrBroken("null value for " + path + " passed to withValue");
|
||||||
|
ConfigRenderOptions options = ConfigRenderOptions.defaults();
|
||||||
|
options = options.setOriginComments(false);
|
||||||
|
return withValueText(path, newValue.render(options).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigDocument withoutPath(String path) {
|
||||||
|
return new SimpleConfigDocument(configNodeTree.setValue(path, null, parseOptions.getSyntax()), parseOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPath(String path) {
|
||||||
|
return configNodeTree.hasValue(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String render() {
|
||||||
|
return configNodeTree.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return other instanceof ConfigDocument && render().equals(((ConfigDocument) other).render());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return render().hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
+464
@@ -0,0 +1,464 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigList;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
final class SimpleConfigList extends AbstractConfigValue implements ConfigList, Container, Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
final private List<AbstractConfigValue> value;
|
||||||
|
final private boolean resolved;
|
||||||
|
|
||||||
|
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value) {
|
||||||
|
this(origin, value, ResolveStatus
|
||||||
|
.fromValues(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value,
|
||||||
|
ResolveStatus status) {
|
||||||
|
super(origin);
|
||||||
|
this.value = value;
|
||||||
|
this.resolved = status == ResolveStatus.RESOLVED;
|
||||||
|
|
||||||
|
// kind of an expensive debug check (makes this constructor pointless)
|
||||||
|
if (status != ResolveStatus.fromValues(value))
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"SimpleConfigList created with wrong resolve status: " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValueType valueType() {
|
||||||
|
return ConfigValueType.LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Object> unwrapped() {
|
||||||
|
List<Object> list = new ArrayList<Object>();
|
||||||
|
for (AbstractConfigValue v : value) {
|
||||||
|
list.add(v.unwrapped());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveStatus resolveStatus() {
|
||||||
|
return ResolveStatus.fromBoolean(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigList replaceChild(AbstractConfigValue child, AbstractConfigValue replacement) {
|
||||||
|
List<AbstractConfigValue> newList = replaceChildInList(value, child, replacement);
|
||||||
|
if (newList == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
// we use the constructor flavor that will recompute the resolve
|
||||||
|
// status
|
||||||
|
return new SimpleConfigList(origin(), newList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDescendant(AbstractConfigValue descendant) {
|
||||||
|
return hasDescendantInList(value, descendant);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleConfigList modify(NoExceptionsModifier modifier, ResolveStatus newResolveStatus) {
|
||||||
|
try {
|
||||||
|
return modifyMayThrow(modifier, newResolveStatus);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConfigException.BugOrBroken("unexpected checked exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleConfigList modifyMayThrow(Modifier modifier, ResolveStatus newResolveStatus)
|
||||||
|
throws Exception {
|
||||||
|
// lazy-create for optimization
|
||||||
|
List<AbstractConfigValue> changed = null;
|
||||||
|
int i = 0;
|
||||||
|
for (AbstractConfigValue v : value) {
|
||||||
|
AbstractConfigValue modified = modifier.modifyChildMayThrow(null /* key */, v);
|
||||||
|
|
||||||
|
// lazy-create the new list if required
|
||||||
|
if (changed == null && modified != v) {
|
||||||
|
changed = new ArrayList<AbstractConfigValue>();
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
changed.add(value.get(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// once the new list is created, all elements
|
||||||
|
// have to go in it. if modifyChild returned
|
||||||
|
// null, we drop that element.
|
||||||
|
if (changed != null && modified != null) {
|
||||||
|
changed.add(modified);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed != null) {
|
||||||
|
if (newResolveStatus != null) {
|
||||||
|
return new SimpleConfigList(origin(), changed, newResolveStatus);
|
||||||
|
} else {
|
||||||
|
return new SimpleConfigList(origin(), changed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ResolveModifier implements Modifier {
|
||||||
|
ResolveContext context;
|
||||||
|
final ResolveSource source;
|
||||||
|
ResolveModifier(ResolveContext context, ResolveSource source) {
|
||||||
|
this.context = context;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue modifyChildMayThrow(String key, AbstractConfigValue v)
|
||||||
|
throws NotPossibleToResolve {
|
||||||
|
ResolveResult<? extends AbstractConfigValue> result = context.resolve(v, source);
|
||||||
|
context = result.context;
|
||||||
|
return result.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveResult<? extends SimpleConfigList> resolveSubstitutions(ResolveContext context, ResolveSource source)
|
||||||
|
throws NotPossibleToResolve {
|
||||||
|
if (resolved)
|
||||||
|
return ResolveResult.make(context, this);
|
||||||
|
|
||||||
|
if (context.isRestrictedToChild()) {
|
||||||
|
// if a list restricts to a child path, then it has no child paths,
|
||||||
|
// so nothing to do.
|
||||||
|
return ResolveResult.make(context, this);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
ResolveModifier modifier = new ResolveModifier(context, source.pushParent(this));
|
||||||
|
SimpleConfigList value = modifyMayThrow(modifier, context.options().getAllowUnresolved() ? null : ResolveStatus.RESOLVED);
|
||||||
|
return ResolveResult.make(modifier.context, value);
|
||||||
|
} catch (NotPossibleToResolve e) {
|
||||||
|
throw e;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConfigException.BugOrBroken("unexpected checked exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SimpleConfigList relativized(final Path prefix) {
|
||||||
|
return modify(new NoExceptionsModifier() {
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue modifyChild(String key, AbstractConfigValue v) {
|
||||||
|
return v.relativized(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, resolveStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof SimpleConfigList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
if (other instanceof SimpleConfigList) {
|
||||||
|
// optimization to avoid unwrapped() for two ConfigList
|
||||||
|
return canEqual(other)
|
||||||
|
&& (value == ((SimpleConfigList) other).value || value.equals(((SimpleConfigList) other).value));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
return value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
sb.append("[]");
|
||||||
|
} else {
|
||||||
|
sb.append("[");
|
||||||
|
if (options.getFormatted())
|
||||||
|
sb.append('\n');
|
||||||
|
for (AbstractConfigValue v : value) {
|
||||||
|
if (options.getOriginComments()) {
|
||||||
|
String[] lines = v.origin().description().split("\n");
|
||||||
|
for (String l : lines) {
|
||||||
|
indent(sb, indent + 1, options);
|
||||||
|
sb.append('#');
|
||||||
|
if (!l.isEmpty())
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append(l);
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options.getComments()) {
|
||||||
|
for (String comment : v.origin().comments()) {
|
||||||
|
indent(sb, indent + 1, options);
|
||||||
|
sb.append("# ");
|
||||||
|
sb.append(comment);
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent(sb, indent + 1, options);
|
||||||
|
|
||||||
|
v.render(sb, indent + 1, atRoot, options);
|
||||||
|
sb.append(",");
|
||||||
|
if (options.getFormatted())
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
sb.setLength(sb.length() - 1); // chop or newline
|
||||||
|
if (options.getFormatted()) {
|
||||||
|
sb.setLength(sb.length() - 1); // also chop comma
|
||||||
|
sb.append('\n');
|
||||||
|
indent(sb, indent, options);
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return value.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> c) {
|
||||||
|
return value.containsAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue get(int index) {
|
||||||
|
return value.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
return value.indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return value.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ConfigValue> iterator() {
|
||||||
|
final Iterator<AbstractConfigValue> i = value.iterator();
|
||||||
|
|
||||||
|
return new Iterator<ConfigValue>() {
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return i.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue next() {
|
||||||
|
return i.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw weAreImmutable("iterator().remove");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
return value.lastIndexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ListIterator<ConfigValue> wrapListIterator(
|
||||||
|
final ListIterator<AbstractConfigValue> i) {
|
||||||
|
return new ListIterator<ConfigValue>() {
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return i.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue next() {
|
||||||
|
return i.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw weAreImmutable("listIterator().remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(ConfigValue arg0) {
|
||||||
|
throw weAreImmutable("listIterator().add");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPrevious() {
|
||||||
|
return i.hasPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int nextIndex() {
|
||||||
|
return i.nextIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue previous() {
|
||||||
|
return i.previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int previousIndex() {
|
||||||
|
return i.previousIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(ConfigValue arg0) {
|
||||||
|
throw weAreImmutable("listIterator().set");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator<ConfigValue> listIterator() {
|
||||||
|
return wrapListIterator(value.listIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator<ConfigValue> listIterator(int index) {
|
||||||
|
return wrapListIterator(value.listIterator(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigValue> subList(int fromIndex, int toIndex) {
|
||||||
|
List<ConfigValue> list = new ArrayList<ConfigValue>();
|
||||||
|
// yay bloat caused by lack of type variance
|
||||||
|
for (AbstractConfigValue v : value.subList(fromIndex, toIndex)) {
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return value.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(T[] a) {
|
||||||
|
return value.toArray(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UnsupportedOperationException weAreImmutable(String method) {
|
||||||
|
return new UnsupportedOperationException(
|
||||||
|
"ConfigList is immutable, you can't call List.'" + method + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(ConfigValue e) {
|
||||||
|
throw weAreImmutable("add");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, ConfigValue element) {
|
||||||
|
throw weAreImmutable("add");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends ConfigValue> c) {
|
||||||
|
throw weAreImmutable("addAll");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int index, Collection<? extends ConfigValue> c) {
|
||||||
|
throw weAreImmutable("addAll");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw weAreImmutable("clear");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
throw weAreImmutable("remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue remove(int index) {
|
||||||
|
throw weAreImmutable("remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
throw weAreImmutable("removeAll");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
throw weAreImmutable("retainAll");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigValue set(int index, ConfigValue element) {
|
||||||
|
throw weAreImmutable("set");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SimpleConfigList newCopy(ConfigOrigin newOrigin) {
|
||||||
|
return new SimpleConfigList(newOrigin, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final SimpleConfigList concatenate(SimpleConfigList other) {
|
||||||
|
ConfigOrigin combinedOrigin = SimpleConfigOrigin.mergeOrigins(origin(), other.origin());
|
||||||
|
List<AbstractConfigValue> combined = new ArrayList<AbstractConfigValue>(value.size()
|
||||||
|
+ other.value.size());
|
||||||
|
combined.addAll(value);
|
||||||
|
combined.addAll(other.value);
|
||||||
|
return new SimpleConfigList(combinedOrigin, combined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigList withOrigin(ConfigOrigin origin) {
|
||||||
|
return (SimpleConfigList) super.withOrigin(origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
+671
@@ -0,0 +1,671 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValue;
|
||||||
|
|
||||||
|
final class SimpleConfigObject extends AbstractConfigObject implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2L;
|
||||||
|
|
||||||
|
// this map should never be modified - assume immutable
|
||||||
|
final private Map<String, AbstractConfigValue> value;
|
||||||
|
final private boolean resolved;
|
||||||
|
final private boolean ignoresFallbacks;
|
||||||
|
|
||||||
|
SimpleConfigObject(ConfigOrigin origin,
|
||||||
|
Map<String, AbstractConfigValue> value, ResolveStatus status,
|
||||||
|
boolean ignoresFallbacks) {
|
||||||
|
super(origin);
|
||||||
|
if (value == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"creating config object with null map");
|
||||||
|
this.value = value;
|
||||||
|
this.resolved = status == ResolveStatus.RESOLVED;
|
||||||
|
this.ignoresFallbacks = ignoresFallbacks;
|
||||||
|
|
||||||
|
// Kind of an expensive debug check. Comment out?
|
||||||
|
if (status != ResolveStatus.fromValues(value.values()))
|
||||||
|
throw new ConfigException.BugOrBroken("Wrong resolved status on " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigObject(ConfigOrigin origin,
|
||||||
|
Map<String, AbstractConfigValue> value) {
|
||||||
|
this(origin, value, ResolveStatus.fromValues(value.values()), false /* ignoresFallbacks */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigObject withOnlyKey(String key) {
|
||||||
|
return withOnlyPath(Path.newKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigObject withoutKey(String key) {
|
||||||
|
return withoutPath(Path.newKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the object with only the path if the path
|
||||||
|
// exists, otherwise null if it doesn't. this ensures
|
||||||
|
// that if we have { a : { b : 42 } } and do
|
||||||
|
// withOnlyPath("a.b.c") that we don't keep an empty
|
||||||
|
// "a" object.
|
||||||
|
@Override
|
||||||
|
protected SimpleConfigObject withOnlyPathOrNull(Path path) {
|
||||||
|
String key = path.first();
|
||||||
|
Path next = path.remainder();
|
||||||
|
AbstractConfigValue v = value.get(key);
|
||||||
|
|
||||||
|
if (next != null) {
|
||||||
|
if (v != null && (v instanceof AbstractConfigObject)) {
|
||||||
|
v = ((AbstractConfigObject) v).withOnlyPathOrNull(next);
|
||||||
|
} else {
|
||||||
|
// if the path has more elements but we don't have an object,
|
||||||
|
// then the rest of the path does not exist.
|
||||||
|
v = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new SimpleConfigObject(origin(), Collections.singletonMap(key, v),
|
||||||
|
v.resolveStatus(), ignoresFallbacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SimpleConfigObject withOnlyPath(Path path) {
|
||||||
|
SimpleConfigObject o = withOnlyPathOrNull(path);
|
||||||
|
if (o == null) {
|
||||||
|
return new SimpleConfigObject(origin(),
|
||||||
|
Collections.<String, AbstractConfigValue> emptyMap(), ResolveStatus.RESOLVED,
|
||||||
|
ignoresFallbacks);
|
||||||
|
} else {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SimpleConfigObject withoutPath(Path path) {
|
||||||
|
String key = path.first();
|
||||||
|
Path next = path.remainder();
|
||||||
|
AbstractConfigValue v = value.get(key);
|
||||||
|
|
||||||
|
if (v != null && next != null && v instanceof AbstractConfigObject) {
|
||||||
|
v = ((AbstractConfigObject) v).withoutPath(next);
|
||||||
|
Map<String, AbstractConfigValue> updated = new HashMap<String, AbstractConfigValue>(
|
||||||
|
value);
|
||||||
|
updated.put(key, v);
|
||||||
|
return new SimpleConfigObject(origin(), updated, ResolveStatus.fromValues(updated
|
||||||
|
.values()), ignoresFallbacks);
|
||||||
|
} else if (next != null || v == null) {
|
||||||
|
// can't descend, nothing to remove
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
Map<String, AbstractConfigValue> smaller = new HashMap<String, AbstractConfigValue>(
|
||||||
|
value.size() - 1);
|
||||||
|
for (Map.Entry<String, AbstractConfigValue> old : value.entrySet()) {
|
||||||
|
if (!old.getKey().equals(key))
|
||||||
|
smaller.put(old.getKey(), old.getValue());
|
||||||
|
}
|
||||||
|
return new SimpleConfigObject(origin(), smaller, ResolveStatus.fromValues(smaller
|
||||||
|
.values()), ignoresFallbacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigObject withValue(String key, ConfigValue v) {
|
||||||
|
if (v == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"Trying to store null ConfigValue in a ConfigObject");
|
||||||
|
|
||||||
|
Map<String, AbstractConfigValue> newMap;
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
newMap = Collections.singletonMap(key, (AbstractConfigValue) v);
|
||||||
|
} else {
|
||||||
|
newMap = new HashMap<String, AbstractConfigValue>(value);
|
||||||
|
newMap.put(key, (AbstractConfigValue) v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleConfigObject(origin(), newMap, ResolveStatus.fromValues(newMap.values()),
|
||||||
|
ignoresFallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SimpleConfigObject withValue(Path path, ConfigValue v) {
|
||||||
|
String key = path.first();
|
||||||
|
Path next = path.remainder();
|
||||||
|
|
||||||
|
if (next == null) {
|
||||||
|
return withValue(key, v);
|
||||||
|
} else {
|
||||||
|
AbstractConfigValue child = value.get(key);
|
||||||
|
if (child != null && child instanceof AbstractConfigObject) {
|
||||||
|
// if we have an object, add to it
|
||||||
|
return withValue(key, ((AbstractConfigObject) child).withValue(next, v));
|
||||||
|
} else {
|
||||||
|
// as soon as we have a non-object, replace it entirely
|
||||||
|
SimpleConfig subtree = ((AbstractConfigValue) v).atPath(
|
||||||
|
SimpleConfigOrigin.newSimple("withValue(" + next.render() + ")"), next);
|
||||||
|
return withValue(key, subtree.root());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigValue attemptPeekWithPartialResolve(String key) {
|
||||||
|
return value.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleConfigObject newCopy(ResolveStatus newStatus, ConfigOrigin newOrigin,
|
||||||
|
boolean newIgnoresFallbacks) {
|
||||||
|
return new SimpleConfigObject(newOrigin, value, newStatus, newIgnoresFallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SimpleConfigObject newCopy(ResolveStatus newStatus, ConfigOrigin newOrigin) {
|
||||||
|
return newCopy(newStatus, newOrigin, ignoresFallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SimpleConfigObject withFallbacksIgnored() {
|
||||||
|
if (ignoresFallbacks)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return newCopy(resolveStatus(), origin(), true /* ignoresFallbacks */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveStatus resolveStatus() {
|
||||||
|
return ResolveStatus.fromBoolean(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigObject replaceChild(AbstractConfigValue child, AbstractConfigValue replacement) {
|
||||||
|
HashMap<String, AbstractConfigValue> newChildren = new HashMap<String, AbstractConfigValue>(value);
|
||||||
|
for (Map.Entry<String, AbstractConfigValue> old : newChildren.entrySet()) {
|
||||||
|
if (old.getValue() == child) {
|
||||||
|
if (replacement != null)
|
||||||
|
old.setValue(replacement);
|
||||||
|
else
|
||||||
|
newChildren.remove(old.getKey());
|
||||||
|
|
||||||
|
return new SimpleConfigObject(origin(), newChildren, ResolveStatus.fromValues(newChildren.values()),
|
||||||
|
ignoresFallbacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ConfigException.BugOrBroken("SimpleConfigObject.replaceChild did not find " + child + " in " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDescendant(AbstractConfigValue descendant) {
|
||||||
|
for (AbstractConfigValue child : value.values()) {
|
||||||
|
if (child == descendant)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// now do the expensive search
|
||||||
|
for (AbstractConfigValue child : value.values()) {
|
||||||
|
if (child instanceof Container && ((Container) child).hasDescendant(descendant))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean ignoresFallbacks() {
|
||||||
|
return ignoresFallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> unwrapped() {
|
||||||
|
Map<String, Object> m = new HashMap<String, Object>();
|
||||||
|
for (Map.Entry<String, AbstractConfigValue> e : value.entrySet()) {
|
||||||
|
m.put(e.getKey(), e.getValue().unwrapped());
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SimpleConfigObject mergedWithObject(AbstractConfigObject abstractFallback) {
|
||||||
|
requireNotIgnoringFallbacks();
|
||||||
|
|
||||||
|
if (!(abstractFallback instanceof SimpleConfigObject)) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"should not be reached (merging non-SimpleConfigObject)");
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigObject fallback = (SimpleConfigObject) abstractFallback;
|
||||||
|
|
||||||
|
boolean changed = false;
|
||||||
|
boolean allResolved = true;
|
||||||
|
Map<String, AbstractConfigValue> merged = new HashMap<String, AbstractConfigValue>();
|
||||||
|
Set<String> allKeys = new HashSet<String>();
|
||||||
|
allKeys.addAll(this.keySet());
|
||||||
|
allKeys.addAll(fallback.keySet());
|
||||||
|
for (String key : allKeys) {
|
||||||
|
AbstractConfigValue first = this.value.get(key);
|
||||||
|
AbstractConfigValue second = fallback.value.get(key);
|
||||||
|
AbstractConfigValue kept;
|
||||||
|
if (first == null)
|
||||||
|
kept = second;
|
||||||
|
else if (second == null)
|
||||||
|
kept = first;
|
||||||
|
else
|
||||||
|
kept = first.withFallback(second);
|
||||||
|
|
||||||
|
merged.put(key, kept);
|
||||||
|
|
||||||
|
if (first != kept)
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
if (kept.resolveStatus() == ResolveStatus.UNRESOLVED)
|
||||||
|
allResolved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveStatus newResolveStatus = ResolveStatus.fromBoolean(allResolved);
|
||||||
|
boolean newIgnoresFallbacks = fallback.ignoresFallbacks();
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
return new SimpleConfigObject(mergeOrigins(this, fallback), merged, newResolveStatus,
|
||||||
|
newIgnoresFallbacks);
|
||||||
|
else if (newResolveStatus != resolveStatus() || newIgnoresFallbacks != ignoresFallbacks())
|
||||||
|
return newCopy(newResolveStatus, origin(), newIgnoresFallbacks);
|
||||||
|
else
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleConfigObject modify(NoExceptionsModifier modifier) {
|
||||||
|
try {
|
||||||
|
return modifyMayThrow(modifier);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConfigException.BugOrBroken("unexpected checked exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleConfigObject modifyMayThrow(Modifier modifier) throws Exception {
|
||||||
|
Map<String, AbstractConfigValue> changes = null;
|
||||||
|
for (String k : keySet()) {
|
||||||
|
AbstractConfigValue v = value.get(k);
|
||||||
|
// "modified" may be null, which means remove the child;
|
||||||
|
// to do that we put null in the "changes" map.
|
||||||
|
AbstractConfigValue modified = modifier.modifyChildMayThrow(k, v);
|
||||||
|
if (modified != v) {
|
||||||
|
if (changes == null)
|
||||||
|
changes = new HashMap<String, AbstractConfigValue>();
|
||||||
|
changes.put(k, modified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changes == null) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
Map<String, AbstractConfigValue> modified = new HashMap<String, AbstractConfigValue>();
|
||||||
|
boolean sawUnresolved = false;
|
||||||
|
for (String k : keySet()) {
|
||||||
|
if (changes.containsKey(k)) {
|
||||||
|
AbstractConfigValue newValue = changes.get(k);
|
||||||
|
if (newValue != null) {
|
||||||
|
modified.put(k, newValue);
|
||||||
|
if (newValue.resolveStatus() == ResolveStatus.UNRESOLVED)
|
||||||
|
sawUnresolved = true;
|
||||||
|
} else {
|
||||||
|
// remove this child; don't put it in the new map.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AbstractConfigValue newValue = value.get(k);
|
||||||
|
modified.put(k, newValue);
|
||||||
|
if (newValue.resolveStatus() == ResolveStatus.UNRESOLVED)
|
||||||
|
sawUnresolved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new SimpleConfigObject(origin(), modified,
|
||||||
|
sawUnresolved ? ResolveStatus.UNRESOLVED : ResolveStatus.RESOLVED,
|
||||||
|
ignoresFallbacks());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ResolveModifier implements Modifier {
|
||||||
|
|
||||||
|
final Path originalRestrict;
|
||||||
|
ResolveContext context;
|
||||||
|
final ResolveSource source;
|
||||||
|
|
||||||
|
ResolveModifier(ResolveContext context, ResolveSource source) {
|
||||||
|
this.context = context;
|
||||||
|
this.source = source;
|
||||||
|
originalRestrict = context.restrictToChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue modifyChildMayThrow(String key, AbstractConfigValue v) throws NotPossibleToResolve {
|
||||||
|
if (context.isRestrictedToChild()) {
|
||||||
|
if (key.equals(context.restrictToChild().first())) {
|
||||||
|
Path remainder = context.restrictToChild().remainder();
|
||||||
|
if (remainder != null) {
|
||||||
|
ResolveResult<? extends AbstractConfigValue> result = context.restrict(remainder).resolve(v,
|
||||||
|
source);
|
||||||
|
context = result.context.unrestricted().restrict(originalRestrict);
|
||||||
|
return result.value;
|
||||||
|
} else {
|
||||||
|
// we don't want to resolve the leaf child.
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// not in the restrictToChild path
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no restrictToChild, resolve everything
|
||||||
|
ResolveResult<? extends AbstractConfigValue> result = context.unrestricted().resolve(v, source);
|
||||||
|
context = result.context.unrestricted().restrict(originalRestrict);
|
||||||
|
return result.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ResolveResult<? extends AbstractConfigObject> resolveSubstitutions(ResolveContext context, ResolveSource source)
|
||||||
|
throws NotPossibleToResolve {
|
||||||
|
if (resolveStatus() == ResolveStatus.RESOLVED)
|
||||||
|
return ResolveResult.make(context, this);
|
||||||
|
|
||||||
|
final ResolveSource sourceWithParent = source.pushParent(this);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ResolveModifier modifier = new ResolveModifier(context, sourceWithParent);
|
||||||
|
|
||||||
|
AbstractConfigValue value = modifyMayThrow(modifier);
|
||||||
|
return ResolveResult.make(modifier.context, value).asObjectResult();
|
||||||
|
} catch (NotPossibleToResolve e) {
|
||||||
|
throw e;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConfigException.BugOrBroken("unexpected checked exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SimpleConfigObject relativized(final Path prefix) {
|
||||||
|
return modify(new NoExceptionsModifier() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue modifyChild(String key, AbstractConfigValue v) {
|
||||||
|
return v.relativized(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is only Serializable to chill out a findbugs warning
|
||||||
|
static final private class RenderComparator implements java.util.Comparator<String>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static boolean isAllDigits(String s) {
|
||||||
|
int length = s.length();
|
||||||
|
|
||||||
|
// empty string doesn't count as a number
|
||||||
|
if (length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
|
||||||
|
if (Character.isDigit(c))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is supposed to sort numbers before strings,
|
||||||
|
// and sort the numbers numerically. The point is
|
||||||
|
// to make objects which are really list-like
|
||||||
|
// (numeric indices) appear in order.
|
||||||
|
@Override
|
||||||
|
public int compare(String a, String b) {
|
||||||
|
boolean aDigits = isAllDigits(a);
|
||||||
|
boolean bDigits = isAllDigits(b);
|
||||||
|
if (aDigits && bDigits) {
|
||||||
|
return Integer.compare(Integer.parseInt(a), Integer.parseInt(b));
|
||||||
|
} else if (aDigits) {
|
||||||
|
return -1;
|
||||||
|
} else if (bDigits) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return a.compareTo(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
|
if (isEmpty()) {
|
||||||
|
sb.append("{}");
|
||||||
|
} else {
|
||||||
|
boolean outerBraces = options.getJson() || !atRoot;
|
||||||
|
|
||||||
|
int innerIndent;
|
||||||
|
if (outerBraces) {
|
||||||
|
innerIndent = indent + 1;
|
||||||
|
sb.append("{");
|
||||||
|
|
||||||
|
if (options.getFormatted())
|
||||||
|
sb.append('\n');
|
||||||
|
} else {
|
||||||
|
innerIndent = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int separatorCount = 0;
|
||||||
|
String[] keys = keySet().toArray(new String[size()]);
|
||||||
|
Arrays.sort(keys, new RenderComparator());
|
||||||
|
for (String k : keys) {
|
||||||
|
AbstractConfigValue v;
|
||||||
|
v = value.get(k);
|
||||||
|
|
||||||
|
if (options.getOriginComments()) {
|
||||||
|
String[] lines = v.origin().description().split("\n");
|
||||||
|
for (String l : lines) {
|
||||||
|
indent(sb, indent + 1, options);
|
||||||
|
sb.append('#');
|
||||||
|
if (!l.isEmpty())
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append(l);
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options.getComments()) {
|
||||||
|
for (String comment : v.origin().comments()) {
|
||||||
|
indent(sb, innerIndent, options);
|
||||||
|
sb.append("#");
|
||||||
|
if (!comment.startsWith(" "))
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append(comment);
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent(sb, innerIndent, options);
|
||||||
|
v.render(sb, innerIndent, false /* atRoot */, k, options);
|
||||||
|
|
||||||
|
if (options.getFormatted()) {
|
||||||
|
if (options.getJson()) {
|
||||||
|
sb.append(",");
|
||||||
|
separatorCount = 2;
|
||||||
|
} else {
|
||||||
|
separatorCount = 1;
|
||||||
|
}
|
||||||
|
sb.append('\n');
|
||||||
|
} else {
|
||||||
|
sb.append(",");
|
||||||
|
separatorCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// chop last commas/newlines
|
||||||
|
sb.setLength(sb.length() - separatorCount);
|
||||||
|
|
||||||
|
if (outerBraces) {
|
||||||
|
if (options.getFormatted()) {
|
||||||
|
sb.append('\n'); // put a newline back
|
||||||
|
if (outerBraces)
|
||||||
|
indent(sb, indent, options);
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (atRoot && options.getFormatted())
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractConfigValue get(Object key) {
|
||||||
|
return value.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean mapEquals(Map<String, ConfigValue> a, Map<String, ConfigValue> b) {
|
||||||
|
if (a == b)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Set<String> aKeys = a.keySet();
|
||||||
|
Set<String> bKeys = b.keySet();
|
||||||
|
|
||||||
|
if (!aKeys.equals(bKeys))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (String key : aKeys) {
|
||||||
|
if (!a.get(key).equals(b.get(key)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int mapHash(Map<String, ConfigValue> m) {
|
||||||
|
// the keys have to be sorted, otherwise we could be equal
|
||||||
|
// to another map but have a different hashcode.
|
||||||
|
List<String> keys = new ArrayList<String>();
|
||||||
|
keys.addAll(m.keySet());
|
||||||
|
Collections.sort(keys);
|
||||||
|
|
||||||
|
int valuesHash = 0;
|
||||||
|
for (String k : keys) {
|
||||||
|
valuesHash += m.get(k).hashCode();
|
||||||
|
}
|
||||||
|
return 41 * (41 + keys.hashCode()) + valuesHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof ConfigObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
// note that "origin" is deliberately NOT part of equality.
|
||||||
|
// neither are other "extras" like ignoresFallbacks or resolve status.
|
||||||
|
if (other instanceof ConfigObject) {
|
||||||
|
// optimization to avoid unwrapped() for two ConfigObject,
|
||||||
|
// which is what AbstractConfigValue does.
|
||||||
|
return canEqual(other) && mapEquals(this, ((ConfigObject) other));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// note that "origin" is deliberately NOT part of equality
|
||||||
|
// neither are other "extras" like ignoresFallbacks or resolve status.
|
||||||
|
return mapHash(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return value.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> keySet() {
|
||||||
|
return value.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object v) {
|
||||||
|
return value.containsValue(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Map.Entry<String, ConfigValue>> entrySet() {
|
||||||
|
// total bloat just to work around lack of type variance
|
||||||
|
|
||||||
|
HashSet<java.util.Map.Entry<String, ConfigValue>> entries = new HashSet<Map.Entry<String, ConfigValue>>();
|
||||||
|
for (Map.Entry<String, AbstractConfigValue> e : value.entrySet()) {
|
||||||
|
entries.add(new AbstractMap.SimpleImmutableEntry<String, ConfigValue>(
|
||||||
|
e.getKey(), e
|
||||||
|
.getValue()));
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return value.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigValue> values() {
|
||||||
|
return new HashSet<ConfigValue>(value.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
final private static String EMPTY_NAME = "empty config";
|
||||||
|
final private static SimpleConfigObject emptyInstance = empty(SimpleConfigOrigin
|
||||||
|
.newSimple(EMPTY_NAME));
|
||||||
|
|
||||||
|
final static SimpleConfigObject empty() {
|
||||||
|
return emptyInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
final static SimpleConfigObject empty(ConfigOrigin origin) {
|
||||||
|
if (origin == null)
|
||||||
|
return empty();
|
||||||
|
else
|
||||||
|
return new SimpleConfigObject(origin,
|
||||||
|
Collections.<String, AbstractConfigValue> emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
final static SimpleConfigObject emptyMissing(ConfigOrigin baseOrigin) {
|
||||||
|
return new SimpleConfigObject(SimpleConfigOrigin.newSimple(
|
||||||
|
baseOrigin.description() + " (not found)"),
|
||||||
|
Collections.<String, AbstractConfigValue> emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialization all goes through SerializedConfigValue
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new SerializedConfigValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+575
@@ -0,0 +1,575 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.impl.SerializedConfigValue.SerializedField;
|
||||||
|
|
||||||
|
// it would be cleaner to have a class hierarchy for various origin types,
|
||||||
|
// but was hoping this would be enough simpler to be a little messy. eh.
|
||||||
|
final class SimpleConfigOrigin implements ConfigOrigin {
|
||||||
|
|
||||||
|
final private String description;
|
||||||
|
final private int lineNumber;
|
||||||
|
final private int endLineNumber;
|
||||||
|
final private OriginType originType;
|
||||||
|
final private String urlOrNull;
|
||||||
|
final private String resourceOrNull;
|
||||||
|
final private List<String> commentsOrNull;
|
||||||
|
|
||||||
|
protected SimpleConfigOrigin(String description, int lineNumber, int endLineNumber, OriginType originType,
|
||||||
|
String urlOrNull, String resourceOrNull, List<String> commentsOrNull) {
|
||||||
|
if (description == null)
|
||||||
|
throw new ConfigException.BugOrBroken("description may not be null");
|
||||||
|
this.description = description;
|
||||||
|
this.lineNumber = lineNumber;
|
||||||
|
this.endLineNumber = endLineNumber;
|
||||||
|
this.originType = originType;
|
||||||
|
this.urlOrNull = urlOrNull;
|
||||||
|
this.resourceOrNull = resourceOrNull;
|
||||||
|
this.commentsOrNull = commentsOrNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newSimple(String description) {
|
||||||
|
return new SimpleConfigOrigin(description, -1, -1, OriginType.GENERIC, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newFile(String filename) {
|
||||||
|
String url;
|
||||||
|
try {
|
||||||
|
url = (new File(filename)).toURI().toURL().toExternalForm();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
url = null;
|
||||||
|
}
|
||||||
|
return new SimpleConfigOrigin(filename, -1, -1, OriginType.FILE, url, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newURL(URL url) {
|
||||||
|
String u = url.toExternalForm();
|
||||||
|
return new SimpleConfigOrigin(u, -1, -1, OriginType.URL, u, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newResource(String resource, URL url) {
|
||||||
|
String desc;
|
||||||
|
if (url != null)
|
||||||
|
desc = resource + " @ " + url.toExternalForm();
|
||||||
|
else
|
||||||
|
desc = resource;
|
||||||
|
return new SimpleConfigOrigin(desc, -1, -1, OriginType.RESOURCE, url != null ? url.toExternalForm() : null,
|
||||||
|
resource, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newResource(String resource) {
|
||||||
|
return newResource(resource, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigOrigin withLineNumber(int lineNumber) {
|
||||||
|
if (lineNumber == this.lineNumber && lineNumber == this.endLineNumber) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return new SimpleConfigOrigin(this.description, lineNumber, lineNumber, this.originType, this.urlOrNull,
|
||||||
|
this.resourceOrNull, this.commentsOrNull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigOrigin addURL(URL url) {
|
||||||
|
return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber, this.originType,
|
||||||
|
url != null ? url.toExternalForm() : null, this.resourceOrNull, this.commentsOrNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfigOrigin withComments(List<String> comments) {
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(comments, this.commentsOrNull)) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber, this.originType,
|
||||||
|
this.urlOrNull, this.resourceOrNull, comments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigOrigin prependComments(List<String> comments) {
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(comments, this.commentsOrNull) || comments == null) {
|
||||||
|
return this;
|
||||||
|
} else if (this.commentsOrNull == null) {
|
||||||
|
return withComments(comments);
|
||||||
|
} else {
|
||||||
|
List<String> merged = new ArrayList<String>(comments.size() + this.commentsOrNull.size());
|
||||||
|
merged.addAll(comments);
|
||||||
|
merged.addAll(this.commentsOrNull);
|
||||||
|
return withComments(merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigOrigin appendComments(List<String> comments) {
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(comments, this.commentsOrNull) || comments == null) {
|
||||||
|
return this;
|
||||||
|
} else if (this.commentsOrNull == null) {
|
||||||
|
return withComments(comments);
|
||||||
|
} else {
|
||||||
|
List<String> merged = new ArrayList<String>(comments.size() + this.commentsOrNull.size());
|
||||||
|
merged.addAll(this.commentsOrNull);
|
||||||
|
merged.addAll(comments);
|
||||||
|
return withComments(merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
if (lineNumber < 0) {
|
||||||
|
return description;
|
||||||
|
} else if (endLineNumber == lineNumber) {
|
||||||
|
return description + ": " + lineNumber;
|
||||||
|
} else {
|
||||||
|
return description + ": " + lineNumber + "-" + endLineNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof SimpleConfigOrigin) {
|
||||||
|
SimpleConfigOrigin otherOrigin = (SimpleConfigOrigin) other;
|
||||||
|
|
||||||
|
return this.description.equals(otherOrigin.description) && this.lineNumber == otherOrigin.lineNumber
|
||||||
|
&& this.endLineNumber == otherOrigin.endLineNumber && this.originType == otherOrigin.originType
|
||||||
|
&& ConfigImplUtil.equalsHandlingNull(this.urlOrNull, otherOrigin.urlOrNull)
|
||||||
|
&& ConfigImplUtil.equalsHandlingNull(this.resourceOrNull, otherOrigin.resourceOrNull);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = 41 * (41 + description.hashCode());
|
||||||
|
h = 41 * (h + lineNumber);
|
||||||
|
h = 41 * (h + endLineNumber);
|
||||||
|
h = 41 * (h + originType.hashCode());
|
||||||
|
if (urlOrNull != null)
|
||||||
|
h = 41 * (h + urlOrNull.hashCode());
|
||||||
|
if (resourceOrNull != null)
|
||||||
|
h = 41 * (h + resourceOrNull.hashCode());
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ConfigOrigin(" + description + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String filename() {
|
||||||
|
if (originType == OriginType.FILE) {
|
||||||
|
return description;
|
||||||
|
} else if (urlOrNull != null) {
|
||||||
|
URL url;
|
||||||
|
try {
|
||||||
|
url = new URL(urlOrNull);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (url.getProtocol().equals("file")) {
|
||||||
|
return url.getFile();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL url() {
|
||||||
|
if (urlOrNull == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return new URL(urlOrNull);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resource() {
|
||||||
|
return resourceOrNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lineNumber() {
|
||||||
|
return lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> comments() {
|
||||||
|
if (commentsOrNull != null) {
|
||||||
|
return Collections.unmodifiableList(commentsOrNull);
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String MERGE_OF_PREFIX = "merge of ";
|
||||||
|
|
||||||
|
private static SimpleConfigOrigin mergeTwo(SimpleConfigOrigin a, SimpleConfigOrigin b) {
|
||||||
|
String mergedDesc;
|
||||||
|
int mergedStartLine;
|
||||||
|
int mergedEndLine;
|
||||||
|
List<String> mergedComments;
|
||||||
|
|
||||||
|
OriginType mergedType;
|
||||||
|
if (a.originType == b.originType) {
|
||||||
|
mergedType = a.originType;
|
||||||
|
} else {
|
||||||
|
mergedType = OriginType.GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first use the "description" field which has no line numbers
|
||||||
|
// cluttering it.
|
||||||
|
String aDesc = a.description;
|
||||||
|
String bDesc = b.description;
|
||||||
|
if (aDesc.startsWith(MERGE_OF_PREFIX))
|
||||||
|
aDesc = aDesc.substring(MERGE_OF_PREFIX.length());
|
||||||
|
if (bDesc.startsWith(MERGE_OF_PREFIX))
|
||||||
|
bDesc = bDesc.substring(MERGE_OF_PREFIX.length());
|
||||||
|
|
||||||
|
if (aDesc.equals(bDesc)) {
|
||||||
|
mergedDesc = aDesc;
|
||||||
|
|
||||||
|
if (a.lineNumber < 0)
|
||||||
|
mergedStartLine = b.lineNumber;
|
||||||
|
else if (b.lineNumber < 0)
|
||||||
|
mergedStartLine = a.lineNumber;
|
||||||
|
else
|
||||||
|
mergedStartLine = Math.min(a.lineNumber, b.lineNumber);
|
||||||
|
|
||||||
|
mergedEndLine = Math.max(a.endLineNumber, b.endLineNumber);
|
||||||
|
} else {
|
||||||
|
// this whole merge song-and-dance was intended to avoid this case
|
||||||
|
// whenever possible, but we've lost. Now we have to lose some
|
||||||
|
// structured information and cram into a string.
|
||||||
|
|
||||||
|
// description() method includes line numbers, so use it instead
|
||||||
|
// of description field.
|
||||||
|
String aFull = a.description();
|
||||||
|
String bFull = b.description();
|
||||||
|
if (aFull.startsWith(MERGE_OF_PREFIX))
|
||||||
|
aFull = aFull.substring(MERGE_OF_PREFIX.length());
|
||||||
|
if (bFull.startsWith(MERGE_OF_PREFIX))
|
||||||
|
bFull = bFull.substring(MERGE_OF_PREFIX.length());
|
||||||
|
|
||||||
|
mergedDesc = MERGE_OF_PREFIX + aFull + "," + bFull;
|
||||||
|
|
||||||
|
mergedStartLine = -1;
|
||||||
|
mergedEndLine = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mergedURL;
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull)) {
|
||||||
|
mergedURL = a.urlOrNull;
|
||||||
|
} else {
|
||||||
|
mergedURL = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mergedResource;
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(a.resourceOrNull, b.resourceOrNull)) {
|
||||||
|
mergedResource = a.resourceOrNull;
|
||||||
|
} else {
|
||||||
|
mergedResource = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(a.commentsOrNull, b.commentsOrNull)) {
|
||||||
|
mergedComments = a.commentsOrNull;
|
||||||
|
} else {
|
||||||
|
mergedComments = new ArrayList<String>();
|
||||||
|
if (a.commentsOrNull != null)
|
||||||
|
mergedComments.addAll(a.commentsOrNull);
|
||||||
|
if (b.commentsOrNull != null)
|
||||||
|
mergedComments.addAll(b.commentsOrNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleConfigOrigin(mergedDesc, mergedStartLine, mergedEndLine, mergedType, mergedURL,
|
||||||
|
mergedResource, mergedComments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int similarity(SimpleConfigOrigin a, SimpleConfigOrigin b) {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (a.originType == b.originType)
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
if (a.description.equals(b.description)) {
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
// only count these if the description field (which is the file
|
||||||
|
// or resource name) also matches.
|
||||||
|
if (a.lineNumber == b.lineNumber)
|
||||||
|
count += 1;
|
||||||
|
if (a.endLineNumber == b.endLineNumber)
|
||||||
|
count += 1;
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull))
|
||||||
|
count += 1;
|
||||||
|
if (ConfigImplUtil.equalsHandlingNull(a.resourceOrNull, b.resourceOrNull))
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this picks the best pair to merge, because the pair has the most in
|
||||||
|
// common. we want to merge two lines in the same file rather than something
|
||||||
|
// else with one of the lines; because two lines in the same file can be
|
||||||
|
// better consolidated.
|
||||||
|
private static SimpleConfigOrigin mergeThree(SimpleConfigOrigin a, SimpleConfigOrigin b, SimpleConfigOrigin c) {
|
||||||
|
if (similarity(a, b) >= similarity(b, c)) {
|
||||||
|
return mergeTwo(mergeTwo(a, b), c);
|
||||||
|
} else {
|
||||||
|
return mergeTwo(a, mergeTwo(b, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigOrigin mergeOrigins(ConfigOrigin a, ConfigOrigin b) {
|
||||||
|
return mergeTwo((SimpleConfigOrigin) a, (SimpleConfigOrigin) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigOrigin mergeOrigins(List<? extends AbstractConfigValue> stack) {
|
||||||
|
List<ConfigOrigin> origins = new ArrayList<ConfigOrigin>(stack.size());
|
||||||
|
for (AbstractConfigValue v : stack) {
|
||||||
|
origins.add(v.origin());
|
||||||
|
}
|
||||||
|
return mergeOrigins(origins);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigOrigin mergeOrigins(Collection<? extends ConfigOrigin> stack) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
throw new ConfigException.BugOrBroken("can't merge empty list of origins");
|
||||||
|
} else if (stack.size() == 1) {
|
||||||
|
return stack.iterator().next();
|
||||||
|
} else if (stack.size() == 2) {
|
||||||
|
Iterator<? extends ConfigOrigin> i = stack.iterator();
|
||||||
|
return mergeTwo((SimpleConfigOrigin) i.next(), (SimpleConfigOrigin) i.next());
|
||||||
|
} else {
|
||||||
|
List<SimpleConfigOrigin> remaining = new ArrayList<SimpleConfigOrigin>();
|
||||||
|
for (ConfigOrigin o : stack) {
|
||||||
|
remaining.add((SimpleConfigOrigin) o);
|
||||||
|
}
|
||||||
|
while (remaining.size() > 2) {
|
||||||
|
SimpleConfigOrigin c = remaining.get(remaining.size() - 1);
|
||||||
|
remaining.remove(remaining.size() - 1);
|
||||||
|
SimpleConfigOrigin b = remaining.get(remaining.size() - 1);
|
||||||
|
remaining.remove(remaining.size() - 1);
|
||||||
|
SimpleConfigOrigin a = remaining.get(remaining.size() - 1);
|
||||||
|
remaining.remove(remaining.size() - 1);
|
||||||
|
|
||||||
|
SimpleConfigOrigin merged = mergeThree(a, b, c);
|
||||||
|
|
||||||
|
remaining.add(merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be down to either 1 or 2
|
||||||
|
return mergeOrigins(remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<SerializedField, Object> toFields() {
|
||||||
|
Map<SerializedField, Object> m = new EnumMap<SerializedField, Object>(SerializedField.class);
|
||||||
|
|
||||||
|
m.put(SerializedField.ORIGIN_DESCRIPTION, description);
|
||||||
|
|
||||||
|
if (lineNumber >= 0)
|
||||||
|
m.put(SerializedField.ORIGIN_LINE_NUMBER, lineNumber);
|
||||||
|
if (endLineNumber >= 0)
|
||||||
|
m.put(SerializedField.ORIGIN_END_LINE_NUMBER, endLineNumber);
|
||||||
|
|
||||||
|
m.put(SerializedField.ORIGIN_TYPE, originType.ordinal());
|
||||||
|
|
||||||
|
if (urlOrNull != null)
|
||||||
|
m.put(SerializedField.ORIGIN_URL, urlOrNull);
|
||||||
|
if (resourceOrNull != null)
|
||||||
|
m.put(SerializedField.ORIGIN_RESOURCE, resourceOrNull);
|
||||||
|
if (commentsOrNull != null)
|
||||||
|
m.put(SerializedField.ORIGIN_COMMENTS, commentsOrNull);
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<SerializedField, Object> toFieldsDelta(SimpleConfigOrigin baseOrigin) {
|
||||||
|
Map<SerializedField, Object> baseFields;
|
||||||
|
if (baseOrigin != null)
|
||||||
|
baseFields = baseOrigin.toFields();
|
||||||
|
else
|
||||||
|
baseFields = Collections.<SerializedField, Object> emptyMap();
|
||||||
|
return fieldsDelta(baseFields, toFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we're trying to avoid serializing the same info over and over
|
||||||
|
// in the common case that child objects have the same origin fields
|
||||||
|
// as their parent objects. e.g. we don't need to store the source
|
||||||
|
// filename with every single value.
|
||||||
|
static Map<SerializedField, Object> fieldsDelta(Map<SerializedField, Object> base,
|
||||||
|
Map<SerializedField, Object> child) {
|
||||||
|
Map<SerializedField, Object> m = new EnumMap<SerializedField, Object>(child);
|
||||||
|
|
||||||
|
for (Map.Entry<SerializedField, Object> baseEntry : base.entrySet()) {
|
||||||
|
SerializedField f = baseEntry.getKey();
|
||||||
|
if (m.containsKey(f) && ConfigImplUtil.equalsHandlingNull(baseEntry.getValue(), m.get(f))) {
|
||||||
|
// if field is unchanged, just remove it so we inherit
|
||||||
|
m.remove(f);
|
||||||
|
} else if (!m.containsKey(f)) {
|
||||||
|
// if field has been removed, we have to add a deletion entry
|
||||||
|
switch (f) {
|
||||||
|
case ORIGIN_DESCRIPTION:
|
||||||
|
throw new ConfigException.BugOrBroken("origin missing description field? " + child);
|
||||||
|
case ORIGIN_LINE_NUMBER:
|
||||||
|
m.put(SerializedField.ORIGIN_LINE_NUMBER, -1);
|
||||||
|
break;
|
||||||
|
case ORIGIN_END_LINE_NUMBER:
|
||||||
|
m.put(SerializedField.ORIGIN_END_LINE_NUMBER, -1);
|
||||||
|
break;
|
||||||
|
case ORIGIN_TYPE:
|
||||||
|
throw new ConfigException.BugOrBroken("should always be an ORIGIN_TYPE field");
|
||||||
|
case ORIGIN_URL:
|
||||||
|
m.put(SerializedField.ORIGIN_NULL_URL, "");
|
||||||
|
break;
|
||||||
|
case ORIGIN_RESOURCE:
|
||||||
|
m.put(SerializedField.ORIGIN_NULL_RESOURCE, "");
|
||||||
|
break;
|
||||||
|
case ORIGIN_COMMENTS:
|
||||||
|
m.put(SerializedField.ORIGIN_NULL_COMMENTS, "");
|
||||||
|
break;
|
||||||
|
case ORIGIN_NULL_URL: // FALL THRU
|
||||||
|
case ORIGIN_NULL_RESOURCE: // FALL THRU
|
||||||
|
case ORIGIN_NULL_COMMENTS:
|
||||||
|
throw new ConfigException.BugOrBroken("computing delta, base object should not contain " + f + " "
|
||||||
|
+ base);
|
||||||
|
case END_MARKER:
|
||||||
|
case ROOT_VALUE:
|
||||||
|
case ROOT_WAS_CONFIG:
|
||||||
|
case UNKNOWN:
|
||||||
|
case VALUE_DATA:
|
||||||
|
case VALUE_ORIGIN:
|
||||||
|
throw new ConfigException.BugOrBroken("should not appear here: " + f);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// field is in base and child, but differs, so leave it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin fromFields(Map<SerializedField, Object> m) throws IOException {
|
||||||
|
// we represent a null origin as one with no fields at all
|
||||||
|
if (m.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String description = (String) m.get(SerializedField.ORIGIN_DESCRIPTION);
|
||||||
|
Integer lineNumber = (Integer) m.get(SerializedField.ORIGIN_LINE_NUMBER);
|
||||||
|
Integer endLineNumber = (Integer) m.get(SerializedField.ORIGIN_END_LINE_NUMBER);
|
||||||
|
Number originTypeOrdinal = (Number) m.get(SerializedField.ORIGIN_TYPE);
|
||||||
|
if (originTypeOrdinal == null)
|
||||||
|
throw new IOException("Missing ORIGIN_TYPE field");
|
||||||
|
OriginType originType = OriginType.values()[originTypeOrdinal.byteValue()];
|
||||||
|
String urlOrNull = (String) m.get(SerializedField.ORIGIN_URL);
|
||||||
|
String resourceOrNull = (String) m.get(SerializedField.ORIGIN_RESOURCE);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<String> commentsOrNull = (List<String>) m.get(SerializedField.ORIGIN_COMMENTS);
|
||||||
|
// Older versions did not have a resource field, they stuffed it into
|
||||||
|
// the description.
|
||||||
|
if (originType == OriginType.RESOURCE && resourceOrNull == null) {
|
||||||
|
resourceOrNull = description;
|
||||||
|
}
|
||||||
|
return new SimpleConfigOrigin(description, lineNumber != null ? lineNumber : -1,
|
||||||
|
endLineNumber != null ? endLineNumber : -1, originType, urlOrNull, resourceOrNull, commentsOrNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<SerializedField, Object> applyFieldsDelta(Map<SerializedField, Object> base,
|
||||||
|
Map<SerializedField, Object> delta) throws IOException {
|
||||||
|
|
||||||
|
Map<SerializedField, Object> m = new EnumMap<SerializedField, Object>(delta);
|
||||||
|
|
||||||
|
for (Map.Entry<SerializedField, Object> baseEntry : base.entrySet()) {
|
||||||
|
SerializedField f = baseEntry.getKey();
|
||||||
|
if (delta.containsKey(f)) {
|
||||||
|
// delta overrides when keys are in both
|
||||||
|
// "m" should already contain the right thing
|
||||||
|
} else {
|
||||||
|
// base has the key and delta does not.
|
||||||
|
// we inherit from base unless a "NULL" key blocks.
|
||||||
|
switch (f) {
|
||||||
|
case ORIGIN_DESCRIPTION:
|
||||||
|
m.put(f, base.get(f));
|
||||||
|
break;
|
||||||
|
case ORIGIN_URL:
|
||||||
|
if (delta.containsKey(SerializedField.ORIGIN_NULL_URL)) {
|
||||||
|
m.remove(SerializedField.ORIGIN_NULL_URL);
|
||||||
|
} else {
|
||||||
|
m.put(f, base.get(f));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ORIGIN_RESOURCE:
|
||||||
|
if (delta.containsKey(SerializedField.ORIGIN_NULL_RESOURCE)) {
|
||||||
|
m.remove(SerializedField.ORIGIN_NULL_RESOURCE);
|
||||||
|
} else {
|
||||||
|
m.put(f, base.get(f));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ORIGIN_COMMENTS:
|
||||||
|
if (delta.containsKey(SerializedField.ORIGIN_NULL_COMMENTS)) {
|
||||||
|
m.remove(SerializedField.ORIGIN_NULL_COMMENTS);
|
||||||
|
} else {
|
||||||
|
m.put(f, base.get(f));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ORIGIN_NULL_URL: // FALL THRU
|
||||||
|
case ORIGIN_NULL_RESOURCE: // FALL THRU
|
||||||
|
case ORIGIN_NULL_COMMENTS: // FALL THRU
|
||||||
|
// base objects shouldn't contain these, should just
|
||||||
|
// lack the field. these are only in deltas.
|
||||||
|
throw new ConfigException.BugOrBroken("applying fields, base object should not contain " + f + " "
|
||||||
|
+ base);
|
||||||
|
case ORIGIN_END_LINE_NUMBER: // FALL THRU
|
||||||
|
case ORIGIN_LINE_NUMBER: // FALL THRU
|
||||||
|
case ORIGIN_TYPE:
|
||||||
|
m.put(f, base.get(f));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case END_MARKER:
|
||||||
|
case ROOT_VALUE:
|
||||||
|
case ROOT_WAS_CONFIG:
|
||||||
|
case UNKNOWN:
|
||||||
|
case VALUE_DATA:
|
||||||
|
case VALUE_ORIGIN:
|
||||||
|
throw new ConfigException.BugOrBroken("should not appear here: " + f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin fromBase(SimpleConfigOrigin baseOrigin, Map<SerializedField, Object> delta)
|
||||||
|
throws IOException {
|
||||||
|
Map<SerializedField, Object> baseFields;
|
||||||
|
if (baseOrigin != null)
|
||||||
|
baseFields = baseOrigin.toFields();
|
||||||
|
else
|
||||||
|
baseFields = Collections.<SerializedField, Object> emptyMap();
|
||||||
|
Map<SerializedField, Object> fields = applyFieldsDelta(baseFields, delta);
|
||||||
|
return fromFields(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncludeContext;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseable;
|
||||||
|
|
||||||
|
class SimpleIncludeContext implements ConfigIncludeContext {
|
||||||
|
|
||||||
|
private final Parseable parseable;
|
||||||
|
private final ConfigParseOptions options;
|
||||||
|
|
||||||
|
SimpleIncludeContext(Parseable parseable) {
|
||||||
|
this.parseable = parseable;
|
||||||
|
this.options = SimpleIncluder.clearForInclude(parseable.options());
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleIncludeContext(Parseable parseable, ConfigParseOptions options) {
|
||||||
|
this.parseable = parseable;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleIncludeContext withParseable(Parseable parseable) {
|
||||||
|
if (parseable == this.parseable)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new SimpleIncludeContext(parseable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigParseable relativeTo(String filename) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled())
|
||||||
|
ConfigImpl.trace("Looking for '" + filename + "' relative to " + parseable);
|
||||||
|
if (parseable != null)
|
||||||
|
return parseable.relativeTo(filename);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigParseOptions parseOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigIncludeContext setParseOptions(ConfigParseOptions options) {
|
||||||
|
return new SimpleIncludeContext(parseable, options.setSyntax(null).setOriginDescription(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
+302
@@ -0,0 +1,302 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigFactory;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncludeContext;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluder;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluderClasspath;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluderFile;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigIncluderURL;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigObject;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigParseable;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
|
||||||
|
class SimpleIncluder implements FullIncluder {
|
||||||
|
|
||||||
|
private ConfigIncluder fallback;
|
||||||
|
|
||||||
|
SimpleIncluder(ConfigIncluder fallback) {
|
||||||
|
this.fallback = fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigIncludeContext does this for us on its options
|
||||||
|
static ConfigParseOptions clearForInclude(ConfigParseOptions options) {
|
||||||
|
// the class loader and includer are inherited, but not this other
|
||||||
|
// stuff.
|
||||||
|
return options.setSyntax(null).setOriginDescription(null).setAllowMissing(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the heuristic includer
|
||||||
|
@Override
|
||||||
|
public ConfigObject include(final ConfigIncludeContext context, String name) {
|
||||||
|
ConfigObject obj = includeWithoutFallback(context, name);
|
||||||
|
|
||||||
|
// now use the fallback includer if any and merge
|
||||||
|
// its result.
|
||||||
|
if (fallback != null) {
|
||||||
|
return obj.withFallback(fallback.include(context, name));
|
||||||
|
} else {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the heuristic includer in static form
|
||||||
|
static ConfigObject includeWithoutFallback(final ConfigIncludeContext context, String name) {
|
||||||
|
// the heuristic is valid URL then URL, else relative to including file;
|
||||||
|
// relativeTo in a file falls back to classpath inside relativeTo().
|
||||||
|
|
||||||
|
URL url;
|
||||||
|
try {
|
||||||
|
url = new URL(name);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
url = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url != null) {
|
||||||
|
return includeURLWithoutFallback(context, url);
|
||||||
|
} else {
|
||||||
|
NameSource source = new RelativeNameSource(context);
|
||||||
|
return fromBasename(source, name, context.parseOptions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject includeURL(ConfigIncludeContext context, URL url) {
|
||||||
|
ConfigObject obj = includeURLWithoutFallback(context, url);
|
||||||
|
|
||||||
|
// now use the fallback includer if any and merge
|
||||||
|
// its result.
|
||||||
|
if (fallback != null && fallback instanceof ConfigIncluderURL) {
|
||||||
|
return obj.withFallback(((ConfigIncluderURL) fallback).includeURL(context, url));
|
||||||
|
} else {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigObject includeURLWithoutFallback(final ConfigIncludeContext context, URL url) {
|
||||||
|
return ConfigFactory.parseURL(url, context.parseOptions()).root();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject includeFile(ConfigIncludeContext context, File file) {
|
||||||
|
ConfigObject obj = includeFileWithoutFallback(context, file);
|
||||||
|
|
||||||
|
// now use the fallback includer if any and merge
|
||||||
|
// its result.
|
||||||
|
if (fallback != null && fallback instanceof ConfigIncluderFile) {
|
||||||
|
return obj.withFallback(((ConfigIncluderFile) fallback).includeFile(context, file));
|
||||||
|
} else {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigObject includeFileWithoutFallback(final ConfigIncludeContext context, File file) {
|
||||||
|
return ConfigFactory.parseFileAnySyntax(file, context.parseOptions()).root();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject includeResources(ConfigIncludeContext context, String resource) {
|
||||||
|
ConfigObject obj = includeResourceWithoutFallback(context, resource);
|
||||||
|
|
||||||
|
// now use the fallback includer if any and merge
|
||||||
|
// its result.
|
||||||
|
if (fallback != null && fallback instanceof ConfigIncluderClasspath) {
|
||||||
|
return obj.withFallback(((ConfigIncluderClasspath) fallback).includeResources(context,
|
||||||
|
resource));
|
||||||
|
} else {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigObject includeResourceWithoutFallback(final ConfigIncludeContext context,
|
||||||
|
String resource) {
|
||||||
|
return ConfigFactory.parseResourcesAnySyntax(resource, context.parseOptions()).root();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigIncluder withFallback(ConfigIncluder fallback) {
|
||||||
|
if (this == fallback) {
|
||||||
|
throw new ConfigException.BugOrBroken("trying to create includer cycle");
|
||||||
|
} else if (this.fallback == fallback) {
|
||||||
|
return this;
|
||||||
|
} else if (this.fallback != null) {
|
||||||
|
return new SimpleIncluder(this.fallback.withFallback(fallback));
|
||||||
|
} else {
|
||||||
|
return new SimpleIncluder(fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NameSource {
|
||||||
|
ConfigParseable nameToParseable(String name, ConfigParseOptions parseOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static private class RelativeNameSource implements NameSource {
|
||||||
|
final private ConfigIncludeContext context;
|
||||||
|
|
||||||
|
RelativeNameSource(ConfigIncludeContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigParseable nameToParseable(String name, ConfigParseOptions options) {
|
||||||
|
ConfigParseable p = context.relativeTo(name);
|
||||||
|
if (p == null) {
|
||||||
|
// avoid returning null
|
||||||
|
return Parseable
|
||||||
|
.newNotFound(name, "include was not found: '" + name + "'", options);
|
||||||
|
} else {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// this function is a little tricky because there are three places we're
|
||||||
|
// trying to use it; for 'include "basename"' in a .conf file, for
|
||||||
|
// loading app.{conf,json,properties} from classpath, and for
|
||||||
|
// loading app.{conf,json,properties} from the filesystem.
|
||||||
|
static ConfigObject fromBasename(NameSource source, String name, ConfigParseOptions options) {
|
||||||
|
ConfigObject obj;
|
||||||
|
if (name.endsWith(".conf") || name.endsWith(".json") || name.endsWith(".properties")) {
|
||||||
|
ConfigParseable p = source.nameToParseable(name, options);
|
||||||
|
|
||||||
|
obj = p.parse(p.options().setAllowMissing(options.getAllowMissing()));
|
||||||
|
} else {
|
||||||
|
ConfigParseable confHandle = source.nameToParseable(name + ".conf", options);
|
||||||
|
ConfigParseable jsonHandle = source.nameToParseable(name + ".json", options);
|
||||||
|
ConfigParseable propsHandle = source.nameToParseable(name + ".properties", options);
|
||||||
|
boolean gotSomething = false;
|
||||||
|
List<ConfigException.IO> fails = new ArrayList<ConfigException.IO>();
|
||||||
|
|
||||||
|
ConfigSyntax syntax = options.getSyntax();
|
||||||
|
|
||||||
|
obj = SimpleConfigObject.empty(SimpleConfigOrigin.newSimple(name));
|
||||||
|
if (syntax == null || syntax == ConfigSyntax.CONF) {
|
||||||
|
try {
|
||||||
|
obj = confHandle.parse(confHandle.options().setAllowMissing(false)
|
||||||
|
.setSyntax(ConfigSyntax.CONF));
|
||||||
|
gotSomething = true;
|
||||||
|
} catch (ConfigException.IO e) {
|
||||||
|
fails.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syntax == null || syntax == ConfigSyntax.JSON) {
|
||||||
|
try {
|
||||||
|
ConfigObject parsed = jsonHandle.parse(jsonHandle.options()
|
||||||
|
.setAllowMissing(false).setSyntax(ConfigSyntax.JSON));
|
||||||
|
obj = obj.withFallback(parsed);
|
||||||
|
gotSomething = true;
|
||||||
|
} catch (ConfigException.IO e) {
|
||||||
|
fails.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syntax == null || syntax == ConfigSyntax.PROPERTIES) {
|
||||||
|
try {
|
||||||
|
ConfigObject parsed = propsHandle.parse(propsHandle.options()
|
||||||
|
.setAllowMissing(false).setSyntax(ConfigSyntax.PROPERTIES));
|
||||||
|
obj = obj.withFallback(parsed);
|
||||||
|
gotSomething = true;
|
||||||
|
} catch (ConfigException.IO e) {
|
||||||
|
fails.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.getAllowMissing() && !gotSomething) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled()) {
|
||||||
|
// the individual exceptions should have been logged already
|
||||||
|
// with tracing enabled
|
||||||
|
ConfigImpl.trace("Did not find '" + name
|
||||||
|
+ "' with any extension (.conf, .json, .properties); "
|
||||||
|
+ "exceptions should have been logged above.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fails.isEmpty()) {
|
||||||
|
// this should not happen
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"should not be reached: nothing found but no exceptions thrown");
|
||||||
|
} else {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Throwable t : fails) {
|
||||||
|
sb.append(t.getMessage());
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.setLength(sb.length() - 2);
|
||||||
|
throw new ConfigException.IO(SimpleConfigOrigin.newSimple(name), sb.toString(),
|
||||||
|
fails.get(0));
|
||||||
|
}
|
||||||
|
} else if (!gotSomething) {
|
||||||
|
if (ConfigImpl.traceLoadsEnabled()) {
|
||||||
|
ConfigImpl.trace("Did not find '" + name
|
||||||
|
+ "' with any extension (.conf, .json, .properties); but '" + name
|
||||||
|
+ "' is allowed to be missing. Exceptions from load attempts should have been logged above.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the Proxy is a proxy for an application-provided includer that uses our
|
||||||
|
// default implementations when the application-provided includer doesn't
|
||||||
|
// have an implementation.
|
||||||
|
static private class Proxy implements FullIncluder {
|
||||||
|
final ConfigIncluder delegate;
|
||||||
|
|
||||||
|
Proxy(ConfigIncluder delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigIncluder withFallback(ConfigIncluder fallback) {
|
||||||
|
// we never fall back
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject include(ConfigIncludeContext context, String what) {
|
||||||
|
return delegate.include(context, what);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject includeResources(ConfigIncludeContext context, String what) {
|
||||||
|
if (delegate instanceof ConfigIncluderClasspath)
|
||||||
|
return ((ConfigIncluderClasspath) delegate).includeResources(context, what);
|
||||||
|
else
|
||||||
|
return includeResourceWithoutFallback(context, what);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject includeURL(ConfigIncludeContext context, URL what) {
|
||||||
|
if (delegate instanceof ConfigIncluderURL)
|
||||||
|
return ((ConfigIncluderURL) delegate).includeURL(context, what);
|
||||||
|
else
|
||||||
|
return includeURLWithoutFallback(context, what);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigObject includeFile(ConfigIncludeContext context, File what) {
|
||||||
|
if (delegate instanceof ConfigIncluderFile)
|
||||||
|
return ((ConfigIncluderFile) delegate).includeFile(context, what);
|
||||||
|
else
|
||||||
|
return includeFileWithoutFallback(context, what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FullIncluder makeFull(ConfigIncluder includer) {
|
||||||
|
if (includer instanceof FullIncluder)
|
||||||
|
return (FullIncluder) includer;
|
||||||
|
else
|
||||||
|
return new Proxy(includer);
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
final class SubstitutionExpression {
|
||||||
|
|
||||||
|
final private Path path;
|
||||||
|
final private boolean optional;
|
||||||
|
|
||||||
|
SubstitutionExpression(Path path, boolean optional) {
|
||||||
|
this.path = path;
|
||||||
|
this.optional = optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path path() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean optional() {
|
||||||
|
return optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubstitutionExpression changePath(Path newPath) {
|
||||||
|
if (newPath == path)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new SubstitutionExpression(newPath, optional);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "${" + (optional ? "?" : "") + path.render() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof SubstitutionExpression) {
|
||||||
|
SubstitutionExpression otherExp = (SubstitutionExpression) other;
|
||||||
|
return otherExp.path.equals(this.path) && otherExp.optional == this.optional;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = 41 * (41 + path.hashCode());
|
||||||
|
h = 41 * (h + (optional ? 1 : 0));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
|
||||||
|
class Token {
|
||||||
|
final private TokenType tokenType;
|
||||||
|
final private String debugString;
|
||||||
|
final private ConfigOrigin origin;
|
||||||
|
final private String tokenText;
|
||||||
|
|
||||||
|
Token(TokenType tokenType, ConfigOrigin origin) {
|
||||||
|
this(tokenType, origin, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token(TokenType tokenType, ConfigOrigin origin, String tokenText) {
|
||||||
|
this(tokenType, origin, tokenText, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token(TokenType tokenType, ConfigOrigin origin, String tokenText, String debugString) {
|
||||||
|
this.tokenType = tokenType;
|
||||||
|
this.origin = origin;
|
||||||
|
this.debugString = debugString;
|
||||||
|
this.tokenText = tokenText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is used for singleton tokens like COMMA or OPEN_CURLY
|
||||||
|
static Token newWithoutOrigin(TokenType tokenType, String debugString, String tokenText) {
|
||||||
|
return new Token(tokenType, null, tokenText, debugString);
|
||||||
|
}
|
||||||
|
|
||||||
|
final TokenType tokenType() {
|
||||||
|
return tokenType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String tokenText() { return tokenText; }
|
||||||
|
|
||||||
|
// this is final because we don't always use the origin() accessor,
|
||||||
|
// and we don't because it throws if origin is null
|
||||||
|
final ConfigOrigin origin() {
|
||||||
|
// code is only supposed to call origin() on token types that are
|
||||||
|
// expected to have an origin.
|
||||||
|
if (origin == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"tried to get origin from token that doesn't have one: " + this);
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int lineNumber() {
|
||||||
|
if (origin != null)
|
||||||
|
return origin.lineNumber();
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (debugString != null)
|
||||||
|
return debugString;
|
||||||
|
else
|
||||||
|
return tokenType.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof Token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof Token) {
|
||||||
|
// origin is deliberately left out
|
||||||
|
return canEqual(other)
|
||||||
|
&& this.tokenType == ((Token) other).tokenType;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// origin is deliberately left out
|
||||||
|
return tokenType.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
enum TokenType {
|
||||||
|
START,
|
||||||
|
END,
|
||||||
|
COMMA,
|
||||||
|
EQUALS,
|
||||||
|
COLON,
|
||||||
|
OPEN_CURLY,
|
||||||
|
CLOSE_CURLY,
|
||||||
|
OPEN_SQUARE,
|
||||||
|
CLOSE_SQUARE,
|
||||||
|
VALUE,
|
||||||
|
NEWLINE,
|
||||||
|
UNQUOTED_TEXT,
|
||||||
|
IGNORED_WHITESPACE,
|
||||||
|
SUBSTITUTION,
|
||||||
|
PROBLEM,
|
||||||
|
COMMENT,
|
||||||
|
PLUS_EQUALS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,695 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigSyntax;
|
||||||
|
|
||||||
|
final class Tokenizer {
|
||||||
|
// this exception should not leave this file
|
||||||
|
private static class ProblemException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
final private Token problem;
|
||||||
|
|
||||||
|
ProblemException(Token problem) {
|
||||||
|
this.problem = problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token problem() {
|
||||||
|
return problem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String asString(int codepoint) {
|
||||||
|
if (codepoint == '\n')
|
||||||
|
return "newline";
|
||||||
|
else if (codepoint == '\t')
|
||||||
|
return "tab";
|
||||||
|
else if (codepoint == -1)
|
||||||
|
return "end of file";
|
||||||
|
else if (ConfigImplUtil.isC0Control(codepoint))
|
||||||
|
return String.format("control character 0x%x", codepoint);
|
||||||
|
else
|
||||||
|
return String.format("%c", codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tokenizes a Reader. Does not close the reader; you have to arrange to do
|
||||||
|
* that after you're done with the returned iterator.
|
||||||
|
*/
|
||||||
|
static Iterator<Token> tokenize(ConfigOrigin origin, Reader input, ConfigSyntax flavor) {
|
||||||
|
return new TokenIterator(origin, input, flavor != ConfigSyntax.JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String render(Iterator<Token> tokens) {
|
||||||
|
StringBuilder renderedText = new StringBuilder();
|
||||||
|
while (tokens.hasNext()) {
|
||||||
|
renderedText.append(tokens.next().tokenText());
|
||||||
|
}
|
||||||
|
return renderedText.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TokenIterator implements Iterator<Token> {
|
||||||
|
|
||||||
|
private static class WhitespaceSaver {
|
||||||
|
// has to be saved inside value concatenations
|
||||||
|
private StringBuilder whitespace;
|
||||||
|
// may need to value-concat with next value
|
||||||
|
private boolean lastTokenWasSimpleValue;
|
||||||
|
|
||||||
|
WhitespaceSaver() {
|
||||||
|
whitespace = new StringBuilder();
|
||||||
|
lastTokenWasSimpleValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(int c) {
|
||||||
|
whitespace.appendCodePoint(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token check(Token t, ConfigOrigin baseOrigin, int lineNumber) {
|
||||||
|
if (isSimpleValue(t)) {
|
||||||
|
return nextIsASimpleValue(baseOrigin, lineNumber);
|
||||||
|
} else {
|
||||||
|
return nextIsNotASimpleValue(baseOrigin, lineNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called if the next token is not a simple value;
|
||||||
|
// discards any whitespace we were saving between
|
||||||
|
// simple values.
|
||||||
|
private Token nextIsNotASimpleValue(ConfigOrigin baseOrigin, int lineNumber) {
|
||||||
|
lastTokenWasSimpleValue = false;
|
||||||
|
return createWhitespaceTokenFromSaver(baseOrigin, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// called if the next token IS a simple value,
|
||||||
|
// so creates a whitespace token if the previous
|
||||||
|
// token also was.
|
||||||
|
private Token nextIsASimpleValue(ConfigOrigin baseOrigin,
|
||||||
|
int lineNumber) {
|
||||||
|
Token t = createWhitespaceTokenFromSaver(baseOrigin, lineNumber);
|
||||||
|
if (!lastTokenWasSimpleValue) {
|
||||||
|
lastTokenWasSimpleValue = true;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token createWhitespaceTokenFromSaver(ConfigOrigin baseOrigin,
|
||||||
|
int lineNumber) {
|
||||||
|
if (whitespace.length() > 0) {
|
||||||
|
Token t;
|
||||||
|
if (lastTokenWasSimpleValue) {
|
||||||
|
t = Tokens.newUnquotedText(
|
||||||
|
lineOrigin(baseOrigin, lineNumber),
|
||||||
|
whitespace.toString());
|
||||||
|
} else {
|
||||||
|
t = Tokens.newIgnoredWhitespace(lineOrigin(baseOrigin, lineNumber),
|
||||||
|
whitespace.toString());
|
||||||
|
}
|
||||||
|
whitespace.setLength(0); // reset
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final private SimpleConfigOrigin origin;
|
||||||
|
final private Reader input;
|
||||||
|
final private LinkedList<Integer> buffer;
|
||||||
|
private int lineNumber;
|
||||||
|
private ConfigOrigin lineOrigin;
|
||||||
|
final private Queue<Token> tokens;
|
||||||
|
final private WhitespaceSaver whitespaceSaver;
|
||||||
|
final private boolean allowComments;
|
||||||
|
|
||||||
|
TokenIterator(ConfigOrigin origin, Reader input, boolean allowComments) {
|
||||||
|
this.origin = (SimpleConfigOrigin) origin;
|
||||||
|
this.input = input;
|
||||||
|
this.allowComments = allowComments;
|
||||||
|
this.buffer = new LinkedList<Integer>();
|
||||||
|
lineNumber = 1;
|
||||||
|
lineOrigin = this.origin.withLineNumber(lineNumber);
|
||||||
|
tokens = new LinkedList<Token>();
|
||||||
|
tokens.add(Tokens.START);
|
||||||
|
whitespaceSaver = new WhitespaceSaver();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// this should ONLY be called from nextCharSkippingComments
|
||||||
|
// or when inside a quoted string, or when parsing a sequence
|
||||||
|
// like ${ or +=, everything else should use
|
||||||
|
// nextCharSkippingComments().
|
||||||
|
private int nextCharRaw() {
|
||||||
|
if (buffer.isEmpty()) {
|
||||||
|
try {
|
||||||
|
return input.read();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ConfigException.IO(origin, "read error: "
|
||||||
|
+ e.getMessage(), e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int c = buffer.pop();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putBack(int c) {
|
||||||
|
if (buffer.size() > 2) {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"bug: putBack() three times, undesirable look-ahead");
|
||||||
|
}
|
||||||
|
buffer.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isWhitespace(int c) {
|
||||||
|
return ConfigImplUtil.isWhitespace(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isWhitespaceNotNewline(int c) {
|
||||||
|
return c != '\n' && ConfigImplUtil.isWhitespace(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startOfComment(int c) {
|
||||||
|
if (c == -1) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (allowComments) {
|
||||||
|
if (c == '#') {
|
||||||
|
return true;
|
||||||
|
} else if (c == '/') {
|
||||||
|
int maybeSecondSlash = nextCharRaw();
|
||||||
|
// we want to predictably NOT consume any chars
|
||||||
|
putBack(maybeSecondSlash);
|
||||||
|
if (maybeSecondSlash == '/') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get next char, skipping non-newline whitespace
|
||||||
|
private int nextCharAfterWhitespace(WhitespaceSaver saver) {
|
||||||
|
for (;;) {
|
||||||
|
int c = nextCharRaw();
|
||||||
|
|
||||||
|
if (c == -1) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if (isWhitespaceNotNewline(c)) {
|
||||||
|
saver.add(c);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProblemException problem(String message) {
|
||||||
|
return problem("", message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProblemException problem(String what, String message) {
|
||||||
|
return problem(what, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProblemException problem(String what, String message, boolean suggestQuotes) {
|
||||||
|
return problem(what, message, suggestQuotes, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProblemException problem(String what, String message, Throwable cause) {
|
||||||
|
return problem(lineOrigin, what, message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProblemException problem(String what, String message, boolean suggestQuotes,
|
||||||
|
Throwable cause) {
|
||||||
|
return problem(lineOrigin, what, message, suggestQuotes, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProblemException problem(ConfigOrigin origin, String what,
|
||||||
|
String message,
|
||||||
|
Throwable cause) {
|
||||||
|
return problem(origin, what, message, false, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProblemException problem(ConfigOrigin origin, String what, String message,
|
||||||
|
boolean suggestQuotes, Throwable cause) {
|
||||||
|
if (what == null || message == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"internal error, creating bad ProblemException");
|
||||||
|
return new ProblemException(Tokens.newProblem(origin, what, message, suggestQuotes,
|
||||||
|
cause));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProblemException problem(ConfigOrigin origin, String message) {
|
||||||
|
return problem(origin, "", message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfigOrigin lineOrigin(ConfigOrigin baseOrigin,
|
||||||
|
int lineNumber) {
|
||||||
|
return ((SimpleConfigOrigin) baseOrigin).withLineNumber(lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ONE char has always been consumed, either the # or the first /, but
|
||||||
|
// not both slashes
|
||||||
|
private Token pullComment(int firstChar) {
|
||||||
|
boolean doubleSlash = false;
|
||||||
|
if (firstChar == '/') {
|
||||||
|
int discard = nextCharRaw();
|
||||||
|
if (discard != '/')
|
||||||
|
throw new ConfigException.BugOrBroken("called pullComment but // not seen");
|
||||||
|
doubleSlash = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (;;) {
|
||||||
|
int c = nextCharRaw();
|
||||||
|
if (c == -1 || c == '\n') {
|
||||||
|
putBack(c);
|
||||||
|
if (doubleSlash)
|
||||||
|
return Tokens.newCommentDoubleSlash(lineOrigin, sb.toString());
|
||||||
|
else
|
||||||
|
return Tokens.newCommentHash(lineOrigin, sb.toString());
|
||||||
|
} else {
|
||||||
|
sb.appendCodePoint(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chars JSON allows a number to start with
|
||||||
|
static final String firstNumberChars = "0123456789-";
|
||||||
|
// chars JSON allows to be part of a number
|
||||||
|
static final String numberChars = "0123456789eE+-.";
|
||||||
|
// chars that stop an unquoted string
|
||||||
|
static final String notInUnquotedText = "$\"{}[]:=,+#`^?!@*&\\";
|
||||||
|
|
||||||
|
// The rules here are intended to maximize convenience while
|
||||||
|
// avoiding confusion with real valid JSON. Basically anything
|
||||||
|
// that parses as JSON is treated the JSON way and otherwise
|
||||||
|
// we assume it's a string and let the parser sort it out.
|
||||||
|
private Token pullUnquotedText() {
|
||||||
|
ConfigOrigin origin = lineOrigin;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int c = nextCharRaw();
|
||||||
|
while (true) {
|
||||||
|
if (c == -1) {
|
||||||
|
break;
|
||||||
|
} else if (notInUnquotedText.indexOf(c) >= 0) {
|
||||||
|
break;
|
||||||
|
} else if (isWhitespace(c)) {
|
||||||
|
break;
|
||||||
|
} else if (startOfComment(c)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
sb.appendCodePoint(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we parse true/false/null tokens as such no matter
|
||||||
|
// what is after them, as long as they are at the
|
||||||
|
// start of the unquoted token.
|
||||||
|
if (sb.length() == 4) {
|
||||||
|
String s = sb.toString();
|
||||||
|
if (s.equals("true"))
|
||||||
|
return Tokens.newBoolean(origin, true);
|
||||||
|
else if (s.equals("null"))
|
||||||
|
return Tokens.newNull(origin);
|
||||||
|
} else if (sb.length() == 5) {
|
||||||
|
String s = sb.toString();
|
||||||
|
if (s.equals("false"))
|
||||||
|
return Tokens.newBoolean(origin, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
c = nextCharRaw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// put back the char that ended the unquoted text
|
||||||
|
putBack(c);
|
||||||
|
|
||||||
|
String s = sb.toString();
|
||||||
|
return Tokens.newUnquotedText(origin, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token pullNumber(int firstChar) throws ProblemException {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.appendCodePoint(firstChar);
|
||||||
|
boolean containedDecimalOrE = false;
|
||||||
|
int c = nextCharRaw();
|
||||||
|
while (c != -1 && numberChars.indexOf(c) >= 0) {
|
||||||
|
if (c == '.' || c == 'e' || c == 'E')
|
||||||
|
containedDecimalOrE = true;
|
||||||
|
sb.appendCodePoint(c);
|
||||||
|
c = nextCharRaw();
|
||||||
|
}
|
||||||
|
// the last character we looked at wasn't part of the number, put it
|
||||||
|
// back
|
||||||
|
putBack(c);
|
||||||
|
String s = sb.toString();
|
||||||
|
try {
|
||||||
|
if (containedDecimalOrE) {
|
||||||
|
// force floating point representation
|
||||||
|
return Tokens.newDouble(lineOrigin, Double.parseDouble(s), s);
|
||||||
|
} else {
|
||||||
|
// this should throw if the integer is too large for Long
|
||||||
|
return Tokens.newLong(lineOrigin, Long.parseLong(s), s);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// not a number after all, see if it's an unquoted string.
|
||||||
|
for (char u : s.toCharArray()) {
|
||||||
|
if (notInUnquotedText.indexOf(u) >= 0)
|
||||||
|
throw problem(asString(u), "Reserved character '" + asString(u)
|
||||||
|
+ "' is not allowed outside quotes", true /* suggestQuotes */);
|
||||||
|
}
|
||||||
|
// no evil chars so we just decide this was a string and
|
||||||
|
// not a number.
|
||||||
|
return Tokens.newUnquotedText(lineOrigin, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pullEscapeSequence(StringBuilder sb, StringBuilder sbOrig) throws ProblemException {
|
||||||
|
int escaped = nextCharRaw();
|
||||||
|
if (escaped == -1)
|
||||||
|
throw problem("End of input but backslash in string had nothing after it");
|
||||||
|
|
||||||
|
// This is needed so we return the unescaped escape characters back out when rendering
|
||||||
|
// the token
|
||||||
|
sbOrig.appendCodePoint('\\');
|
||||||
|
sbOrig.appendCodePoint(escaped);
|
||||||
|
|
||||||
|
switch (escaped) {
|
||||||
|
case '"':
|
||||||
|
sb.append('"');
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
sb.append('\\');
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
sb.append('/');
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
sb.append('\b');
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
sb.append('\f');
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
sb.append('\n');
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
sb.append('\r');
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
sb.append('\t');
|
||||||
|
break;
|
||||||
|
case 'u': {
|
||||||
|
// kind of absurdly slow, but screw it for now
|
||||||
|
char[] a = new char[4];
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
int c = nextCharRaw();
|
||||||
|
if (c == -1)
|
||||||
|
throw problem("End of input but expecting 4 hex digits for \\uXXXX escape");
|
||||||
|
a[i] = (char) c;
|
||||||
|
}
|
||||||
|
String digits = new String(a);
|
||||||
|
sbOrig.append(a);
|
||||||
|
try {
|
||||||
|
sb.appendCodePoint(Integer.parseInt(digits, 16));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw problem(digits, String.format(
|
||||||
|
"Malformed hex digits after \\u escape in string: '%s'", digits), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw problem(
|
||||||
|
asString(escaped),
|
||||||
|
String.format(
|
||||||
|
"backslash followed by '%s', this is not a valid escape sequence (quoted strings use JSON escaping, so use double-backslash \\\\ for literal backslash)",
|
||||||
|
asString(escaped)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendTripleQuotedString(StringBuilder sb, StringBuilder sbOrig) throws ProblemException {
|
||||||
|
// we are after the opening triple quote and need to consume the
|
||||||
|
// close triple
|
||||||
|
int consecutiveQuotes = 0;
|
||||||
|
for (;;) {
|
||||||
|
int c = nextCharRaw();
|
||||||
|
|
||||||
|
if (c == '"') {
|
||||||
|
consecutiveQuotes += 1;
|
||||||
|
} else if (consecutiveQuotes >= 3) {
|
||||||
|
// the last three quotes end the string and the others are
|
||||||
|
// kept.
|
||||||
|
sb.setLength(sb.length() - 3);
|
||||||
|
putBack(c);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
consecutiveQuotes = 0;
|
||||||
|
if (c == -1)
|
||||||
|
throw problem("End of input but triple-quoted string was still open");
|
||||||
|
else if (c == '\n') {
|
||||||
|
// keep the line number accurate
|
||||||
|
lineNumber += 1;
|
||||||
|
lineOrigin = origin.withLineNumber(lineNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.appendCodePoint(c);
|
||||||
|
sbOrig.appendCodePoint(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token pullQuotedString() throws ProblemException {
|
||||||
|
// the open quote has already been consumed
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
// We need a second string builder to keep track of escape characters.
|
||||||
|
// We want to return them exactly as they appeared in the original text,
|
||||||
|
// which means we will need a new StringBuilder to escape escape characters
|
||||||
|
// so we can also keep the actual value of the string. This is gross.
|
||||||
|
StringBuilder sbOrig = new StringBuilder();
|
||||||
|
sbOrig.appendCodePoint('"');
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int c = nextCharRaw();
|
||||||
|
if (c == -1)
|
||||||
|
throw problem("End of input but string quote was still open");
|
||||||
|
|
||||||
|
if (c == '\\') {
|
||||||
|
pullEscapeSequence(sb, sbOrig);
|
||||||
|
} else if (c == '"') {
|
||||||
|
sbOrig.appendCodePoint(c);
|
||||||
|
break;
|
||||||
|
} else if (ConfigImplUtil.isC0Control(c)) {
|
||||||
|
throw problem(asString(c), "JSON does not allow unescaped " + asString(c)
|
||||||
|
+ " in quoted strings, use a backslash escape");
|
||||||
|
} else {
|
||||||
|
sb.appendCodePoint(c);
|
||||||
|
sbOrig.appendCodePoint(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe switch to triple-quoted string, sort of hacky...
|
||||||
|
if (sb.length() == 0) {
|
||||||
|
int third = nextCharRaw();
|
||||||
|
if (third == '"') {
|
||||||
|
sbOrig.appendCodePoint(third);
|
||||||
|
appendTripleQuotedString(sb, sbOrig);
|
||||||
|
} else {
|
||||||
|
putBack(third);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return Tokens.newString(lineOrigin, sb.toString(), sbOrig.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token pullPlusEquals() throws ProblemException {
|
||||||
|
// the initial '+' has already been consumed
|
||||||
|
int c = nextCharRaw();
|
||||||
|
if (c != '=') {
|
||||||
|
throw problem(asString(c), "'+' not followed by =, '" + asString(c)
|
||||||
|
+ "' not allowed after '+'", true /* suggestQuotes */);
|
||||||
|
}
|
||||||
|
return Tokens.PLUS_EQUALS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token pullSubstitution() throws ProblemException {
|
||||||
|
// the initial '$' has already been consumed
|
||||||
|
ConfigOrigin origin = lineOrigin;
|
||||||
|
int c = nextCharRaw();
|
||||||
|
if (c != '{') {
|
||||||
|
throw problem(asString(c), "'$' not followed by {, '" + asString(c)
|
||||||
|
+ "' not allowed after '$'", true /* suggestQuotes */);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean optional = false;
|
||||||
|
c = nextCharRaw();
|
||||||
|
if (c == '?') {
|
||||||
|
optional = true;
|
||||||
|
} else {
|
||||||
|
putBack(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
WhitespaceSaver saver = new WhitespaceSaver();
|
||||||
|
List<Token> expression = new ArrayList<Token>();
|
||||||
|
|
||||||
|
Token t;
|
||||||
|
do {
|
||||||
|
t = pullNextToken(saver);
|
||||||
|
|
||||||
|
// note that we avoid validating the allowed tokens inside
|
||||||
|
// the substitution here; we even allow nested substitutions
|
||||||
|
// in the tokenizer. The parser sorts it out.
|
||||||
|
if (t == Tokens.CLOSE_CURLY) {
|
||||||
|
// end the loop, done!
|
||||||
|
break;
|
||||||
|
} else if (t == Tokens.END) {
|
||||||
|
throw problem(origin,
|
||||||
|
"Substitution ${ was not closed with a }");
|
||||||
|
} else {
|
||||||
|
Token whitespace = saver.check(t, origin, lineNumber);
|
||||||
|
if (whitespace != null)
|
||||||
|
expression.add(whitespace);
|
||||||
|
expression.add(t);
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
return Tokens.newSubstitution(origin, optional, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token pullNextToken(WhitespaceSaver saver) throws ProblemException {
|
||||||
|
int c = nextCharAfterWhitespace(saver);
|
||||||
|
if (c == -1) {
|
||||||
|
return Tokens.END;
|
||||||
|
} else if (c == '\n') {
|
||||||
|
// newline tokens have the just-ended line number
|
||||||
|
Token line = Tokens.newLine(lineOrigin);
|
||||||
|
lineNumber += 1;
|
||||||
|
lineOrigin = origin.withLineNumber(lineNumber);
|
||||||
|
return line;
|
||||||
|
} else {
|
||||||
|
Token t;
|
||||||
|
if (startOfComment(c)) {
|
||||||
|
t = pullComment(c);
|
||||||
|
} else {
|
||||||
|
switch (c) {
|
||||||
|
case '"':
|
||||||
|
t = pullQuotedString();
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
t = pullSubstitution();
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
t = Tokens.COLON;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
t = Tokens.COMMA;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
t = Tokens.EQUALS;
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
t = Tokens.OPEN_CURLY;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
t = Tokens.CLOSE_CURLY;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
t = Tokens.OPEN_SQUARE;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
t = Tokens.CLOSE_SQUARE;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
t = pullPlusEquals();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
t = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == null) {
|
||||||
|
if (firstNumberChars.indexOf(c) >= 0) {
|
||||||
|
t = pullNumber(c);
|
||||||
|
} else if (notInUnquotedText.indexOf(c) >= 0) {
|
||||||
|
throw problem(asString(c), "Reserved character '" + asString(c)
|
||||||
|
+ "' is not allowed outside quotes", true /* suggestQuotes */);
|
||||||
|
} else {
|
||||||
|
putBack(c);
|
||||||
|
t = pullUnquotedText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == null)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"bug: failed to generate next token");
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSimpleValue(Token t) {
|
||||||
|
if (Tokens.isSubstitution(t) || Tokens.isUnquotedText(t)
|
||||||
|
|| Tokens.isValue(t)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueNextToken() throws ProblemException {
|
||||||
|
Token t = pullNextToken(whitespaceSaver);
|
||||||
|
Token whitespace = whitespaceSaver.check(t, origin, lineNumber);
|
||||||
|
if (whitespace != null)
|
||||||
|
tokens.add(whitespace);
|
||||||
|
|
||||||
|
tokens.add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return !tokens.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Token next() {
|
||||||
|
Token t = tokens.remove();
|
||||||
|
if (tokens.isEmpty() && t != Tokens.END) {
|
||||||
|
try {
|
||||||
|
queueNextToken();
|
||||||
|
} catch (ProblemException e) {
|
||||||
|
tokens.add(e.problem());
|
||||||
|
}
|
||||||
|
if (tokens.isEmpty())
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"bug: tokens queue should not be empty here");
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Does not make sense to remove items from token stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,521 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
|
||||||
|
*/
|
||||||
|
package com.drtshock.playervaults.lib.com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigException;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigOrigin;
|
||||||
|
import com.drtshock.playervaults.lib.com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
|
/* FIXME the way the subclasses of Token are private with static isFoo and accessors is kind of ridiculous. */
|
||||||
|
final class Tokens {
|
||||||
|
static private class Value extends Token {
|
||||||
|
|
||||||
|
final private AbstractConfigValue value;
|
||||||
|
|
||||||
|
Value(AbstractConfigValue value) {
|
||||||
|
this(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value(AbstractConfigValue value, String origText) {
|
||||||
|
super(TokenType.VALUE, value.origin(), origText);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractConfigValue value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (value().resolveStatus() == ResolveStatus.RESOLVED)
|
||||||
|
return "'" + value().unwrapped() + "' (" + value.valueType().name() + ")";
|
||||||
|
else
|
||||||
|
return "'<unresolved value>' (" + value.valueType().name() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return super.equals(other) && ((Value) other).value.equals(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 41 * (41 + super.hashCode()) + value.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private class Line extends Token {
|
||||||
|
Line(ConfigOrigin origin) {
|
||||||
|
super(TokenType.NEWLINE, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "'\\n'@" + lineNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof Line;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return super.equals(other) && ((Line) other).lineNumber() == lineNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 41 * (41 + super.hashCode()) + lineNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tokenText() {
|
||||||
|
return "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not a Value, because it requires special processing
|
||||||
|
static private class UnquotedText extends Token {
|
||||||
|
final private String value;
|
||||||
|
|
||||||
|
UnquotedText(ConfigOrigin origin, String s) {
|
||||||
|
super(TokenType.UNQUOTED_TEXT, origin);
|
||||||
|
this.value = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "'" + value + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof UnquotedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return super.equals(other)
|
||||||
|
&& ((UnquotedText) other).value.equals(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 41 * (41 + super.hashCode()) + value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tokenText() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private class IgnoredWhitespace extends Token {
|
||||||
|
final private String value;
|
||||||
|
|
||||||
|
IgnoredWhitespace(ConfigOrigin origin, String s) {
|
||||||
|
super(TokenType.IGNORED_WHITESPACE, origin);
|
||||||
|
this.value = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() { return "'" + value + "' (WHITESPACE)"; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof IgnoredWhitespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return super.equals(other)
|
||||||
|
&& ((IgnoredWhitespace) other).value.equals(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 41 * (41 + super.hashCode()) + value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tokenText() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private class Problem extends Token {
|
||||||
|
final private String what;
|
||||||
|
final private String message;
|
||||||
|
final private boolean suggestQuotes;
|
||||||
|
final private Throwable cause;
|
||||||
|
|
||||||
|
Problem(ConfigOrigin origin, String what, String message, boolean suggestQuotes,
|
||||||
|
Throwable cause) {
|
||||||
|
super(TokenType.PROBLEM, origin);
|
||||||
|
this.what = what;
|
||||||
|
this.message = message;
|
||||||
|
this.suggestQuotes = suggestQuotes;
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
String what() {
|
||||||
|
return what;
|
||||||
|
}
|
||||||
|
|
||||||
|
String message() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean suggestQuotes() {
|
||||||
|
return suggestQuotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Throwable cause() {
|
||||||
|
return cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append('\'');
|
||||||
|
sb.append(what);
|
||||||
|
sb.append('\'');
|
||||||
|
sb.append(" (");
|
||||||
|
sb.append(message);
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof Problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return super.equals(other) && ((Problem) other).what.equals(what)
|
||||||
|
&& ((Problem) other).message.equals(message)
|
||||||
|
&& ((Problem) other).suggestQuotes == suggestQuotes
|
||||||
|
&& ConfigImplUtil.equalsHandlingNull(((Problem) other).cause, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = 41 * (41 + super.hashCode());
|
||||||
|
h = 41 * (h + what.hashCode());
|
||||||
|
h = 41 * (h + message.hashCode());
|
||||||
|
h = 41 * (h + Boolean.valueOf(suggestQuotes).hashCode());
|
||||||
|
if (cause != null)
|
||||||
|
h = 41 * (h + cause.hashCode());
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private abstract class Comment extends Token {
|
||||||
|
final private String text;
|
||||||
|
|
||||||
|
Comment(ConfigOrigin origin, String text) {
|
||||||
|
super(TokenType.COMMENT, origin);
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
final static class DoubleSlashComment extends Comment {
|
||||||
|
DoubleSlashComment(ConfigOrigin origin, String text) {
|
||||||
|
super(origin, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tokenText() {
|
||||||
|
return "//" + super.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final static class HashComment extends Comment {
|
||||||
|
HashComment(ConfigOrigin origin, String text) {
|
||||||
|
super(origin, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tokenText() {
|
||||||
|
return "#" + super.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String text() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("'#");
|
||||||
|
sb.append(text);
|
||||||
|
sb.append("' (COMMENT)");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof Comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return super.equals(other) && ((Comment) other).text.equals(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = 41 * (41 + super.hashCode());
|
||||||
|
h = 41 * (h + text.hashCode());
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not a Value, because it requires special processing
|
||||||
|
static private class Substitution extends Token {
|
||||||
|
final private boolean optional;
|
||||||
|
final private List<Token> value;
|
||||||
|
|
||||||
|
Substitution(ConfigOrigin origin, boolean optional, List<Token> expression) {
|
||||||
|
super(TokenType.SUBSTITUTION, origin);
|
||||||
|
this.optional = optional;
|
||||||
|
this.value = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean optional() {
|
||||||
|
return optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Token> value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tokenText() {
|
||||||
|
return "${" + (this.optional? "?" : "") + Tokenizer.render(this.value.iterator()) + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Token t : value) {
|
||||||
|
sb.append(t.toString());
|
||||||
|
}
|
||||||
|
return "'${" + sb.toString() + "}'";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canEqual(Object other) {
|
||||||
|
return other instanceof Substitution;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return super.equals(other)
|
||||||
|
&& ((Substitution) other).value.equals(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 41 * (41 + super.hashCode()) + value.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isValue(Token token) {
|
||||||
|
return token instanceof Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigValue getValue(Token token) {
|
||||||
|
if (token instanceof Value) {
|
||||||
|
return ((Value) token).value();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"tried to get value of non-value token " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isValueWithType(Token t, ConfigValueType valueType) {
|
||||||
|
return isValue(t) && getValue(t).valueType() == valueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isNewline(Token token) {
|
||||||
|
return token instanceof Line;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isProblem(Token token) {
|
||||||
|
return token instanceof Problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getProblemWhat(Token token) {
|
||||||
|
if (token instanceof Problem) {
|
||||||
|
return ((Problem) token).what();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("tried to get problem what from " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getProblemMessage(Token token) {
|
||||||
|
if (token instanceof Problem) {
|
||||||
|
return ((Problem) token).message();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("tried to get problem message from " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean getProblemSuggestQuotes(Token token) {
|
||||||
|
if (token instanceof Problem) {
|
||||||
|
return ((Problem) token).suggestQuotes();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("tried to get problem suggestQuotes from "
|
||||||
|
+ token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Throwable getProblemCause(Token token) {
|
||||||
|
if (token instanceof Problem) {
|
||||||
|
return ((Problem) token).cause();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("tried to get problem cause from " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isComment(Token token) {
|
||||||
|
return token instanceof Comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getCommentText(Token token) {
|
||||||
|
if (token instanceof Comment) {
|
||||||
|
return ((Comment) token).text();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("tried to get comment text from " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isUnquotedText(Token token) {
|
||||||
|
return token instanceof UnquotedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getUnquotedText(Token token) {
|
||||||
|
if (token instanceof UnquotedText) {
|
||||||
|
return ((UnquotedText) token).value();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"tried to get unquoted text from " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isIgnoredWhitespace(Token token) {
|
||||||
|
return token instanceof IgnoredWhitespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isSubstitution(Token token) {
|
||||||
|
return token instanceof Substitution;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Token> getSubstitutionPathExpression(Token token) {
|
||||||
|
if (token instanceof Substitution) {
|
||||||
|
return ((Substitution) token).value();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"tried to get substitution from " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean getSubstitutionOptional(Token token) {
|
||||||
|
if (token instanceof Substitution) {
|
||||||
|
return ((Substitution) token).optional();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("tried to get substitution optionality from "
|
||||||
|
+ token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final static Token START = Token.newWithoutOrigin(TokenType.START, "start of file", "");
|
||||||
|
final static Token END = Token.newWithoutOrigin(TokenType.END, "end of file", "");
|
||||||
|
final static Token COMMA = Token.newWithoutOrigin(TokenType.COMMA, "','", ",");
|
||||||
|
final static Token EQUALS = Token.newWithoutOrigin(TokenType.EQUALS, "'='", "=");
|
||||||
|
final static Token COLON = Token.newWithoutOrigin(TokenType.COLON, "':'", ":");
|
||||||
|
final static Token OPEN_CURLY = Token.newWithoutOrigin(TokenType.OPEN_CURLY, "'{'", "{");
|
||||||
|
final static Token CLOSE_CURLY = Token.newWithoutOrigin(TokenType.CLOSE_CURLY, "'}'", "}");
|
||||||
|
final static Token OPEN_SQUARE = Token.newWithoutOrigin(TokenType.OPEN_SQUARE, "'['", "[");
|
||||||
|
final static Token CLOSE_SQUARE = Token.newWithoutOrigin(TokenType.CLOSE_SQUARE, "']'", "]");
|
||||||
|
final static Token PLUS_EQUALS = Token.newWithoutOrigin(TokenType.PLUS_EQUALS, "'+='", "+=");
|
||||||
|
|
||||||
|
static Token newLine(ConfigOrigin origin) {
|
||||||
|
return new Line(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newProblem(ConfigOrigin origin, String what, String message,
|
||||||
|
boolean suggestQuotes, Throwable cause) {
|
||||||
|
return new Problem(origin, what, message, suggestQuotes, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newCommentDoubleSlash(ConfigOrigin origin, String text) {
|
||||||
|
return new Comment.DoubleSlashComment(origin, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newCommentHash(ConfigOrigin origin, String text) {
|
||||||
|
return new Comment.HashComment(origin, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newUnquotedText(ConfigOrigin origin, String s) {
|
||||||
|
return new UnquotedText(origin, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newIgnoredWhitespace(ConfigOrigin origin, String s) {
|
||||||
|
return new IgnoredWhitespace(origin, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newSubstitution(ConfigOrigin origin, boolean optional, List<Token> expression) {
|
||||||
|
return new Substitution(origin, optional, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newValue(AbstractConfigValue value) {
|
||||||
|
return new Value(value);
|
||||||
|
}
|
||||||
|
static Token newValue(AbstractConfigValue value, String origText) {
|
||||||
|
return new Value(value, origText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newString(ConfigOrigin origin, String value, String origText) {
|
||||||
|
return newValue(new ConfigString.Quoted(origin, value), origText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newInt(ConfigOrigin origin, int value, String origText) {
|
||||||
|
return newValue(ConfigNumber.newNumber(origin, value,
|
||||||
|
origText), origText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newDouble(ConfigOrigin origin, double value,
|
||||||
|
String origText) {
|
||||||
|
return newValue(ConfigNumber.newNumber(origin, value,
|
||||||
|
origText), origText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newLong(ConfigOrigin origin, long value, String origText) {
|
||||||
|
return newValue(ConfigNumber.newNumber(origin, value,
|
||||||
|
origText), origText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newNull(ConfigOrigin origin) {
|
||||||
|
return newValue(new ConfigNull(origin), "null");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token newBoolean(ConfigOrigin origin, boolean value) {
|
||||||
|
return newValue(new ConfigBoolean(origin, value), "" + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user