move Settings, SettingsBuilder, SettingsLoader to API package, make SettingsBuilder to be loadable by ServiceLoader
This commit is contained in:
parent
823b15d3a9
commit
9411e901be
57 changed files with 1381 additions and 2396 deletions
3
content-api/build.gradle
Normal file
3
content-api/build.gradle
Normal file
|
@ -0,0 +1,3 @@
|
|||
dependencies {
|
||||
api "org.xbib:datastructures-api:${project.property('xbib-datastructures.version')}"
|
||||
}
|
|
@ -1,3 +1,8 @@
|
|||
module org.xbib.content {
|
||||
import org.xbib.content.SettingsBuilder;
|
||||
|
||||
module org.xbib.content.api {
|
||||
exports org.xbib.content;
|
||||
}
|
||||
requires transitive org.xbib.datastructures.api;
|
||||
requires java.sql;
|
||||
uses SettingsBuilder;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.content.settings;
|
||||
package org.xbib.content;
|
||||
|
||||
/**
|
||||
* Strategy interface used to resolve replacement values for placeholders contained in Strings.
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
package org.xbib.content;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
65
content-api/src/main/java/org/xbib/content/Settings.java
Normal file
65
content-api/src/main/java/org/xbib/content/Settings.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
package org.xbib.content;
|
||||
|
||||
import org.xbib.datastructures.api.ByteSizeValue;
|
||||
import org.xbib.datastructures.api.TimeValue;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
public interface Settings extends AutoCloseable {
|
||||
|
||||
class Holder {
|
||||
|
||||
private static SettingsBuilder createBuilder() {
|
||||
ServiceLoader<SettingsBuilder> serviceLoader = ServiceLoader.load(SettingsBuilder.class);
|
||||
Optional<SettingsBuilder> optionalConfigLogger = serviceLoader.findFirst();
|
||||
return optionalConfigLogger.orElse(null);
|
||||
}
|
||||
|
||||
private static final Settings emptySettings = createBuilder().build();
|
||||
}
|
||||
|
||||
static SettingsBuilder settingsBuilder() {
|
||||
return Holder.createBuilder();
|
||||
}
|
||||
|
||||
static Settings emptySettings() {
|
||||
return Holder.emptySettings;
|
||||
}
|
||||
|
||||
boolean isEmpty();
|
||||
|
||||
String get(String setting);
|
||||
|
||||
String get(String setting, String defaultValue);
|
||||
|
||||
Float getAsFloat(String setting, Float defaultValue);
|
||||
|
||||
Double getAsDouble(String setting, Double defaultValue);
|
||||
|
||||
Integer getAsInt(String setting, Integer defaultValue);
|
||||
|
||||
Long getAsLong(String setting, Long defaultValue);
|
||||
|
||||
Boolean getAsBoolean(String setting, Boolean defaultValue);
|
||||
|
||||
TimeValue getAsTime(String setting, TimeValue defaultValue);
|
||||
|
||||
ByteSizeValue getAsBytesSize(String setting, ByteSizeValue defaultValue);
|
||||
|
||||
String[] getAsArray(String settingPrefix);
|
||||
|
||||
String[] getAsArray(String settingPrefix, String[] defaultArray);
|
||||
|
||||
Map<String, String> getAsMap();
|
||||
|
||||
Map<String, Object> getAsStructuredMap();
|
||||
|
||||
Map<String, Settings> getGroups(String prefix);
|
||||
|
||||
Settings getAsSettings(String setting);
|
||||
|
||||
Settings getByPrefix(String prefix);
|
||||
|
||||
boolean containsSetting(String setting);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package org.xbib.content;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public interface SettingsBuilder {
|
||||
|
||||
SettingsBuilder put(String setting, String value);
|
||||
|
||||
SettingsBuilder put(String setting, Class<?> clazz);
|
||||
|
||||
SettingsBuilder put(String setting, boolean value);
|
||||
|
||||
SettingsBuilder put(String setting, int value);
|
||||
|
||||
SettingsBuilder put(String setting, long value);
|
||||
|
||||
SettingsBuilder put(String setting, float value);
|
||||
|
||||
SettingsBuilder put(String setting, double value);
|
||||
|
||||
SettingsBuilder putArray(String setting, String... values);
|
||||
|
||||
SettingsBuilder putArray(String setting, List<String> values);
|
||||
|
||||
SettingsBuilder put(String settingPrefix, String groupName, String[] settings, String[] values)
|
||||
throws SettingsException;
|
||||
|
||||
SettingsBuilder put(Settings settings);
|
||||
|
||||
SettingsBuilder put(Map<String, String> settings);
|
||||
|
||||
SettingsBuilder loadFromString(String source);
|
||||
|
||||
SettingsBuilder loadFromResource(String resourceName, InputStream inputStream) throws SettingsException;
|
||||
|
||||
default SettingsBuilder fromJdbcConfTable(Connection connection, String id, String type) throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("select key, value from conf where id = ? and type = ?",
|
||||
new String[]{id, type}); ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
String key = resultSet.getString("key");
|
||||
String value = resultSet.getString("value");
|
||||
put(key, value);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
SettingsBuilder loadFromSystemProperties();
|
||||
|
||||
SettingsBuilder loadFromSystemEnvironment();
|
||||
|
||||
SettingsBuilder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder,
|
||||
PlaceholderResolver placeholderResolver);
|
||||
|
||||
SettingsBuilder replacePropertyPlaceholders();
|
||||
|
||||
SettingsBuilder setRefresh(Path path, long initialDelay, long period, TimeUnit timeUnit);
|
||||
|
||||
Settings build();
|
||||
|
||||
boolean isEmpty();
|
||||
|
||||
Map<String, String> map();
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
package org.xbib.content;
|
||||
|
||||
/**
|
||||
* A generic failure to handle settings.
|
|
@ -1,9 +1,7 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -69,17 +67,4 @@ public final class SettingsLoaderService {
|
|||
}
|
||||
return suffixes;
|
||||
}
|
||||
|
||||
public static Settings fromJdbcConfTable(Connection connection, String id, String type) throws SQLException {
|
||||
Settings.Builder settingsBuilder = Settings.settingsBuilder();
|
||||
try (PreparedStatement statement = connection.prepareStatement("select key, value from conf where id = ? and type = ?",
|
||||
new String[]{id, type}); ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
String key = resultSet.getString("key");
|
||||
String value = resultSet.getString("value");
|
||||
settingsBuilder.put(key, value);
|
||||
}
|
||||
}
|
||||
return settingsBuilder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Content API.
|
||||
*/
|
||||
package org.xbib.content;
|
|
@ -1,10 +1,11 @@
|
|||
import org.xbib.content.config.ConfigLogger;
|
||||
import org.xbib.content.settings.datastructures.SettingsLoader;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
|
||||
module org.xbib.content.config {
|
||||
exports org.xbib.content.config;
|
||||
uses ConfigLogger;
|
||||
uses SettingsLoader;
|
||||
provides ConfigLogger with org.xbib.content.config.SystemConfigLogger;
|
||||
requires org.xbib.content.api;
|
||||
requires transitive org.xbib.content.settings.datastructures;
|
||||
}
|
||||
|
|
|
@ -161,7 +161,8 @@ public class ConfigFinder {
|
|||
return Files.getLastModifiedTime(p);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}});
|
||||
}
|
||||
});
|
||||
|
||||
private static final Comparator<Path> PATH_NAME_COMPARATOR = Comparator.comparing(Path::toString);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package org.xbib.content.config;
|
||||
|
||||
import org.xbib.content.settings.datastructures.SettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.Settings;
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.SettingsBuilder;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.SettingsLoaderService;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -42,11 +42,12 @@ public class ConfigLoader {
|
|||
Optional<ConfigLogger> optionalConfigLogger = serviceLoader.findFirst();
|
||||
return optionalConfigLogger.orElse(new SystemConfigLogger());
|
||||
}
|
||||
static ConfigLoader LOADER = new ConfigLoader().withLogger(createConfigLogger());
|
||||
|
||||
private static final ConfigLoader configLoader = new ConfigLoader().withLogger(createConfigLogger());
|
||||
}
|
||||
|
||||
public static ConfigLoader getInstance() {
|
||||
return Holder.LOADER;
|
||||
return Holder.configLoader;
|
||||
}
|
||||
|
||||
public ConfigLoader withLogger(ConfigLogger logger) {
|
||||
|
@ -59,8 +60,8 @@ public class ConfigLoader {
|
|||
return map.get(configParams);
|
||||
}
|
||||
|
||||
private Settings.Builder internalLoad(ConfigParams params) throws ConfigException {
|
||||
Settings.Builder settings = Settings.settingsBuilder();
|
||||
private SettingsBuilder internalLoad(ConfigParams params) throws ConfigException {
|
||||
SettingsBuilder settings = Settings.settingsBuilder();
|
||||
if (params.withSystemEnvironment) {
|
||||
settings.loadFromSystemEnvironment();
|
||||
}
|
||||
|
@ -74,7 +75,7 @@ public class ConfigLoader {
|
|||
}
|
||||
if (!params.reader.isEmpty()) {
|
||||
for (ConfigParams.SuffixedReader reader : params.reader) {
|
||||
Settings.Builder readerSettings = createSettingsFromReader(reader.reader, reader.suffix);
|
||||
SettingsBuilder readerSettings = createSettingsFromReader(reader.reader, reader.suffix);
|
||||
if (readerSettings != null) {
|
||||
settings.put(readerSettings.build());
|
||||
if (!params.includeAll) {
|
||||
|
@ -84,7 +85,7 @@ public class ConfigLoader {
|
|||
}
|
||||
}
|
||||
if (params.args != null) {
|
||||
Settings.Builder argsSettings = createSettingsFromArgs(params);
|
||||
SettingsBuilder argsSettings = createSettingsFromArgs(params);
|
||||
if (argsSettings != null) {
|
||||
settings.put(argsSettings.build());
|
||||
if (!params.includeAll) {
|
||||
|
@ -93,7 +94,7 @@ public class ConfigLoader {
|
|||
}
|
||||
}
|
||||
if (params.withStdin) {
|
||||
Settings.Builder stdinSettings = createSettingsFromStdin();
|
||||
SettingsBuilder stdinSettings = createSettingsFromStdin();
|
||||
if (stdinSettings != null) {
|
||||
settings.put(stdinSettings.build());
|
||||
if (!params.includeAll) {
|
||||
|
@ -102,7 +103,7 @@ public class ConfigLoader {
|
|||
}
|
||||
}
|
||||
if (!params.fileLocations.isEmpty()) {
|
||||
Settings.Builder fileSettings = createSettingsFromFile(params.fileLocations);
|
||||
SettingsBuilder fileSettings = createSettingsFromFile(params.fileLocations);
|
||||
if (fileSettings != null) {
|
||||
settings.put(fileSettings.build());
|
||||
if (!params.includeAll) {
|
||||
|
@ -112,7 +113,7 @@ public class ConfigLoader {
|
|||
}
|
||||
if (!params.fileNamesWithoutSuffix.isEmpty()) {
|
||||
for (String fileNameWithoutSuffix : params.fileNamesWithoutSuffix) {
|
||||
Settings.Builder fileSettings = createSettingsFromFile(createListOfLocations(params, fileNameWithoutSuffix));
|
||||
SettingsBuilder fileSettings = createSettingsFromFile(createListOfLocations(params, fileNameWithoutSuffix));
|
||||
if (fileSettings != null) {
|
||||
settings.put(fileSettings.build());
|
||||
if (!params.includeAll) {
|
||||
|
@ -124,7 +125,7 @@ public class ConfigLoader {
|
|||
if (params.classLoaders != null) {
|
||||
for (ClassLoader cl : params.classLoaders) {
|
||||
if (cl != null) {
|
||||
Settings.Builder classpathSettings = createClasspathSettings(params, cl, fileNameWithoutSuffix);
|
||||
SettingsBuilder classpathSettings = createClasspathSettings(params, cl, fileNameWithoutSuffix);
|
||||
if (classpathSettings != null) {
|
||||
settings.put(classpathSettings.build());
|
||||
if (!params.includeAll) {
|
||||
|
@ -142,14 +143,15 @@ public class ConfigLoader {
|
|||
throw new ConfigException("no config found");
|
||||
}
|
||||
|
||||
private Settings.Builder createSettingsFromArgs(ConfigParams params) throws ConfigException {
|
||||
private SettingsBuilder createSettingsFromArgs(ConfigParams params) throws ConfigException {
|
||||
if (!params.fileNamesWithoutSuffix.isEmpty() && params.args != null) {
|
||||
for (String fileNameWithoutSuffix : params.fileNamesWithoutSuffix) {
|
||||
for (String suffix : SettingsLoaderService.getInstance().getSuffixes()) {
|
||||
for (int i = 0; i < params.args.size() - 1; i++) {
|
||||
String arg = params.args.get(i);
|
||||
String s = params.directoryName != null ?
|
||||
"--" + params.directoryName + "-" + fileNameWithoutSuffix + suffix : "--" + fileNameWithoutSuffix + suffix;
|
||||
"--" + params.directoryName + "-" + fileNameWithoutSuffix + suffix :
|
||||
"--" + fileNameWithoutSuffix + suffix;
|
||||
if (arg.equals(s)) {
|
||||
return createSettingsFromReader(new StringReader(params.args.get(i + 1)), suffix);
|
||||
}
|
||||
|
@ -160,7 +162,7 @@ public class ConfigLoader {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Settings.Builder createSettingsFromStdin() throws ConfigException {
|
||||
private SettingsBuilder createSettingsFromStdin() throws ConfigException {
|
||||
if (System.in != null) {
|
||||
try {
|
||||
int numBytesWaiting = System.in.available();
|
||||
|
@ -175,8 +177,8 @@ public class ConfigLoader {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Settings.Builder createSettingsFromFile(List<String> settingsFileNames) throws ConfigException {
|
||||
Settings.Builder settings = Settings.settingsBuilder();
|
||||
private SettingsBuilder createSettingsFromFile(List<String> settingsFileNames) throws ConfigException {
|
||||
SettingsBuilder settings = Settings.settingsBuilder();
|
||||
for (String settingsFileName: settingsFileNames) {
|
||||
int pos = settingsFileName.lastIndexOf('.');
|
||||
String suffix = (pos > 0 ? settingsFileName.substring(pos + 1) : "").toLowerCase(Locale.ROOT);
|
||||
|
@ -191,7 +193,7 @@ public class ConfigLoader {
|
|||
System.setProperty("config.path", path.getParent().toString());
|
||||
try {
|
||||
InputStream inputStream = Files.newInputStream(path);
|
||||
Settings.Builder fileSettings = createSettingsFromStream(inputStream, suffix);
|
||||
SettingsBuilder fileSettings = createSettingsFromStream(inputStream, suffix);
|
||||
if (fileSettings != null) {
|
||||
settings.put(fileSettings.build());
|
||||
}
|
||||
|
@ -203,10 +205,10 @@ public class ConfigLoader {
|
|||
return settings.isEmpty() ? null : settings;
|
||||
}
|
||||
|
||||
private Settings.Builder createClasspathSettings(ConfigParams params,
|
||||
ClassLoader classLoader,
|
||||
String fileNameWithoutSuffix) throws ConfigException {
|
||||
Settings.Builder settings = Settings.settingsBuilder();
|
||||
private SettingsBuilder createClasspathSettings(ConfigParams params,
|
||||
ClassLoader classLoader,
|
||||
String fileNameWithoutSuffix) throws ConfigException {
|
||||
SettingsBuilder settings = Settings.settingsBuilder();
|
||||
for (String suffix : SettingsLoaderService.getInstance().getSuffixes()) {
|
||||
String path = params.directoryName != null ?
|
||||
params.directoryName + '-' + fileNameWithoutSuffix + suffix : fileNameWithoutSuffix + suffix;
|
||||
|
@ -215,7 +217,7 @@ public class ConfigLoader {
|
|||
if (logger != null) {
|
||||
logger.info("found resource: " + path);
|
||||
}
|
||||
Settings.Builder streamSettings = createSettingsFromStream(inputStream, suffix);
|
||||
SettingsBuilder streamSettings = createSettingsFromStream(inputStream, suffix);
|
||||
if (streamSettings != null) {
|
||||
settings.put(streamSettings.build());
|
||||
}
|
||||
|
@ -224,8 +226,8 @@ public class ConfigLoader {
|
|||
return settings.isEmpty() ? null : settings;
|
||||
}
|
||||
|
||||
private Settings.Builder createSettingsFromStream(InputStream inputStream,
|
||||
String suffix) throws ConfigException {
|
||||
private SettingsBuilder createSettingsFromStream(InputStream inputStream,
|
||||
String suffix) throws ConfigException {
|
||||
if (inputStream == null) {
|
||||
if (logger != null) {
|
||||
logger.error("unable to open input stream");
|
||||
|
@ -235,8 +237,8 @@ public class ConfigLoader {
|
|||
return createSettingsFromReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8), suffix);
|
||||
}
|
||||
|
||||
private Settings.Builder createSettingsFromReader(Reader reader,
|
||||
String suffix) throws ConfigException {
|
||||
private SettingsBuilder createSettingsFromReader(Reader reader,
|
||||
String suffix) throws ConfigException {
|
||||
if (reader == null) {
|
||||
if (logger != null) {
|
||||
logger.error("unable to open reader");
|
||||
|
@ -245,7 +247,7 @@ public class ConfigLoader {
|
|||
}
|
||||
SettingsLoader settingsLoader = SettingsLoaderService.getInstance().loaderFromResource(suffix);
|
||||
if (settingsLoader != null) {
|
||||
Settings.Builder settings;
|
||||
SettingsBuilder settings;
|
||||
try (BufferedReader bufferedReader = new BufferedReader(reader)) {
|
||||
String content = bufferedReader.lines().collect(Collectors.joining("\n"));
|
||||
settings = Settings.settingsBuilder().put(settingsLoader.load(content));
|
||||
|
@ -261,8 +263,8 @@ public class ConfigLoader {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Settings.Builder overrideFromProperties(ConfigParams params,
|
||||
Settings.Builder settings) {
|
||||
private SettingsBuilder overrideFromProperties(ConfigParams params,
|
||||
SettingsBuilder settings) {
|
||||
for (String key : settings.map().keySet()) {
|
||||
String value = System.getProperty(params.directoryName != null ? params.directoryName + '.' + key : key);
|
||||
if (value != null) {
|
||||
|
@ -282,7 +284,7 @@ public class ConfigLoader {
|
|||
}
|
||||
if (params.directoryName != null) {
|
||||
list.add(params.directoryName + '-' + fileNameWithoutSuffix + "." + suffix);
|
||||
list.add(xdgConfigHome + '/' + params.directoryName + '/' + fileNameWithoutSuffix + "." +suffix);
|
||||
list.add(xdgConfigHome + '/' + params.directoryName + '/' + fileNameWithoutSuffix + "." + suffix);
|
||||
list.add("/etc/" + params.directoryName + '/' + fileNameWithoutSuffix + "." + suffix);
|
||||
} else {
|
||||
list.add(fileNameWithoutSuffix + "." + suffix);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.xbib.content.config;
|
||||
|
||||
import org.xbib.content.settings.datastructures.Settings;
|
||||
|
||||
import org.xbib.content.settings.datastructures.DatastructureSettings;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
|
@ -26,7 +25,7 @@ public class ConfigParams implements Comparable<ConfigParams> {
|
|||
|
||||
final List<SuffixedReader> reader = new ArrayList<>();
|
||||
|
||||
final List<Settings> settings = new ArrayList<>();
|
||||
final List<DatastructureSettings> settings = new ArrayList<>();
|
||||
|
||||
List<String> args = null;
|
||||
|
||||
|
@ -77,7 +76,7 @@ public class ConfigParams implements Comparable<ConfigParams> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ConfigParams withSettings(Settings settings) {
|
||||
public ConfigParams withSettings(DatastructureSettings settings) {
|
||||
this.settings.add(settings);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
package org.xbib.content.config.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.config.ConfigLoader;
|
||||
import org.xbib.content.config.ConfigParams;
|
||||
import org.xbib.content.settings.datastructures.Settings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ConfigLoaderTest {
|
||||
|
||||
|
@ -33,13 +29,4 @@ public class ConfigLoaderTest {
|
|||
assertEquals("world2", settings.get("hello2"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void configInterlibraryTest() {
|
||||
Settings settings = ConfigLoader.getInstance()
|
||||
.load(new ConfigParams()
|
||||
.withDirectoryName("interlibrary")
|
||||
.withFileNamesWithoutSuffix("test"));
|
||||
Logger.getAnonymousLogger().log(Level.INFO, settings.getAsMap().toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Test classes for content config.
|
||||
*/
|
||||
package org.xbib.content.config.test;
|
|
@ -1,16 +1,10 @@
|
|||
import org.xbib.content.XContent;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.properties.PropertiesSettingsLoader;
|
||||
|
||||
module org.xbib.content.core {
|
||||
uses XContent;
|
||||
uses SettingsLoader;
|
||||
exports org.xbib.content.io;
|
||||
exports org.xbib.content.properties;
|
||||
exports org.xbib.content.util.geo;
|
||||
exports org.xbib.content.util.unit;
|
||||
exports org.xbib.content.core;
|
||||
requires transitive org.xbib.content;
|
||||
requires transitive org.xbib.content.api;
|
||||
requires com.fasterxml.jackson.core;
|
||||
provides SettingsLoader with PropertiesSettingsLoader;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import org.xbib.content.XContentParser;
|
|||
import org.xbib.content.io.BytesReference;
|
||||
import org.xbib.content.io.BytesStreamOutput;
|
||||
import org.xbib.content.util.geo.GeoPoint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigDecimal;
|
||||
|
@ -136,6 +135,7 @@ public final class DefaultXContentBuilder implements XContentBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder field(String name, Integer value) throws IOException {
|
||||
field(name);
|
||||
if (value == null) {
|
||||
|
@ -146,6 +146,7 @@ public final class DefaultXContentBuilder implements XContentBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder field(String name, int value) throws IOException {
|
||||
field(name);
|
||||
generator.writeNumber(value);
|
||||
|
@ -289,6 +290,7 @@ public final class DefaultXContentBuilder implements XContentBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder field(String name, int... value) throws IOException {
|
||||
startArray(name);
|
||||
for (Object o : value) {
|
||||
|
@ -505,6 +507,7 @@ public final class DefaultXContentBuilder implements XContentBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder value(byte[] value) throws IOException {
|
||||
if (value == null) {
|
||||
return nullValue();
|
||||
|
|
|
@ -5,7 +5,6 @@ import org.xbib.content.XContentBuilder;
|
|||
import org.xbib.content.XContentGenerator;
|
||||
import org.xbib.content.XContentParser;
|
||||
import org.xbib.content.io.BytesReference;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.xbib.content.core;
|
|||
import org.xbib.content.XContent;
|
||||
import org.xbib.content.XContentBuilder;
|
||||
import org.xbib.content.io.BytesArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* Classes for properties-based content.
|
||||
*/
|
||||
package org.xbib.content.properties;
|
|
@ -1,233 +0,0 @@
|
|||
package org.xbib.content.util.unit;
|
||||
|
||||
/**
|
||||
* A {@code SizeUnit} represents size at a given unit of
|
||||
* granularity and provides utility methods to convert across units.
|
||||
* A {@code SizeUnit} does not maintain size information, but only
|
||||
* helps organize and use size representations that may be maintained
|
||||
* separately across various contexts.
|
||||
*/
|
||||
public enum ByteSizeUnit {
|
||||
BYTES {
|
||||
@Override
|
||||
public long toBytes(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKB(long size) {
|
||||
return size / (C1 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMB(long size) {
|
||||
return size / (C2 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGB(long size) {
|
||||
return size / (C3 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTB(long size) {
|
||||
return size / (C4 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPB(long size) {
|
||||
return size / (C5 / C0);
|
||||
}
|
||||
},
|
||||
KB {
|
||||
@Override
|
||||
public long toBytes(long size) {
|
||||
return x(size, C1 / C0, MAX / (C1 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKB(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMB(long size) {
|
||||
return size / (C2 / C1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGB(long size) {
|
||||
return size / (C3 / C1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTB(long size) {
|
||||
return size / (C4 / C1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPB(long size) {
|
||||
return size / (C5 / C1);
|
||||
}
|
||||
},
|
||||
MB {
|
||||
@Override
|
||||
public long toBytes(long size) {
|
||||
return x(size, C2 / C0, MAX / (C2 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKB(long size) {
|
||||
return x(size, C2 / C1, MAX / (C2 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMB(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGB(long size) {
|
||||
return size / (C3 / C2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTB(long size) {
|
||||
return size / (C4 / C2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPB(long size) {
|
||||
return size / (C5 / C2);
|
||||
}
|
||||
},
|
||||
GB {
|
||||
@Override
|
||||
public long toBytes(long size) {
|
||||
return x(size, C3 / C0, MAX / (C3 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKB(long size) {
|
||||
return x(size, C3 / C1, MAX / (C3 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMB(long size) {
|
||||
return x(size, C3 / C2, MAX / (C3 / C2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGB(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTB(long size) {
|
||||
return size / (C4 / C3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPB(long size) {
|
||||
return size / (C5 / C3);
|
||||
}
|
||||
},
|
||||
TB {
|
||||
@Override
|
||||
public long toBytes(long size) {
|
||||
return x(size, C4 / C0, MAX / (C4 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKB(long size) {
|
||||
return x(size, C4 / C1, MAX / (C4 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMB(long size) {
|
||||
return x(size, C4 / C2, MAX / (C4 / C2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGB(long size) {
|
||||
return x(size, C4 / C3, MAX / (C4 / C3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTB(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPB(long size) {
|
||||
return size / (C5 / C4);
|
||||
}
|
||||
},
|
||||
PB {
|
||||
@Override
|
||||
public long toBytes(long size) {
|
||||
return x(size, C5 / C0, MAX / (C5 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKB(long size) {
|
||||
return x(size, C5 / C1, MAX / (C5 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMB(long size) {
|
||||
return x(size, C5 / C2, MAX / (C5 / C2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGB(long size) {
|
||||
return x(size, C5 / C3, MAX / (C5 / C3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTB(long size) {
|
||||
return x(size, C5 / C4, MAX / (C5 / C4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPB(long size) {
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
static final long C0 = 1L;
|
||||
static final long C1 = C0 * 1024L;
|
||||
static final long C2 = C1 * 1024L;
|
||||
static final long C3 = C2 * 1024L;
|
||||
static final long C4 = C3 * 1024L;
|
||||
static final long C5 = C4 * 1024L;
|
||||
|
||||
static final long MAX = Long.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Scale d by m, checking for overflow.
|
||||
* This has a short name to make above code more readable.
|
||||
*/
|
||||
static long x(long d, long m, long over) {
|
||||
if (d > over) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
if (d < -over) {
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
return d * m;
|
||||
}
|
||||
|
||||
|
||||
public abstract long toBytes(long size);
|
||||
|
||||
public abstract long toKB(long size);
|
||||
|
||||
public abstract long toMB(long size);
|
||||
|
||||
public abstract long toGB(long size);
|
||||
|
||||
public abstract long toTB(long size);
|
||||
|
||||
public abstract long toPB(long size);
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
package org.xbib.content.util.unit;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ByteSizeValue {
|
||||
|
||||
private long size;
|
||||
|
||||
private ByteSizeUnit sizeUnit;
|
||||
|
||||
private ByteSizeValue() {
|
||||
}
|
||||
|
||||
public ByteSizeValue(long size, ByteSizeUnit sizeUnit) {
|
||||
this.size = size;
|
||||
this.sizeUnit = sizeUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the double value with a single decimal points, trimming trailing '.0'.
|
||||
* @param value value
|
||||
* @param suffix suffix
|
||||
* @return formatted decimal
|
||||
*/
|
||||
public static String format1Decimals(double value, String suffix) {
|
||||
String p = String.valueOf(value);
|
||||
int ix = p.indexOf('.') + 1;
|
||||
int ex = p.indexOf('E');
|
||||
char fraction = p.charAt(ix);
|
||||
if (fraction == '0') {
|
||||
if (ex != -1) {
|
||||
return p.substring(0, ix - 1) + p.substring(ex) + suffix;
|
||||
} else {
|
||||
return p.substring(0, ix - 1) + suffix;
|
||||
}
|
||||
} else {
|
||||
if (ex != -1) {
|
||||
return p.substring(0, ix) + fraction + p.substring(ex) + suffix;
|
||||
} else {
|
||||
return p.substring(0, ix) + fraction + suffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteSizeValue parseBytesSizeValue(String sValue) {
|
||||
return parseBytesSizeValue(sValue, null);
|
||||
}
|
||||
|
||||
public static ByteSizeValue parseBytesSizeValue(String sValue, ByteSizeValue defaultValue) {
|
||||
if (sValue == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
long bytes;
|
||||
try {
|
||||
String lastTwoChars = sValue.substring(sValue.length() - Math.min(2, sValue.length())).toLowerCase(Locale.ROOT);
|
||||
if (lastTwoChars.endsWith("k")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C1);
|
||||
} else if (lastTwoChars.endsWith("kb")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C1);
|
||||
} else if (lastTwoChars.endsWith("m")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C2);
|
||||
} else if (lastTwoChars.endsWith("mb")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C2);
|
||||
} else if (lastTwoChars.endsWith("g")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C3);
|
||||
} else if (lastTwoChars.endsWith("gb")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C3);
|
||||
} else if (lastTwoChars.endsWith("t")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C4);
|
||||
} else if (lastTwoChars.endsWith("tb")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C4);
|
||||
} else if (lastTwoChars.endsWith("p")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C5);
|
||||
} else if (lastTwoChars.endsWith("pb")) {
|
||||
bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C5);
|
||||
} else if (lastTwoChars.endsWith("b")) {
|
||||
bytes = Long.parseLong(sValue.substring(0, sValue.length() - 1));
|
||||
} else {
|
||||
bytes = Long.parseLong(sValue);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
return new ByteSizeValue(bytes, ByteSizeUnit.BYTES);
|
||||
}
|
||||
|
||||
public int bytesAsInt() throws IllegalArgumentException {
|
||||
long bytes = bytes();
|
||||
if (bytes > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("size [" + toString() + "] is bigger than max int");
|
||||
}
|
||||
return (int) bytes;
|
||||
}
|
||||
|
||||
public long bytes() {
|
||||
return sizeUnit.toBytes(size);
|
||||
}
|
||||
|
||||
public long getBytes() {
|
||||
return bytes();
|
||||
}
|
||||
|
||||
public long kb() {
|
||||
return sizeUnit.toKB(size);
|
||||
}
|
||||
|
||||
public long getKb() {
|
||||
return kb();
|
||||
}
|
||||
|
||||
public long mb() {
|
||||
return sizeUnit.toMB(size);
|
||||
}
|
||||
|
||||
public long getMb() {
|
||||
return mb();
|
||||
}
|
||||
|
||||
public long gb() {
|
||||
return sizeUnit.toGB(size);
|
||||
}
|
||||
|
||||
public long getGb() {
|
||||
return gb();
|
||||
}
|
||||
|
||||
public long tb() {
|
||||
return sizeUnit.toTB(size);
|
||||
}
|
||||
|
||||
public long getTb() {
|
||||
return tb();
|
||||
}
|
||||
|
||||
public long pb() {
|
||||
return sizeUnit.toPB(size);
|
||||
}
|
||||
|
||||
public long getPb() {
|
||||
return pb();
|
||||
}
|
||||
|
||||
public double kbFrac() {
|
||||
return ((double) bytes()) / ByteSizeUnit.C1;
|
||||
}
|
||||
|
||||
public double getKbFrac() {
|
||||
return kbFrac();
|
||||
}
|
||||
|
||||
public double mbFrac() {
|
||||
return ((double) bytes()) / ByteSizeUnit.C2;
|
||||
}
|
||||
|
||||
public double getMbFrac() {
|
||||
return mbFrac();
|
||||
}
|
||||
|
||||
public double gbFrac() {
|
||||
return ((double) bytes()) / ByteSizeUnit.C3;
|
||||
}
|
||||
|
||||
public double getGbFrac() {
|
||||
return gbFrac();
|
||||
}
|
||||
|
||||
public double tbFrac() {
|
||||
return ((double) bytes()) / ByteSizeUnit.C4;
|
||||
}
|
||||
|
||||
public double getTbFrac() {
|
||||
return tbFrac();
|
||||
}
|
||||
|
||||
public double pbFrac() {
|
||||
return ((double) bytes()) / ByteSizeUnit.C5;
|
||||
}
|
||||
|
||||
public double getPbFrac() {
|
||||
return pbFrac();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
long bytes = bytes();
|
||||
double value = bytes;
|
||||
String suffix = "b";
|
||||
if (bytes >= ByteSizeUnit.C5) {
|
||||
value = pbFrac();
|
||||
suffix = "pb";
|
||||
} else if (bytes >= ByteSizeUnit.C4) {
|
||||
value = tbFrac();
|
||||
suffix = "tb";
|
||||
} else if (bytes >= ByteSizeUnit.C3) {
|
||||
value = gbFrac();
|
||||
suffix = "gb";
|
||||
} else if (bytes >= ByteSizeUnit.C2) {
|
||||
value = mbFrac();
|
||||
suffix = "mb";
|
||||
} else if (bytes >= ByteSizeUnit.C1) {
|
||||
value = kbFrac();
|
||||
suffix = "kb";
|
||||
}
|
||||
return format1Decimals(value, suffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ByteSizeValue sizeValue = (ByteSizeValue) o;
|
||||
return size == sizeValue.size && sizeUnit == sizeValue.sizeUnit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (size ^ (size >>> 32));
|
||||
result = 31 * result + (sizeUnit != null ? sizeUnit.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,229 +0,0 @@
|
|||
package org.xbib.content.util.unit;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public enum SizeUnit {
|
||||
SCALAR {
|
||||
@Override
|
||||
public long toScalar(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKilo(long size) {
|
||||
return size / (C1 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMega(long size) {
|
||||
return size / (C2 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGiga(long size) {
|
||||
return size / (C3 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTera(long size) {
|
||||
return size / (C4 / C0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPeta(long size) {
|
||||
return size / (C5 / C0);
|
||||
}
|
||||
},
|
||||
KILO {
|
||||
@Override
|
||||
public long toScalar(long size) {
|
||||
return x(size, C1 / C0, MAX / (C1 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKilo(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMega(long size) {
|
||||
return size / (C2 / C1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGiga(long size) {
|
||||
return size / (C3 / C1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTera(long size) {
|
||||
return size / (C4 / C1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPeta(long size) {
|
||||
return size / (C5 / C1);
|
||||
}
|
||||
},
|
||||
MEGA {
|
||||
@Override
|
||||
public long toScalar(long size) {
|
||||
return x(size, C2 / C0, MAX / (C2 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKilo(long size) {
|
||||
return x(size, C2 / C1, MAX / (C2 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMega(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGiga(long size) {
|
||||
return size / (C3 / C2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTera(long size) {
|
||||
return size / (C4 / C2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPeta(long size) {
|
||||
return size / (C5 / C2);
|
||||
}
|
||||
},
|
||||
GIGA {
|
||||
@Override
|
||||
public long toScalar(long size) {
|
||||
return x(size, C3 / C0, MAX / (C3 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKilo(long size) {
|
||||
return x(size, C3 / C1, MAX / (C3 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMega(long size) {
|
||||
return x(size, C3 / C2, MAX / (C3 / C2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGiga(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTera(long size) {
|
||||
return size / (C4 / C3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPeta(long size) {
|
||||
return size / (C5 / C3);
|
||||
}
|
||||
},
|
||||
TERA {
|
||||
@Override
|
||||
public long toScalar(long size) {
|
||||
return x(size, C4 / C0, MAX / (C4 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKilo(long size) {
|
||||
return x(size, C4 / C1, MAX / (C4 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMega(long size) {
|
||||
return x(size, C4 / C2, MAX / (C4 / C2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGiga(long size) {
|
||||
return x(size, C4 / C3, MAX / (C4 / C3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTera(long size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPeta(long size) {
|
||||
return size / (C5 / C0);
|
||||
}
|
||||
},
|
||||
PETA {
|
||||
@Override
|
||||
public long toScalar(long size) {
|
||||
return x(size, C5 / C0, MAX / (C5 / C0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toKilo(long size) {
|
||||
return x(size, C5 / C1, MAX / (C5 / C1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toMega(long size) {
|
||||
return x(size, C5 / C2, MAX / (C5 / C2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toGiga(long size) {
|
||||
return x(size, C5 / C3, MAX / (C5 / C3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toTera(long size) {
|
||||
return x(size, C5 / C4, MAX / (C5 / C4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toPeta(long size) {
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
static final long C0 = 1L;
|
||||
static final long C1 = C0 * 1000L;
|
||||
static final long C2 = C1 * 1000L;
|
||||
static final long C3 = C2 * 1000L;
|
||||
static final long C4 = C3 * 1000L;
|
||||
static final long C5 = C4 * 1000L;
|
||||
|
||||
static final long MAX = Long.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Scale d by m, checking for overflow.
|
||||
* This has a short name to make above code more readable.
|
||||
*/
|
||||
static long x(long d, long m, long over) {
|
||||
if (d > over) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
if (d < -over) {
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
return d * m;
|
||||
}
|
||||
|
||||
|
||||
public abstract long toScalar(long size);
|
||||
|
||||
public abstract long toKilo(long size);
|
||||
|
||||
public abstract long toMega(long size);
|
||||
|
||||
public abstract long toGiga(long size);
|
||||
|
||||
public abstract long toTera(long size);
|
||||
|
||||
public abstract long toPeta(long size);
|
||||
}
|
|
@ -1,259 +0,0 @@
|
|||
package org.xbib.content.util.unit;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TimeValue {
|
||||
|
||||
private static final long C0 = 1L;
|
||||
private static final long C1 = C0 * 1000L;
|
||||
private static final long C2 = C1 * 1000L;
|
||||
private static final long C3 = C2 * 1000L;
|
||||
private static final long C4 = C3 * 60L;
|
||||
private static final long C5 = C4 * 60L;
|
||||
private static final long C6 = C5 * 24L;
|
||||
private long duration;
|
||||
private TimeUnit timeUnit;
|
||||
|
||||
private TimeValue() {
|
||||
}
|
||||
|
||||
public TimeValue(long millis) {
|
||||
this(millis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public TimeValue(long duration, TimeUnit timeUnit) {
|
||||
this.duration = duration;
|
||||
this.timeUnit = timeUnit;
|
||||
}
|
||||
|
||||
public static TimeValue timeValueNanos(long nanos) {
|
||||
return new TimeValue(nanos, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
public static TimeValue timeValueMillis(long millis) {
|
||||
return new TimeValue(millis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public static TimeValue timeValueSeconds(long seconds) {
|
||||
return new TimeValue(seconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static TimeValue timeValueMinutes(long minutes) {
|
||||
return new TimeValue(minutes, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
public static TimeValue timeValueHours(long hours) {
|
||||
return new TimeValue(hours, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the double value with a single decimal points, trimming trailing '.0'.
|
||||
*
|
||||
* @param value value
|
||||
* @param suffix suffix
|
||||
* @return string
|
||||
*/
|
||||
public static String format1Decimals(double value, String suffix) {
|
||||
String p = String.valueOf(value);
|
||||
int ix = p.indexOf('.') + 1;
|
||||
int ex = p.indexOf('E');
|
||||
char fraction = p.charAt(ix);
|
||||
if (fraction == '0') {
|
||||
if (ex != -1) {
|
||||
return p.substring(0, ix - 1) + p.substring(ex) + suffix;
|
||||
} else {
|
||||
return p.substring(0, ix - 1) + suffix;
|
||||
}
|
||||
} else {
|
||||
if (ex != -1) {
|
||||
return p.substring(0, ix) + fraction + p.substring(ex) + suffix;
|
||||
} else {
|
||||
return p.substring(0, ix) + fraction + suffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeValue parseTimeValue(String sValue, TimeValue defaultValue) {
|
||||
if (sValue == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
long millis;
|
||||
if (sValue.endsWith("S")) {
|
||||
millis = Long.parseLong(sValue.substring(0, sValue.length() - 1));
|
||||
} else if (sValue.endsWith("ms")) {
|
||||
millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)));
|
||||
} else if (sValue.endsWith("s")) {
|
||||
millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 1000);
|
||||
} else if (sValue.endsWith("m")) {
|
||||
millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 1000);
|
||||
} else if (sValue.endsWith("H") || sValue.endsWith("h")) {
|
||||
millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 60 * 1000);
|
||||
} else if (sValue.endsWith("d")) {
|
||||
millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 24 * 60 * 60 * 1000);
|
||||
} else if (sValue.endsWith("w")) {
|
||||
millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 7 * 24 * 60 * 60 * 1000);
|
||||
} else {
|
||||
millis = Long.parseLong(sValue);
|
||||
}
|
||||
return new TimeValue(millis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public long nanos() {
|
||||
return timeUnit.toNanos(duration);
|
||||
}
|
||||
|
||||
public long getNanos() {
|
||||
return nanos();
|
||||
}
|
||||
|
||||
public long micros() {
|
||||
return timeUnit.toMicros(duration);
|
||||
}
|
||||
|
||||
public long getMicros() {
|
||||
return micros();
|
||||
}
|
||||
|
||||
public long millis() {
|
||||
return timeUnit.toMillis(duration);
|
||||
}
|
||||
|
||||
public long getMillis() {
|
||||
return millis();
|
||||
}
|
||||
|
||||
public long seconds() {
|
||||
return timeUnit.toSeconds(duration);
|
||||
}
|
||||
|
||||
public long getSeconds() {
|
||||
return seconds();
|
||||
}
|
||||
|
||||
public long minutes() {
|
||||
return timeUnit.toMinutes(duration);
|
||||
}
|
||||
|
||||
public long getMinutes() {
|
||||
return minutes();
|
||||
}
|
||||
|
||||
public long hours() {
|
||||
return timeUnit.toHours(duration);
|
||||
}
|
||||
|
||||
public long getHours() {
|
||||
return hours();
|
||||
}
|
||||
|
||||
public long days() {
|
||||
return timeUnit.toDays(duration);
|
||||
}
|
||||
|
||||
public long getDays() {
|
||||
return days();
|
||||
}
|
||||
|
||||
public double microsFrac() {
|
||||
return ((double) nanos()) / C1;
|
||||
}
|
||||
|
||||
public double getMicrosFrac() {
|
||||
return microsFrac();
|
||||
}
|
||||
|
||||
public double millisFrac() {
|
||||
return ((double) nanos()) / C2;
|
||||
}
|
||||
|
||||
public double getMillisFrac() {
|
||||
return millisFrac();
|
||||
}
|
||||
|
||||
public double secondsFrac() {
|
||||
return ((double) nanos()) / C3;
|
||||
}
|
||||
|
||||
public double getSecondsFrac() {
|
||||
return secondsFrac();
|
||||
}
|
||||
|
||||
public double minutesFrac() {
|
||||
return ((double) nanos()) / C4;
|
||||
}
|
||||
|
||||
public double getMinutesFrac() {
|
||||
return minutesFrac();
|
||||
}
|
||||
|
||||
public double hoursFrac() {
|
||||
return ((double) nanos()) / C5;
|
||||
}
|
||||
|
||||
public double getHoursFrac() {
|
||||
return hoursFrac();
|
||||
}
|
||||
|
||||
public double daysFrac() {
|
||||
return ((double) nanos()) / C6;
|
||||
}
|
||||
|
||||
public double getDaysFrac() {
|
||||
return daysFrac();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (duration < 0) {
|
||||
return Long.toString(duration);
|
||||
}
|
||||
long nanos = nanos();
|
||||
if (nanos == 0) {
|
||||
return "0s";
|
||||
}
|
||||
double value = nanos;
|
||||
String suffix = "nanos";
|
||||
if (nanos >= C6) {
|
||||
value = daysFrac();
|
||||
suffix = "d";
|
||||
} else if (nanos >= C5) {
|
||||
value = hoursFrac();
|
||||
suffix = "h";
|
||||
} else if (nanos >= C4) {
|
||||
value = minutesFrac();
|
||||
suffix = "m";
|
||||
} else if (nanos >= C3) {
|
||||
value = secondsFrac();
|
||||
suffix = "s";
|
||||
} else if (nanos >= C2) {
|
||||
value = millisFrac();
|
||||
suffix = "ms";
|
||||
} else if (nanos >= C1) {
|
||||
value = microsFrac();
|
||||
suffix = "micros";
|
||||
}
|
||||
return format1Decimals(value, suffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TimeValue timeValue = (TimeValue) o;
|
||||
return duration == timeValue.duration && timeUnit == timeValue.timeUnit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (duration ^ (duration >>> 32));
|
||||
result = 31 * result + (timeUnit != null ? timeUnit.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* Classes for units.
|
||||
*/
|
||||
package org.xbib.content.util.unit;
|
|
@ -1 +0,0 @@
|
|||
org.xbib.content.properties.PropertiesSettingsLoader
|
|
@ -1,9 +1,10 @@
|
|||
import org.xbib.content.settings.datastructures.SettingsLoader;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.json.JsonSettingsLoader;
|
||||
|
||||
module org.xbib.content.settings.datastructures.json {
|
||||
exports org.xbib.content.settings.datastructures.json;
|
||||
requires transitive org.xbib.content.settings.datastructures;
|
||||
requires org.xbib.datastructures.json.tiny;
|
||||
uses SettingsLoader;
|
||||
provides SettingsLoader with JsonSettingsLoader;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.xbib.content.settings.datastructures.json.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.content.settings.datastructures.Settings;
|
||||
import org.xbib.content.settings.datastructures.SettingsLoader;
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.json.JsonSettingsLoader;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import org.xbib.content.settings.datastructures.SettingsLoader;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.yaml.YamlSettingsLoader;
|
||||
|
||||
module org.xbib.content.settings.datastructures.yaml {
|
||||
exports org.xbib.content.settings.datastructures.yaml;
|
||||
requires transitive org.xbib.content.settings.datastructures;
|
||||
requires org.xbib.datastructures.yaml.tiny;
|
||||
uses SettingsLoader;
|
||||
provides SettingsLoader with YamlSettingsLoader;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ package org.xbib.content.settings.datastructures.yaml.test;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.content.settings.datastructures.Settings;
|
||||
import org.xbib.content.settings.datastructures.SettingsLoader;
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.yaml.YamlSettingsLoader;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
dependencies {
|
||||
api "org.xbib:datastructures-api:${project.property('xbib-datastructures.version')}"
|
||||
api project(':content-api')
|
||||
api "org.xbib:datastructures-tiny:${project.property('xbib-datastructures.version')}"
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import org.xbib.content.SettingsBuilder;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.DatastructureSettingsBuilder;
|
||||
import org.xbib.content.settings.datastructures.PropertiesSettingsLoader;
|
||||
import org.xbib.content.settings.datastructures.SettingsLoader;
|
||||
|
||||
module org.xbib.content.settings.datastructures {
|
||||
uses SettingsLoader;
|
||||
provides SettingsLoader with PropertiesSettingsLoader;
|
||||
uses SettingsBuilder;
|
||||
provides SettingsBuilder with DatastructureSettingsBuilder;
|
||||
exports org.xbib.content.settings.datastructures;
|
||||
requires transitive org.xbib.content.api;
|
||||
requires org.xbib.datastructures.tiny;
|
||||
requires transitive org.xbib.datastructures.api;
|
||||
requires transitive java.sql;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.datastructures.api.Builder;
|
||||
import org.xbib.datastructures.api.DataStructure;
|
||||
import org.xbib.datastructures.api.ListNode;
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.SettingsException;
|
||||
import org.xbib.datastructures.api.ByteSizeValue;
|
||||
import org.xbib.datastructures.api.TimeValue;
|
||||
import org.xbib.datastructures.tiny.TinyMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DatastructureSettings implements Settings {
|
||||
|
||||
private static final String[] EMPTY_ARRAY = new String[0];
|
||||
|
||||
private final TinyMap<String, String> map;
|
||||
|
||||
protected DatastructureSettings(TinyMap<String, String> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public static DatastructureSettings fromMap(Map<String, Object> map) {
|
||||
DatastructureSettingsBuilder builder = new DatastructureSettingsBuilder();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
builder.put(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : null);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static void toMap(DatastructureSettings settings, Map<String, Object> map) {
|
||||
for (String key : settings.getAsMap().keySet()) {
|
||||
map.put(key, settings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] splitStringByCommaToArray(final String s) {
|
||||
return splitStringToArray(s, ',');
|
||||
}
|
||||
|
||||
public static String[] splitStringToArray(final String s, final char c) {
|
||||
if (s.length() == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
final char[] chars = s.toCharArray();
|
||||
int count = 1;
|
||||
for (final char x : chars) {
|
||||
if (x == c) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
final String[] result = new String[count];
|
||||
final int len = chars.length;
|
||||
int start = 0;
|
||||
int pos = 0;
|
||||
int i = 0;
|
||||
for (; pos < len; pos++) {
|
||||
if (chars[pos] == c) {
|
||||
int size = pos - start;
|
||||
if (size > 0) {
|
||||
result[i++] = new String(chars, start, size);
|
||||
}
|
||||
start = pos + 1;
|
||||
}
|
||||
}
|
||||
int size = pos - start;
|
||||
if (size > 0) {
|
||||
result[i++] = new String(chars, start, size);
|
||||
}
|
||||
if (i != count) {
|
||||
String[] result1 = new String[i];
|
||||
System.arraycopy(result, 0, result1, 0, i);
|
||||
return result1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAsMap() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAsStructuredMap() {
|
||||
TinyMap.Builder<String, Object> stringObjectMap = TinyMap.builder();
|
||||
for (String key : map.keySet()) {
|
||||
String value = map.get(key);
|
||||
processSetting(stringObjectMap, "", key, value);
|
||||
}
|
||||
for (String key : stringObjectMap.keySet()) {
|
||||
Object object = stringObjectMap.get(key);
|
||||
if (object instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> valMap = (Map<String, Object>) object;
|
||||
stringObjectMap.put(key, convertMapsToArrays(valMap));
|
||||
}
|
||||
}
|
||||
return stringObjectMap.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings getByPrefix(String prefix) {
|
||||
DatastructureSettingsBuilder builder = new DatastructureSettingsBuilder();
|
||||
for (String key : map.keySet()) {
|
||||
String value = map.get(key);
|
||||
if (key.startsWith(prefix)) {
|
||||
if (key.length() < prefix.length()) {
|
||||
continue;
|
||||
}
|
||||
builder.put(key.substring(prefix.length()), value);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings getAsSettings(String setting) {
|
||||
return getByPrefix(setting + ".");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSetting(String setting) {
|
||||
if (map.containsKey(setting)) {
|
||||
return true;
|
||||
}
|
||||
for (String key : map.keySet()) {
|
||||
if (key.startsWith(setting)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String setting) {
|
||||
return map.get(setting);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String setting, String defaultValue) {
|
||||
String s = map.get(setting);
|
||||
return s == null ? defaultValue : s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getAsFloat(String setting, Float defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Float.parseFloat(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse float setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAsDouble(String setting, Double defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Double.parseDouble(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse double setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getAsInt(String setting, Integer defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse int setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getAsLong(String setting, Long defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Long.parseLong(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse long setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getAsBoolean(String setting, Boolean defaultValue) {
|
||||
String value = get(setting);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return !("false".equals(value) || "0".equals(value) || "off".equals(value) || "no".equals(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeValue getAsTime(String setting, TimeValue defaultValue) {
|
||||
return TimeValue.parseTimeValue(get(setting), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteSizeValue getAsBytesSize(String setting, ByteSizeValue defaultValue) {
|
||||
return ByteSizeValue.parseBytesSizeValue(get(setting), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAsArray(String settingPrefix) {
|
||||
return getAsArray(settingPrefix, EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAsArray(String settingPrefix, String[] defaultArray) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (get(settingPrefix) != null) {
|
||||
String[] strings = splitStringByCommaToArray(get(settingPrefix));
|
||||
if (strings.length > 0) {
|
||||
for (String string : strings) {
|
||||
result.add(string.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = get(settingPrefix + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
result.add(value.trim());
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
return defaultArray;
|
||||
}
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, org.xbib.content.Settings> getGroups(String prefix) {
|
||||
String settingPrefix = prefix;
|
||||
if (settingPrefix.charAt(settingPrefix.length() - 1) != '.') {
|
||||
settingPrefix = settingPrefix + ".";
|
||||
}
|
||||
// we don't really care that it might happen twice
|
||||
TinyMap.Builder<String, TinyMap.Builder<String, String>> hashMap = TinyMap.builder();
|
||||
for (String o : this.map.keySet()) {
|
||||
if (o.startsWith(settingPrefix)) {
|
||||
String nameValue = o.substring(settingPrefix.length());
|
||||
int dotIndex = nameValue.indexOf('.');
|
||||
if (dotIndex == -1) {
|
||||
throw new SettingsException("failed to get setting group for ["
|
||||
+ settingPrefix
|
||||
+ "] setting prefix and setting [" + o + "] because of a missing '.'");
|
||||
}
|
||||
String name = nameValue.substring(0, dotIndex);
|
||||
String value = nameValue.substring(dotIndex + 1);
|
||||
Map<String, String> groupSettings = hashMap.computeIfAbsent(name, k -> TinyMap.builder());
|
||||
groupSettings.put(value, get(o));
|
||||
}
|
||||
}
|
||||
TinyMap.Builder<String, org.xbib.content.Settings> retVal = TinyMap.builder();
|
||||
for (String key : hashMap.keySet()) {
|
||||
TinyMap.Builder<String, String> value = hashMap.get(key);
|
||||
retVal.put(key, new DatastructureSettings(value.build()));
|
||||
}
|
||||
return retVal.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || !(o == null || getClass() != o.getClass()) && map.equals(((DatastructureSettings) o).map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return map.hashCode();
|
||||
}
|
||||
|
||||
private void processSetting(Map<String, Object> map, String prefix, String setting, String value) {
|
||||
int prefixLength = setting.indexOf('.');
|
||||
if (prefixLength == -1) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> innerMap = (Map<String, Object>) map.get(prefix + setting);
|
||||
if (innerMap != null) {
|
||||
for (String k : innerMap.keySet()) {
|
||||
Object v = innerMap.get(k);
|
||||
map.put(prefix + setting + "." + k, v);
|
||||
}
|
||||
}
|
||||
map.put(prefix + setting, value);
|
||||
} else {
|
||||
String key = setting.substring(0, prefixLength);
|
||||
String rest = setting.substring(prefixLength + 1);
|
||||
Object existingValue = map.get(prefix + key);
|
||||
if (existingValue == null) {
|
||||
Map<String, Object> newMap = TinyMap.builder();
|
||||
processSetting(newMap, "", rest, value);
|
||||
map.put(key, newMap);
|
||||
} else {
|
||||
if (existingValue instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> innerMap = (Map<String, Object>) existingValue;
|
||||
processSetting(innerMap, "", rest, value);
|
||||
map.put(key, innerMap);
|
||||
} else {
|
||||
processSetting(map, prefix + key + ".", rest, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object convertMapsToArrays(Map<String, Object> map) {
|
||||
if (map.isEmpty()) {
|
||||
return map;
|
||||
}
|
||||
boolean isArray = true;
|
||||
int maxIndex = -1;
|
||||
for (String key : map.keySet()) {
|
||||
Object value = map.get(key);
|
||||
if (isArray) {
|
||||
try {
|
||||
int index = Integer.parseInt(key);
|
||||
if (index >= 0) {
|
||||
maxIndex = Math.max(maxIndex, index);
|
||||
} else {
|
||||
isArray = false;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
isArray = false;
|
||||
}
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> valMap = (Map<String, Object>) value;
|
||||
map.put(key, convertMapsToArrays(valMap));
|
||||
}
|
||||
}
|
||||
if (isArray && (maxIndex + 1) == map.size()) {
|
||||
ArrayList<Object> newValue = new ArrayList<>(maxIndex + 1);
|
||||
for (int i = 0; i <= maxIndex; i++) {
|
||||
Object obj = map.get(Integer.toString(i));
|
||||
if (obj == null) {
|
||||
return map;
|
||||
}
|
||||
newValue.add(obj);
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,355 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import org.xbib.content.PlaceholderResolver;
|
||||
import org.xbib.content.PropertyPlaceholder;
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.SettingsBuilder;
|
||||
import org.xbib.content.SettingsException;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.datastructures.tiny.TinyMap;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DatastructureSettingsBuilder implements SettingsBuilder {
|
||||
|
||||
private final SettingsLoaderService settingsLoaderService = SettingsLoaderService.getInstance();
|
||||
|
||||
private final TinyMap.Builder<String, String> map;
|
||||
|
||||
public DatastructureSettingsBuilder() {
|
||||
map = TinyMap.builder();
|
||||
}
|
||||
|
||||
public Map<String, String> map() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public String remove(String key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param value The setting value
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String key, String value) {
|
||||
map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and class as value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param clazz The setting class value
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String key, Class<?> clazz) {
|
||||
map.put(key, clazz.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the boolean value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The boolean value
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String setting, boolean value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the int value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The int value
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String setting, int value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the long value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The long value
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String setting, long value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the float value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The float value
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String setting, float value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the double value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The double value
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String setting, double value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder putArray(String setting, String... values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
put(setting + '.' + i, values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder putArray(String setting, List<String> values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
put(setting + '.' + i, values.get(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting group.
|
||||
*
|
||||
* @param settingPrefix setting prefix
|
||||
* @param groupName group name
|
||||
* @param settings settings
|
||||
* @param values values
|
||||
* @return a builder
|
||||
* @throws SettingsException if setting fails
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(String settingPrefix, String groupName, String[] settings, String[] values)
|
||||
throws SettingsException {
|
||||
if (settings.length != values.length) {
|
||||
throw new SettingsException("the settings length must match the value length");
|
||||
}
|
||||
for (int i = 0; i < settings.length; i++) {
|
||||
if (values[i] == null) {
|
||||
continue;
|
||||
}
|
||||
put(settingPrefix + "" + groupName + "." + settings[i], values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
*
|
||||
* @param settings settings
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(Settings settings) {
|
||||
map.putAll(settings.getAsMap());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
*
|
||||
* @param settings settings
|
||||
* @return a builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder put(Map<String, String> settings) {
|
||||
map.putAll(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from the actual string content that represents them using the
|
||||
* {@link SettingsLoaderService#loaderFromString(String)}.
|
||||
*
|
||||
* @param source source
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder loadFromString(String source) {
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromString(source);
|
||||
try {
|
||||
Map<String, String> loadedSettings = settingsLoader.load(source);
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("failed to load settings from [" + source + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from a resource.
|
||||
*
|
||||
* @param resourceName resource name
|
||||
* @param inputStream input stream
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder loadFromResource(String resourceName, InputStream inputStream) throws SettingsException {
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromResource(resourceName);
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||
Map<String, String> loadedSettings = settingsLoader.load(bufferedReader.lines().collect(Collectors.joining()));
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("failed to load settings from [" + resourceName + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system properties to this settings.
|
||||
*
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder loadFromSystemProperties() {
|
||||
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
|
||||
put((String) entry.getKey(), (String) entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system environment to this settings.
|
||||
*
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder loadFromSystemEnvironment() {
|
||||
for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatastructureSettingsBuilder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder,
|
||||
PlaceholderResolver placeholderResolver) {
|
||||
map.replaceAll((k, v) -> propertyPlaceholder.replacePlaceholders(v, placeholderResolver));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs across all the settings set on this builder and replaces {@code ${...}} elements in the
|
||||
* each setting value according to the following logic:
|
||||
* <p>
|
||||
* First, tries to resolve it against a System property ({@link System#getProperty(String)}), next,
|
||||
* tries and resolve it against an environment variable ({@link System#getenv(String)}), next,
|
||||
* tries and resolve it against a date pattern to resolve the current date,
|
||||
* and last, tries and replace it with another setting already set on this builder.
|
||||
*
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public DatastructureSettingsBuilder replacePropertyPlaceholders() {
|
||||
return replacePropertyPlaceholders(new PropertyPlaceholder("${", "}", false),
|
||||
placeholderName -> {
|
||||
// system property
|
||||
String value = System.getProperty(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// environment
|
||||
value = System.getenv(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// current date
|
||||
try {
|
||||
return DateTimeFormatter.ofPattern(placeholderName).format(LocalDate.now());
|
||||
} catch (IllegalArgumentException | DateTimeException e) {
|
||||
return map.get(placeholderName);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatastructureSettingsBuilder setRefresh(Path path, long initialDelay, long period, TimeUnit timeUnit) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatastructureSettings build() {
|
||||
return new DatastructureSettings(map.build());
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Format java exception messages and stack traces.
|
||||
*/
|
||||
public final class ExceptionFormatter {
|
||||
|
||||
private ExceptionFormatter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Format exception with stack trace.
|
||||
*
|
||||
* @param t the thrown object
|
||||
* @return the formatted exception
|
||||
*/
|
||||
public static String format(Throwable t) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
append(sb, t, 0, true);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append Exception to string builder.
|
||||
*/
|
||||
private static void append(StringBuilder sb, Throwable t, int level, boolean details) {
|
||||
if (((t != null) && (t.getMessage() != null)) && (!t.getMessage().isEmpty())) {
|
||||
if (details && (level > 0)) {
|
||||
sb.append("\n\nCaused by\n");
|
||||
}
|
||||
sb.append(t.getMessage());
|
||||
}
|
||||
if (details) {
|
||||
if (t != null) {
|
||||
if ((t.getMessage() != null) && (t.getMessage().isEmpty())) {
|
||||
sb.append("\n\nCaused by ");
|
||||
} else {
|
||||
sb.append("\n\n");
|
||||
}
|
||||
}
|
||||
StringWriter sw = new StringWriter();
|
||||
if (t != null) {
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
}
|
||||
sb.append(sw.toString());
|
||||
}
|
||||
if (t != null) {
|
||||
if (t.getCause() != null) {
|
||||
append(sb, t.getCause(), level + 1, details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
/**
|
||||
* Strategy interface used to resolve replacement values for placeholders contained in Strings.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PlaceholderResolver {
|
||||
|
||||
/**
|
||||
* Resolves the supplied placeholder name into the replacement value.
|
||||
*
|
||||
* @param placeholderName the name of the placeholder to resolve.
|
||||
* @return the replacement value or <code>null</code> if no replacement is to be made.
|
||||
*/
|
||||
String resolvePlaceholder(String placeholderName);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.datastructures.tiny.TinyMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -1,656 +0,0 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import org.xbib.datastructures.api.ByteSizeValue;
|
||||
import org.xbib.datastructures.api.TimeValue;
|
||||
import org.xbib.datastructures.tiny.TinyMap;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Settings implements AutoCloseable {
|
||||
|
||||
public static final Settings EMPTY_SETTINGS = new Builder().build();
|
||||
|
||||
public static final String[] EMPTY_ARRAY = new String[0];
|
||||
|
||||
private final TinyMap<String, String> map;
|
||||
|
||||
private Settings(TinyMap<String, String> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public static Settings fromMap(Map<String, Object> map) {
|
||||
Builder builder = new Builder();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
builder.put(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : null);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static void toMap(Settings settings, Map<String, Object> map) {
|
||||
for (String key : settings.getAsMap().keySet()) {
|
||||
map.put(key, settings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a builder to be used in order to build settings.
|
||||
* @return a builder
|
||||
*/
|
||||
public static Builder settingsBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static String[] splitStringByCommaToArray(final String s) {
|
||||
return splitStringToArray(s, ',');
|
||||
}
|
||||
|
||||
public static String[] splitStringToArray(final String s, final char c) {
|
||||
if (s.length() == 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
final char[] chars = s.toCharArray();
|
||||
int count = 1;
|
||||
for (final char x : chars) {
|
||||
if (x == c) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
final String[] result = new String[count];
|
||||
final int len = chars.length;
|
||||
int start = 0;
|
||||
int pos = 0;
|
||||
int i = 0;
|
||||
for (; pos < len; pos++) {
|
||||
if (chars[pos] == c) {
|
||||
int size = pos - start;
|
||||
if (size > 0) {
|
||||
result[i++] = new String(chars, start, size);
|
||||
}
|
||||
start = pos + 1;
|
||||
}
|
||||
}
|
||||
int size = pos - start;
|
||||
if (size > 0) {
|
||||
result[i++] = new String(chars, start, size);
|
||||
}
|
||||
if (i != count) {
|
||||
String[] result1 = new String[i];
|
||||
System.arraycopy(result, 0, result1, 0, i);
|
||||
return result1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, String> getAsMap() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAsStructuredMap() {
|
||||
TinyMap.Builder<String, Object> stringObjectMap = TinyMap.builder();
|
||||
for (String key : map.keySet()) {
|
||||
String value = map.get(key);
|
||||
processSetting(stringObjectMap, "", key, value);
|
||||
}
|
||||
for (String key : stringObjectMap.keySet()) {
|
||||
Object object = stringObjectMap.get(key);
|
||||
if (object instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> valMap = (Map<String, Object>) object;
|
||||
stringObjectMap.put(key, convertMapsToArrays(valMap));
|
||||
}
|
||||
}
|
||||
return stringObjectMap.build();
|
||||
}
|
||||
|
||||
public Settings getByPrefix(String prefix) {
|
||||
Builder builder = new Builder();
|
||||
for (String key : map.keySet()) {
|
||||
String value = map.get(key);
|
||||
if (key.startsWith(prefix)) {
|
||||
if (key.length() < prefix.length()) {
|
||||
continue;
|
||||
}
|
||||
builder.put(key.substring(prefix.length()), value);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public Settings getAsSettings(String setting) {
|
||||
return getByPrefix(setting + ".");
|
||||
}
|
||||
|
||||
public boolean containsSetting(String setting) {
|
||||
if (map.containsKey(setting)) {
|
||||
return true;
|
||||
}
|
||||
for (String key : map.keySet()) {
|
||||
if (key.startsWith(setting)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String get(String setting) {
|
||||
return map.get(setting);
|
||||
}
|
||||
|
||||
public String get(String setting, String defaultValue) {
|
||||
String s = map.get(setting);
|
||||
return s == null ? defaultValue : s;
|
||||
}
|
||||
|
||||
public Float getAsFloat(String setting, Float defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Float.parseFloat(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse float setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Double getAsDouble(String setting, Double defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Double.parseDouble(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse double setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getAsInt(String setting, Integer defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse int setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Long getAsLong(String setting, Long defaultValue) {
|
||||
String s = get(setting);
|
||||
try {
|
||||
return s == null ? defaultValue : Long.parseLong(s);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SettingsException("Failed to parse long setting [" + setting + "] with value [" + s + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean getAsBoolean(String setting, Boolean defaultValue) {
|
||||
String value = get(setting);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return !("false".equals(value) || "0".equals(value) || "off".equals(value) || "no".equals(value));
|
||||
}
|
||||
|
||||
public TimeValue getAsTime(String setting, TimeValue defaultValue) {
|
||||
return TimeValue.parseTimeValue(get(setting), defaultValue);
|
||||
}
|
||||
|
||||
public ByteSizeValue getAsBytesSize(String setting, ByteSizeValue defaultValue) {
|
||||
return ByteSizeValue.parseBytesSizeValue(get(setting), defaultValue);
|
||||
}
|
||||
|
||||
public String[] getAsArray(String settingPrefix) {
|
||||
return getAsArray(settingPrefix, EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
public String[] getAsArray(String settingPrefix, String[] defaultArray) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (get(settingPrefix) != null) {
|
||||
String[] strings = splitStringByCommaToArray(get(settingPrefix));
|
||||
if (strings.length > 0) {
|
||||
for (String string : strings) {
|
||||
result.add(string.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = get(settingPrefix + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
result.add(value.trim());
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
return defaultArray;
|
||||
}
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public Map<String, Settings> getGroups(String prefix) {
|
||||
String settingPrefix = prefix;
|
||||
if (settingPrefix.charAt(settingPrefix.length() - 1) != '.') {
|
||||
settingPrefix = settingPrefix + ".";
|
||||
}
|
||||
// we don't really care that it might happen twice
|
||||
TinyMap.Builder<String, TinyMap.Builder<String, String>> hashMap = TinyMap.builder();
|
||||
for (String o : this.map.keySet()) {
|
||||
if (o.startsWith(settingPrefix)) {
|
||||
String nameValue = o.substring(settingPrefix.length());
|
||||
int dotIndex = nameValue.indexOf('.');
|
||||
if (dotIndex == -1) {
|
||||
throw new SettingsException("failed to get setting group for ["
|
||||
+ settingPrefix
|
||||
+ "] setting prefix and setting [" + o + "] because of a missing '.'");
|
||||
}
|
||||
String name = nameValue.substring(0, dotIndex);
|
||||
String value = nameValue.substring(dotIndex + 1);
|
||||
Map<String, String> groupSettings = hashMap.computeIfAbsent(name, k -> TinyMap.builder());
|
||||
groupSettings.put(value, get(o));
|
||||
}
|
||||
}
|
||||
TinyMap.Builder<String, Settings> retVal = TinyMap.builder();
|
||||
for (String key : hashMap.keySet()) {
|
||||
TinyMap.Builder<String, String> value = hashMap.get(key);
|
||||
retVal.put(key, new Settings(value.build()));
|
||||
}
|
||||
return retVal.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || !(o == null || getClass() != o.getClass()) && map.equals(((Settings) o).map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return map.hashCode();
|
||||
}
|
||||
|
||||
private void processSetting(Map<String, Object> map, String prefix, String setting, String value) {
|
||||
int prefixLength = setting.indexOf('.');
|
||||
if (prefixLength == -1) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> innerMap = (Map<String, Object>) map.get(prefix + setting);
|
||||
if (innerMap != null) {
|
||||
for (String k : innerMap.keySet()) {
|
||||
Object v = innerMap.get(k);
|
||||
map.put(prefix + setting + "." + k, v);
|
||||
}
|
||||
}
|
||||
map.put(prefix + setting, value);
|
||||
} else {
|
||||
String key = setting.substring(0, prefixLength);
|
||||
String rest = setting.substring(prefixLength + 1);
|
||||
Object existingValue = map.get(prefix + key);
|
||||
if (existingValue == null) {
|
||||
Map<String, Object> newMap = TinyMap.builder();
|
||||
processSetting(newMap, "", rest, value);
|
||||
map.put(key, newMap);
|
||||
} else {
|
||||
if (existingValue instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> innerMap = (Map<String, Object>) existingValue;
|
||||
processSetting(innerMap, "", rest, value);
|
||||
map.put(key, innerMap);
|
||||
} else {
|
||||
processSetting(map, prefix + key + ".", rest, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object convertMapsToArrays(Map<String, Object> map) {
|
||||
if (map.isEmpty()) {
|
||||
return map;
|
||||
}
|
||||
boolean isArray = true;
|
||||
int maxIndex = -1;
|
||||
for (String key : map.keySet()) {
|
||||
Object value = map.get(key);
|
||||
if (isArray) {
|
||||
try {
|
||||
int index = Integer.parseInt(key);
|
||||
if (index >= 0) {
|
||||
maxIndex = Math.max(maxIndex, index);
|
||||
} else {
|
||||
isArray = false;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
isArray = false;
|
||||
}
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> valMap = (Map<String, Object>) value;
|
||||
map.put(key, convertMapsToArrays(valMap));
|
||||
}
|
||||
}
|
||||
if (isArray && (maxIndex + 1) == map.size()) {
|
||||
ArrayList<Object> newValue = new ArrayList<>(maxIndex + 1);
|
||||
for (int i = 0; i <= maxIndex; i++) {
|
||||
Object obj = map.get(Integer.toString(i));
|
||||
if (obj == null) {
|
||||
return map;
|
||||
}
|
||||
newValue.add(obj);
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final SettingsLoaderService settingsLoaderService = SettingsLoaderService.getInstance();
|
||||
|
||||
private final TinyMap.Builder<String, String> map;
|
||||
|
||||
private Builder() {
|
||||
map = TinyMap.builder();
|
||||
}
|
||||
|
||||
public Map<String, String> map() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public String remove(String key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param value The setting value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String key, String value) {
|
||||
map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and class as value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param clazz The setting class value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String key, Class<?> clazz) {
|
||||
map.put(key, clazz.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the boolean value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The boolean value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, boolean value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the int value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The int value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, int value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the long value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The long value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, long value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the float value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The float value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, float value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the double value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The double value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, double value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder putArray(String setting, String... values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
put(setting + '.' + i, values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder putArray(String setting, List<String> values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
put(setting + '.' + i, values.get(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting group.
|
||||
* @param settingPrefix setting prefix
|
||||
* @param groupName group name
|
||||
* @param settings settings
|
||||
* @param values values
|
||||
* @return a builder
|
||||
* @throws SettingsException if setting fails
|
||||
*/
|
||||
public Builder put(String settingPrefix, String groupName, String[] settings, String[] values)
|
||||
throws SettingsException {
|
||||
if (settings.length != values.length) {
|
||||
throw new SettingsException("the settings length must match the value length");
|
||||
}
|
||||
for (int i = 0; i < settings.length; i++) {
|
||||
if (values[i] == null) {
|
||||
continue;
|
||||
}
|
||||
put(settingPrefix + "" + groupName + "." + settings[i], values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
* @param settings settings
|
||||
* @return builder
|
||||
*/
|
||||
public Builder put(Settings settings) {
|
||||
map.putAll(settings.getAsMap());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
*
|
||||
* @param settings settings
|
||||
* @return a builder
|
||||
*/
|
||||
public Builder put(Map<String, String> settings) {
|
||||
map.putAll(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from the actual string content that represents them using the
|
||||
* {@link SettingsLoaderService#loaderFromString(String)}.
|
||||
*
|
||||
* @param source source
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromString(String source) {
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromString(source);
|
||||
try {
|
||||
Map<String, String> loadedSettings = settingsLoader.load(source);
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("failed to load settings from [" + source + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from a resource.
|
||||
* @param resourceName resource name
|
||||
* @param inputStream input stream
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromResource(String resourceName, InputStream inputStream) throws SettingsException {
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromResource(resourceName);
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||
Map<String, String> loadedSettings = settingsLoader.load(bufferedReader.lines().collect(Collectors.joining()));
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("failed to load settings from [" + resourceName + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system properties to this settings.
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromSystemProperties() {
|
||||
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
|
||||
put((String) entry.getKey(), (String) entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system environment to this settings.
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromSystemEnvironment() {
|
||||
for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder,
|
||||
PlaceholderResolver placeholderResolver) {
|
||||
map.replaceAll((k, v) -> propertyPlaceholder.replacePlaceholders(v, placeholderResolver));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs across all the settings set on this builder and replaces {@code ${...}} elements in the
|
||||
* each setting value according to the following logic:
|
||||
*
|
||||
* First, tries to resolve it against a System property ({@link System#getProperty(String)}), next,
|
||||
* tries and resolve it against an environment variable ({@link System#getenv(String)}), next,
|
||||
* tries and resolve it against a date pattern to resolve the current date,
|
||||
* and last, tries and replace it with another setting already set on this builder.
|
||||
* @return builder
|
||||
*/
|
||||
public Builder replacePropertyPlaceholders() {
|
||||
return replacePropertyPlaceholders(new PropertyPlaceholder("${", "}", false),
|
||||
placeholderName -> {
|
||||
// system property
|
||||
String value = System.getProperty(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// environment
|
||||
value = System.getenv(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// current date
|
||||
try {
|
||||
return DateTimeFormatter.ofPattern(placeholderName).format(LocalDate.now());
|
||||
} catch (IllegalArgumentException | DateTimeException e) {
|
||||
return map.get(placeholderName);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public Settings build() {
|
||||
return new Settings(map.build());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package org.xbib.content.settings.datastructures;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides the ability to load settings from
|
||||
* the actual source content that represents them.
|
||||
*/
|
||||
public interface SettingsLoader {
|
||||
|
||||
/**
|
||||
* Suffices for file names to load from.
|
||||
* @return a set of suffices
|
||||
*/
|
||||
Set<String> suffixes();
|
||||
|
||||
/**
|
||||
* Loads the settings from a source string.
|
||||
* @param source the source
|
||||
* @return a Map
|
||||
* @throws IOException if load fails
|
||||
*/
|
||||
Map<String, String> load(String source) throws IOException;
|
||||
|
||||
Map<String, String> load(Map<String, Object> source) throws IOException;
|
||||
|
||||
boolean canLoad(String source);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.xbib.content.settings.datastructures.DatastructureSettingsBuilder
|
|
@ -1,7 +1,7 @@
|
|||
package org.xbib.content.settings.datastructures.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.content.settings.datastructures.Settings;
|
||||
import org.xbib.content.Settings;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -18,8 +18,8 @@ public class SettingsTest {
|
|||
|
||||
@Test
|
||||
public void testEmpty() {
|
||||
Settings settings = Settings.EMPTY_SETTINGS;
|
||||
assertTrue(settings.getAsMap().isEmpty());
|
||||
Settings settings = Settings.emptySettings();
|
||||
assertTrue(settings.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
import org.xbib.content.SettingsBuilder;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.content.settings.ContentSettingsBuilder;
|
||||
import org.xbib.content.settings.PropertiesSettingsLoader;
|
||||
|
||||
module org.xbib.content.settings {
|
||||
uses org.xbib.content.SettingsLoader;
|
||||
uses SettingsLoader;
|
||||
provides SettingsLoader with PropertiesSettingsLoader;
|
||||
uses SettingsBuilder;
|
||||
provides SettingsBuilder with ContentSettingsBuilder;
|
||||
exports org.xbib.content.settings;
|
||||
requires org.xbib.content.core;
|
||||
requires org.xbib.datastructures.api;
|
||||
requires transitive org.xbib.datastructures.tiny;
|
||||
}
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
package org.xbib.content.settings;
|
||||
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.datastructures.api.ByteSizeValue;
|
||||
import org.xbib.datastructures.api.TimeValue;
|
||||
import org.xbib.datastructures.tiny.TinyMap;
|
||||
import org.xbib.content.util.unit.ByteSizeValue;
|
||||
import org.xbib.content.util.unit.TimeValue;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -25,12 +19,9 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Settings implements AutoCloseable {
|
||||
public class ContentSettings implements Settings, AutoCloseable {
|
||||
|
||||
public static final Settings EMPTY_SETTINGS = new Builder().build();
|
||||
public static final ContentSettings EMPTY_SETTINGS = new ContentSettingsBuilder().build();
|
||||
|
||||
public static final String[] EMPTY_ARRAY = new String[0];
|
||||
|
||||
|
@ -40,11 +31,11 @@ public class Settings implements AutoCloseable {
|
|||
|
||||
private Map<String, String> map;
|
||||
|
||||
private Settings(Map<String, String> map) {
|
||||
protected ContentSettings(Map<String, String> map) {
|
||||
this(map, null, 0L, 0L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private Settings(Map<String, String> map, Path path, long initialDelay, long period, TimeUnit timeUnit) {
|
||||
protected ContentSettings(Map<String, String> map, Path path, long initialDelay, long period, TimeUnit timeUnit) {
|
||||
TinyMap.Builder<String, String> builder = TinyMap.builder();
|
||||
builder.putAll(map);
|
||||
this.map = builder.build();
|
||||
|
@ -53,28 +44,20 @@ public class Settings implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
public static Settings readSettingsFromMap(Map<String, Object> map) {
|
||||
Builder builder = new Builder();
|
||||
public static ContentSettings readSettingsFromMap(Map<String, Object> map) {
|
||||
ContentSettingsBuilder builder = new ContentSettingsBuilder();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
builder.put(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : null);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static void writeSettingsToMap(Settings settings, Map<String, Object> map) {
|
||||
public static void writeSettingsToMap(ContentSettings settings, Map<String, Object> map) {
|
||||
for (String key : settings.getAsMap().keySet()) {
|
||||
map.put(key, settings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a builder to be used in order to build settings.
|
||||
* @return a builder
|
||||
*/
|
||||
public static Builder settingsBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static String[] splitStringByCommaToArray(final String s) {
|
||||
return splitStringToArray(s, ',');
|
||||
}
|
||||
|
@ -136,10 +119,12 @@ public class Settings implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAsMap() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAsStructuredMap() {
|
||||
TinyMap.Builder<String, Object> stringObjectMap = TinyMap.builder();
|
||||
for (Map.Entry<String, String> entry : this.map.entrySet()) {
|
||||
|
@ -155,8 +140,9 @@ public class Settings implements AutoCloseable {
|
|||
return stringObjectMap.build();
|
||||
}
|
||||
|
||||
public Settings getByPrefix(String prefix) {
|
||||
Builder builder = new Builder();
|
||||
@Override
|
||||
public ContentSettings getByPrefix(String prefix) {
|
||||
ContentSettingsBuilder builder = new ContentSettingsBuilder();
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
if (entry.getKey().startsWith(prefix)) {
|
||||
if (entry.getKey().length() < prefix.length()) {
|
||||
|
@ -168,10 +154,12 @@ public class Settings implements AutoCloseable {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
public Settings getAsSettings(String setting) {
|
||||
@Override
|
||||
public ContentSettings getAsSettings(String setting) {
|
||||
return getByPrefix(setting + ".");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSetting(String setting) {
|
||||
if (map.containsKey(setting)) {
|
||||
return true;
|
||||
|
@ -184,15 +172,23 @@ public class Settings implements AutoCloseable {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String setting) {
|
||||
return map.get(setting);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String setting, String defaultValue) {
|
||||
String retVal = map.get(setting);
|
||||
return retVal == null ? defaultValue : retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getAsFloat(String setting, Float defaultValue) {
|
||||
String sValue = get(setting);
|
||||
if (sValue == null) {
|
||||
|
@ -205,6 +201,7 @@ public class Settings implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAsDouble(String setting, Double defaultValue) {
|
||||
String sValue = get(setting);
|
||||
if (sValue == null) {
|
||||
|
@ -217,6 +214,7 @@ public class Settings implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getAsInt(String setting, Integer defaultValue) {
|
||||
String sValue = get(setting);
|
||||
if (sValue == null) {
|
||||
|
@ -229,6 +227,7 @@ public class Settings implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getAsLong(String setting, Long defaultValue) {
|
||||
String sValue = get(setting);
|
||||
if (sValue == null) {
|
||||
|
@ -241,6 +240,7 @@ public class Settings implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getAsBoolean(String setting, Boolean defaultValue) {
|
||||
String value = get(setting);
|
||||
if (value == null) {
|
||||
|
@ -249,18 +249,22 @@ public class Settings implements AutoCloseable {
|
|||
return !("false".equals(value) || "0".equals(value) || "off".equals(value) || "no".equals(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeValue getAsTime(String setting, TimeValue defaultValue) {
|
||||
return TimeValue.parseTimeValue(get(setting), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteSizeValue getAsBytesSize(String setting, ByteSizeValue defaultValue) {
|
||||
return ByteSizeValue.parseBytesSizeValue(get(setting), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAsArray(String settingPrefix) {
|
||||
return getAsArray(settingPrefix, EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAsArray(String settingPrefix, String[] defaultArray) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (get(settingPrefix) != null) {
|
||||
|
@ -285,7 +289,8 @@ public class Settings implements AutoCloseable {
|
|||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
public Map<String, Settings> getGroups(String prefix) {
|
||||
@Override
|
||||
public Map<String, org.xbib.content.Settings> getGroups(String prefix) {
|
||||
String settingPrefix = prefix;
|
||||
if (settingPrefix.charAt(settingPrefix.length() - 1) != '.') {
|
||||
settingPrefix = settingPrefix + ".";
|
||||
|
@ -308,16 +313,16 @@ public class Settings implements AutoCloseable {
|
|||
groupSettings.put(value, get(setting));
|
||||
}
|
||||
}
|
||||
TinyMap.Builder<String, Settings> retVal = TinyMap.builder();
|
||||
TinyMap.Builder<String, org.xbib.content.Settings> retVal = TinyMap.builder();
|
||||
for (Map.Entry<String, Map<String, String>> entry : hashMap.entrySet()) {
|
||||
retVal.put(entry.getKey(), new Settings(entry.getValue()));
|
||||
retVal.put(entry.getKey(), new ContentSettings(entry.getValue()));
|
||||
}
|
||||
return retVal.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || !(o == null || getClass() != o.getClass()) && map.equals(((Settings) o).map);
|
||||
return this == o || !(o == null || getClass() != o.getClass()) && map.equals(((ContentSettings) o).map);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -325,6 +330,13 @@ public class Settings implements AutoCloseable {
|
|||
return map.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (refresher != null) {
|
||||
refresher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void processSetting(Map<String, Object> map, String prefix, String setting, String value) {
|
||||
int prefixLength = setting.indexOf('.');
|
||||
if (prefixLength == -1) {
|
||||
|
@ -396,340 +408,6 @@ public class Settings implements AutoCloseable {
|
|||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (refresher != null) {
|
||||
refresher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final Map<String, String> map;
|
||||
|
||||
private Path path;
|
||||
|
||||
private long initialDelay;
|
||||
|
||||
private long period;
|
||||
|
||||
private TimeUnit timeUnit;
|
||||
|
||||
private Builder() {
|
||||
map = TinyMap.builder();
|
||||
}
|
||||
|
||||
public Map<String, String> map() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public String remove(String key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param value The setting value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String key, String value) {
|
||||
map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and class as value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param clazz The setting class value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String key, Class<?> clazz) {
|
||||
map.put(key, clazz.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the boolean value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The boolean value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, boolean value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the int value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The int value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, int value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the long value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The long value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, long value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the float value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The float value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, float value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the double value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The double value
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder put(String setting, double value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder putArray(String setting, String... values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
put(setting + '.' + i, values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
public Builder putArray(String setting, List<String> values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
put(setting + '.' + i, values.get(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting group.
|
||||
* @param settingPrefix setting prefix
|
||||
* @param groupName group name
|
||||
* @param settings settings
|
||||
* @param values values
|
||||
* @return a builder
|
||||
* @throws SettingsException if setting fails
|
||||
*/
|
||||
public Builder put(String settingPrefix, String groupName, String[] settings, String[] values)
|
||||
throws SettingsException {
|
||||
if (settings.length != values.length) {
|
||||
throw new SettingsException("The settings length must match the value length");
|
||||
}
|
||||
for (int i = 0; i < settings.length; i++) {
|
||||
if (values[i] == null) {
|
||||
continue;
|
||||
}
|
||||
put(settingPrefix + "" + groupName + "." + settings[i], values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
* @param settings settings
|
||||
* @return builder
|
||||
*/
|
||||
public Builder put(Settings settings) {
|
||||
map.putAll(settings.getAsMap());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
*
|
||||
* @param settings settings
|
||||
* @return a builder
|
||||
*/
|
||||
public Builder put(Map<String, String> settings) {
|
||||
map.putAll(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from the actual string content that represents them using the
|
||||
* {@link SettingsLoaderService#loaderFromString(String)}.
|
||||
*
|
||||
* @param source source
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromString(String source) {
|
||||
SettingsLoaderService settingsLoaderService = new SettingsLoaderService();
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromString(source);
|
||||
try {
|
||||
Map<String, String> loadedSettings = settingsLoader.load(source);
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("Failed to load settings from [" + source + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from an URL.
|
||||
* @param url url
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromUrl(URL url) throws SettingsException {
|
||||
try {
|
||||
return loadFromStream(url.toExternalForm(), url.openStream());
|
||||
} catch (IOException e) {
|
||||
throw new SettingsException("Failed to open stream for url [" + url.toExternalForm() + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from a stream.
|
||||
* @param resourceName resource name
|
||||
* @param inputStream input stream
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromStream(String resourceName, InputStream inputStream) throws SettingsException {
|
||||
SettingsLoaderService settingsLoaderService = new SettingsLoaderService();
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromResource(resourceName);
|
||||
try {
|
||||
Map<String, String> loadedSettings = settingsLoader
|
||||
.load(copyToString(new InputStreamReader(inputStream, StandardCharsets.UTF_8)));
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("Failed to load settings from [" + resourceName + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system properties to this settings.
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromSystemProperties() {
|
||||
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
|
||||
put((String) entry.getKey(), (String) entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system environment to this settings.
|
||||
* @return builder
|
||||
*/
|
||||
public Builder loadFromSystemEnvironment() {
|
||||
for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Runs across all the settings set on this builder and replaces {@code ${...}} elements in the
|
||||
* each setting value according to the following logic:
|
||||
*
|
||||
* First, tries to resolve it against a System property ({@link System#getProperty(String)}), next,
|
||||
* tries and resolve it against an environment variable ({@link System#getenv(String)}), next,
|
||||
* tries and resolve it against a date pattern to resolve the current date,
|
||||
* and last, tries and replace it with another setting already set on this builder.
|
||||
* @return builder
|
||||
*/
|
||||
public Builder replacePropertyPlaceholders() {
|
||||
return replacePropertyPlaceholders(new PropertyPlaceholder("${", "}", false),
|
||||
placeholderName -> {
|
||||
// system property
|
||||
String value = System.getProperty(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// environment
|
||||
value = System.getenv(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// current date
|
||||
try {
|
||||
return DateTimeFormatter.ofPattern(placeholderName).format(LocalDate.now());
|
||||
} catch (IllegalArgumentException | DateTimeException e) {
|
||||
return map.get(placeholderName);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public Builder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder,
|
||||
PlaceholderResolver placeholderResolver) {
|
||||
map.replaceAll((k, v) -> propertyPlaceholder.replacePlaceholders(v, placeholderResolver));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRefresh(Path path, long initialDelay, long period, TimeUnit timeUnit) {
|
||||
this.path = path;
|
||||
this.initialDelay = initialDelay;
|
||||
this.period = period;
|
||||
this.timeUnit = timeUnit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Settings build() {
|
||||
return new Settings(map, path, initialDelay, period, timeUnit);
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultSettingsRefresher implements Runnable {
|
||||
|
||||
private final Path path;
|
|
@ -0,0 +1,363 @@
|
|||
package org.xbib.content.settings;
|
||||
|
||||
import org.xbib.content.PlaceholderResolver;
|
||||
import org.xbib.content.PropertyPlaceholder;
|
||||
import org.xbib.content.Settings;
|
||||
import org.xbib.content.SettingsBuilder;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
import org.xbib.datastructures.tiny.TinyMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ContentSettingsBuilder implements SettingsBuilder {
|
||||
|
||||
private final Map<String, String> map;
|
||||
|
||||
private Path path;
|
||||
|
||||
private long initialDelay;
|
||||
|
||||
private long period;
|
||||
|
||||
private TimeUnit timeUnit;
|
||||
|
||||
public ContentSettingsBuilder() {
|
||||
map = TinyMap.builder();
|
||||
}
|
||||
|
||||
public Map<String, String> map() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public String remove(String key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param value The setting value
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(String key, String value) {
|
||||
map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting with the provided setting key and class as value.
|
||||
*
|
||||
* @param key The setting key
|
||||
* @param clazz The setting class value
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(String key, Class<?> clazz) {
|
||||
map.put(key, clazz.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the boolean value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The boolean value
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(String setting, boolean value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the int value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The int value
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(String setting, int value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the long value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The long value
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(String setting, long value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the float value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The float value
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(String setting, float value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and the double value.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param value The double value
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(String setting, double value) {
|
||||
put(setting, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder putArray(String setting, String... values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
put(setting + '.' + i, values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting with the provided setting key and an array of values.
|
||||
*
|
||||
* @param setting The setting key
|
||||
* @param values The values
|
||||
* @return The builder
|
||||
*/
|
||||
public ContentSettingsBuilder putArray(String setting, List<String> values) {
|
||||
remove(setting);
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
String value = map.remove(setting + '.' + (counter++));
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
put(setting + '.' + i, values.get(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting group.
|
||||
*
|
||||
* @param settingPrefix setting prefix
|
||||
* @param groupName group name
|
||||
* @param settings settings
|
||||
* @param values values
|
||||
* @return a builder
|
||||
* @throws SettingsException if setting fails
|
||||
*/
|
||||
public ContentSettingsBuilder put(String settingPrefix, String groupName, String[] settings, String[] values)
|
||||
throws SettingsException {
|
||||
if (settings.length != values.length) {
|
||||
throw new SettingsException("The settings length must match the value length");
|
||||
}
|
||||
for (int i = 0; i < settings.length; i++) {
|
||||
if (values[i] == null) {
|
||||
continue;
|
||||
}
|
||||
put(settingPrefix + "" + groupName + "." + settings[i], values[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
*
|
||||
* @param settings settings
|
||||
* @return builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(Settings settings) {
|
||||
map.putAll(settings.getAsMap());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the provided settings.
|
||||
*
|
||||
* @param settings settings
|
||||
* @return a builder
|
||||
*/
|
||||
public ContentSettingsBuilder put(Map<String, String> settings) {
|
||||
map.putAll(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from the actual string content that represents them using the
|
||||
* {@link SettingsLoaderService#loaderFromString(String)}.
|
||||
*
|
||||
* @param source source
|
||||
* @return builder
|
||||
*/
|
||||
public ContentSettingsBuilder loadFromString(String source) {
|
||||
SettingsLoaderService settingsLoaderService = new SettingsLoaderService();
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromString(source);
|
||||
try {
|
||||
Map<String, String> loadedSettings = settingsLoader.load(source);
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("Failed to load settings from [" + source + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from an URL.
|
||||
*
|
||||
* @param url url
|
||||
* @return builder
|
||||
*/
|
||||
public ContentSettingsBuilder loadFromUrl(URL url) throws SettingsException {
|
||||
try {
|
||||
return loadFromResource(url.toExternalForm(), url.openStream());
|
||||
} catch (IOException e) {
|
||||
throw new SettingsException("Failed to open stream for url [" + url.toExternalForm() + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from a stream.
|
||||
*
|
||||
* @param resourceName resource name
|
||||
* @param inputStream input stream
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public ContentSettingsBuilder loadFromResource(String resourceName, InputStream inputStream) throws SettingsException {
|
||||
SettingsLoaderService settingsLoaderService = new SettingsLoaderService();
|
||||
SettingsLoader settingsLoader = settingsLoaderService.loaderFromResource(resourceName);
|
||||
try {
|
||||
Map<String, String> loadedSettings = settingsLoader
|
||||
.load(ContentSettings.copyToString(new InputStreamReader(inputStream, StandardCharsets.UTF_8)));
|
||||
put(loadedSettings);
|
||||
} catch (Exception e) {
|
||||
throw new SettingsException("Failed to load settings from [" + resourceName + "]", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system properties to this settings.
|
||||
*
|
||||
* @return builder
|
||||
*/
|
||||
@Override
|
||||
public ContentSettingsBuilder loadFromSystemProperties() {
|
||||
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
|
||||
put((String) entry.getKey(), (String) entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load system environment to this settings.
|
||||
*
|
||||
* @return builder
|
||||
*/
|
||||
public ContentSettingsBuilder loadFromSystemEnvironment() {
|
||||
for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs across all the settings set on this builder and replaces {@code ${...}} elements in the
|
||||
* each setting value according to the following logic:
|
||||
* <p>
|
||||
* First, tries to resolve it against a System property ({@link System#getProperty(String)}), next,
|
||||
* tries and resolve it against an environment variable ({@link System#getenv(String)}), next,
|
||||
* tries and resolve it against a date pattern to resolve the current date,
|
||||
* and last, tries and replace it with another setting already set on this builder.
|
||||
*
|
||||
* @return builder
|
||||
*/
|
||||
public ContentSettingsBuilder replacePropertyPlaceholders() {
|
||||
return replacePropertyPlaceholders(new PropertyPlaceholder("${", "}", false),
|
||||
placeholderName -> {
|
||||
// system property
|
||||
String value = System.getProperty(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// environment
|
||||
value = System.getenv(placeholderName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
// current date
|
||||
try {
|
||||
return DateTimeFormatter.ofPattern(placeholderName).format(LocalDate.now());
|
||||
} catch (IllegalArgumentException | DateTimeException e) {
|
||||
return map.get(placeholderName);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public ContentSettingsBuilder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder,
|
||||
PlaceholderResolver placeholderResolver) {
|
||||
map.replaceAll((k, v) -> propertyPlaceholder.replacePlaceholders(v, placeholderResolver));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentSettingsBuilder setRefresh(Path path, long initialDelay, long period, TimeUnit timeUnit) {
|
||||
this.path = path;
|
||||
this.initialDelay = initialDelay;
|
||||
this.period = period;
|
||||
this.timeUnit = timeUnit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentSettings build() {
|
||||
return new ContentSettings(map, path, initialDelay, period, timeUnit);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.content.properties;
|
||||
package org.xbib.content.settings;
|
||||
|
||||
import org.xbib.content.io.BytesReference;
|
||||
import org.xbib.content.SettingsLoader;
|
||||
|
@ -39,17 +39,6 @@ public class PropertiesSettingsLoader implements SettingsLoader {
|
|||
}
|
||||
}
|
||||
|
||||
public Map<String, String> load(BytesReference ref) throws IOException {
|
||||
Properties props = new Properties();
|
||||
try (Reader reader = new InputStreamReader(ref.streamInput(), StandardCharsets.UTF_8)) {
|
||||
props.load(reader);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
result.put((String) entry.getKey(), (String) entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> load(Map<String, Object> source) throws IOException {
|
||||
|
@ -66,4 +55,16 @@ public class PropertiesSettingsLoader implements SettingsLoader {
|
|||
public boolean canLoad(String source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Map<String, String> load(BytesReference ref) throws IOException {
|
||||
Properties props = new Properties();
|
||||
try (Reader reader = new InputStreamReader(ref.streamInput(), StandardCharsets.UTF_8)) {
|
||||
props.load(reader);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
result.put((String) entry.getKey(), (String) entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package org.xbib.content.settings;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PropertyPlaceholder {
|
||||
|
||||
private final String placeholderPrefix;
|
||||
|
||||
private final String placeholderSuffix;
|
||||
|
||||
private final boolean ignoreUnresolvablePlaceholders;
|
||||
|
||||
/**
|
||||
* Creates a new <code>PropertyPlaceholderHelper</code> that uses the supplied prefix and suffix.
|
||||
*
|
||||
* @param placeholderPrefix the prefix that denotes the start of a placeholder.
|
||||
* @param placeholderSuffix the suffix that denotes the end of a placeholder.
|
||||
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should be ignored
|
||||
* (<code>true</code>) or cause an exception (<code>false</code>).
|
||||
*/
|
||||
public PropertyPlaceholder(String placeholderPrefix, String placeholderSuffix,
|
||||
boolean ignoreUnresolvablePlaceholders) {
|
||||
this.placeholderPrefix = placeholderPrefix;
|
||||
this.placeholderSuffix = placeholderSuffix;
|
||||
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all placeholders of format <code>${name}</code> with the value returned from the supplied {@link
|
||||
* PlaceholderResolver}.
|
||||
*
|
||||
* @param value the value containing the placeholders to be replaced.
|
||||
* @param placeholderResolver the <code>PlaceholderResolver</code> to use for replacement.
|
||||
* @return the supplied value with placeholders replaced inline.
|
||||
*/
|
||||
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
|
||||
return parseStringValue(value, placeholderResolver, new HashSet<String>());
|
||||
}
|
||||
|
||||
protected String parseStringValue(String strVal, PlaceholderResolver placeholderResolver,
|
||||
Set<String> visitedPlaceholders) {
|
||||
StringBuilder buf = new StringBuilder(strVal);
|
||||
int startIndex = strVal.indexOf(this.placeholderPrefix);
|
||||
while (startIndex != -1) {
|
||||
int endIndex = findPlaceholderEndIndex(buf, startIndex);
|
||||
if (endIndex != -1) {
|
||||
String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
|
||||
if (!visitedPlaceholders.add(placeholder)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Circular placeholder reference '" + placeholder + "' in property definitions");
|
||||
}
|
||||
// Recursive invocation, parsing placeholders contained in the placeholder key.
|
||||
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
|
||||
// Now obtain the value for the fully resolved key...
|
||||
int defaultValueIdx = placeholder.indexOf(':');
|
||||
String defaultValue = null;
|
||||
if (defaultValueIdx != -1) {
|
||||
defaultValue = placeholder.substring(defaultValueIdx + 1);
|
||||
placeholder = placeholder.substring(0, defaultValueIdx);
|
||||
}
|
||||
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
|
||||
if (propVal == null) {
|
||||
propVal = defaultValue;
|
||||
}
|
||||
if (propVal != null) {
|
||||
// Recursive invocation, parsing placeholders contained in the
|
||||
// previously resolved placeholder value.
|
||||
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
|
||||
buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
|
||||
startIndex = buf.indexOf(this.placeholderPrefix, startIndex + propVal.length());
|
||||
} else if (this.ignoreUnresolvablePlaceholders) {
|
||||
// Proceed with unprocessed value.
|
||||
startIndex = buf.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'");
|
||||
}
|
||||
visitedPlaceholders.remove(placeholder);
|
||||
} else {
|
||||
startIndex = -1;
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
|
||||
int index = startIndex + this.placeholderPrefix.length();
|
||||
int withinNestedPlaceholder = 0;
|
||||
while (index < buf.length()) {
|
||||
if (substringMatch(buf, index, this.placeholderSuffix)) {
|
||||
if (withinNestedPlaceholder > 0) {
|
||||
withinNestedPlaceholder--;
|
||||
index = index + this.placeholderPrefix.length() - 1;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
} else if (substringMatch(buf, index, this.placeholderPrefix)) {
|
||||
withinNestedPlaceholder++;
|
||||
index = index + this.placeholderPrefix.length();
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private boolean substringMatch(CharSequence str, int index, CharSequence substring) {
|
||||
for (int j = 0; j < substring.length(); j++) {
|
||||
int i = index + j;
|
||||
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.xbib.content.settings;
|
||||
|
||||
/**
|
||||
* A generic failure to handle settings.
|
||||
*/
|
||||
public class SettingsException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -1833327708622505101L;
|
||||
|
||||
public SettingsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SettingsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package org.xbib.content.settings;
|
||||
|
||||
import org.xbib.content.SettingsLoader;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A settings loader service for loading {@link SettingsLoader} implementations.
|
||||
*/
|
||||
public final class SettingsLoaderService {
|
||||
|
||||
private final Map<Set<String>, SettingsLoader> settingsLoaderMap;
|
||||
|
||||
public SettingsLoaderService() {
|
||||
this.settingsLoaderMap = new HashMap<>();
|
||||
ServiceLoader<SettingsLoader> serviceLoader = ServiceLoader.load(SettingsLoader.class);
|
||||
for (SettingsLoader settingsLoader : serviceLoader) {
|
||||
if (!settingsLoaderMap.containsKey(settingsLoader.suffixes())) {
|
||||
settingsLoaderMap.put(settingsLoader.suffixes(), settingsLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link SettingsLoader} based on the resource name.
|
||||
* @param resourceName the resource
|
||||
* @return the settings loader
|
||||
*/
|
||||
public SettingsLoader loaderFromResource(String resourceName) {
|
||||
for (Map.Entry<Set<String>, SettingsLoader> entry : settingsLoaderMap.entrySet()) {
|
||||
Set<String> suffixes = entry.getKey();
|
||||
for (String suffix : suffixes) {
|
||||
if (resourceName.endsWith("." + suffix)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link SettingsLoader} based on the actual source.
|
||||
* @param source the source
|
||||
* @return the settings loader
|
||||
*/
|
||||
public SettingsLoader loaderFromString(String source) {
|
||||
for (SettingsLoader loader : settingsLoaderMap.values()) {
|
||||
if (loader.canLoad(source)) {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.xbib.content.settings.ContentSettingsBuilder
|
|
@ -0,0 +1 @@
|
|||
org.xbib.content.settings.PropertiesSettingsLoader
|
|
@ -10,7 +10,7 @@ import org.xbib.content.io.BytesArray;
|
|||
import org.xbib.content.io.BytesReference;
|
||||
import org.xbib.content.json.JsonSettingsLoader;
|
||||
import org.xbib.content.json.JsonXContent;
|
||||
import org.xbib.content.settings.Settings;
|
||||
import org.xbib.content.Settings;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
@ -31,8 +31,8 @@ public class SettingsTest {
|
|||
|
||||
@Test
|
||||
public void testEmpty() {
|
||||
Settings settings = Settings.EMPTY_SETTINGS;
|
||||
assertTrue(settings.getAsMap().isEmpty());
|
||||
Settings settings = Settings.emptySettings();
|
||||
assertTrue(settings.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -134,7 +134,7 @@ public class SettingsTest {
|
|||
@Test
|
||||
public void testPropertiesLoader() {
|
||||
Settings settings = Settings.settingsBuilder()
|
||||
.loadFromStream(".properties", new ByteArrayInputStream("a.b=c".getBytes(StandardCharsets.UTF_8)))
|
||||
.loadFromResource(".properties", new ByteArrayInputStream("a.b=c".getBytes(StandardCharsets.UTF_8)))
|
||||
.build();
|
||||
assertEquals("{a.b=c}", settings.getAsMap().toString());
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
public class YamlTest {
|
||||
}
|
|
@ -5,5 +5,5 @@ pmd {
|
|||
consoleOutput = false
|
||||
toolVersion = "6.24.0"
|
||||
rulePriority = 5
|
||||
ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
|
||||
ruleSets = ["category/java/bestpractices.xml"]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue