package edu.uw.zookeeper.common; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.MoreObjects; import com.google.common.base.Optional; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigUtil; import com.typesafe.config.ConfigValueFactory; public class Configuration { protected static String CONFIG_PATH = "edu.uw.zookeeper"; public static String getConfigPath() { return CONFIG_PATH; } protected static Config loadDefaults() { Config config = ConfigFactory.load(); config = config.hasPath(CONFIG_PATH) ? config.getConfig(CONFIG_PATH) : ConfigFactory.empty(); return config; } public static Config defaultConfig() { return DefaultConfig.DEFAULT.get(); } protected static enum DefaultConfig implements Singleton<Config> { DEFAULT(loadDefaults()); private final Config instance; private DefaultConfig(Config instance) { this.instance = instance; } @Override public Config get() { return instance; } } public static Configuration defaults(Arguments arguments) { return create(arguments, DefaultConfig.DEFAULT.get()); } public static Configuration createEmpty() { return create(SimpleArguments.defaults(), ConfigFactory.empty()); } public static Configuration fromConfiguration(Configuration configuration) { return create(configuration.getArguments(), configuration.getConfig()); } public static Configuration create(Arguments arguments, Config config) { return new Configuration(arguments, config); } private final Arguments arguments; private Config config; protected Configuration(Arguments arguments, Config config) { this.arguments = arguments; this.config = config; } public Arguments getArguments() { return arguments; } public synchronized Config getConfig() { return config; } public synchronized Configuration withConfig(Config updates) { this.config = updates.withFallback(this.config); return this; } public synchronized Config getConfigOrEmpty(String path) { Config config = this.config; if (! path.isEmpty()) { if (config.hasPath(path)) { config = config.getConfig(path); } else { config = ConfigFactory.empty(); } } return config; } public synchronized Configuration withConfigurable(Configurable configurable) { String key = configurable.key(); String arg = configurable.arg(); if (key.isEmpty()) { key = arg; } checkArgument(!key.isEmpty(), configurable.toString()); if (! configurable.value().isEmpty()) { String path = configurable.path().isEmpty() ? key : ConfigUtil.joinPath(configurable.path(), key); if (! config.hasPath(path)) { Config update = ConfigValueFactory.fromAnyRef(configurable.value()).atKey(key); if (! configurable.path().isEmpty()) { update = update.atPath(configurable.path()); } withConfig(update); } } if (! arg.isEmpty()) { Optional<String> help = Optional.of( configurable.help().isEmpty() ? configurable.type().toString() : configurable.help()); Optional<String> defaultValue = Optional.fromNullable( configurable.value().isEmpty() ? null : configurable.value()); arguments.addOption(arg, help, defaultValue); arguments.parse(); if (arguments.hasValue(arg)) { String value = arguments.getValue(arg); if (! defaultValue.isPresent() || ! defaultValue.get().equals(value)) { Config update = ConfigValueFactory.fromAnyRef(value).atKey(key); if (! configurable.path().isEmpty()) { update = update.atPath(configurable.path()); } withConfig(update); } } } return this; } @Override public synchronized String toString() { return MoreObjects.toStringHelper(this) .add("config", config) .add("arguments", arguments).toString(); } }