From 8cd9fb24610406ceebaec275a7f607b2deca7d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Tue, 11 Mar 2025 21:58:53 +0100 Subject: [PATCH] update dependencies, add IllegalArgumentException for missing dependency, add settings-common --- build.gradle | 2 +- .../java/org/xbib/config/ConfigLoader.java | 10 - .../java/org/xbib/config/ConfigParams.java | 19 - .../services/org.xbib.settings.SettingsLoader | 2 +- .../csv/test/GeneratorTest.java | 2 +- gradle.properties | 2 +- settings-api/src/main/java/module-info.java | 3 +- .../settings}/AbstractSettingsLoader.java | 28 +- .../xbib/settings/PropertyPlaceholder.java | 3 - .../main/java/org/xbib/settings/Settings.java | 5 +- .../org/xbib/settings/SettingsBuilder.java | 17 - settings-common/build.gradle | 3 + .../src/main/java/module-info.java | 10 + .../xbib/settings/common/BaseSettings.java | 367 ++++++++++++++++++ .../settings/common/BaseSettingsBuilder.java | 328 ++++++++++++++++ .../org.xbib.settings.SettingsBuilder | 1 + .../src/main/java/module-info.java | 1 + .../json/JsonSettingsLoader.java | 19 +- .../src/main/java/module-info.java | 1 + .../yaml/YamlSettingsLoader.java | 17 +- .../src/main/java/module-info.java | 4 +- ...atastructurePropertiesSettingsLoader.java} | 4 +- .../services/org.xbib.settings.SettingsLoader | 2 +- settings.gradle | 9 +- 24 files changed, 772 insertions(+), 87 deletions(-) rename {settings-datastructures/src/main/java/org/xbib/settings/datastructures => settings-api/src/main/java/org/xbib/settings}/AbstractSettingsLoader.java (67%) create mode 100644 settings-common/build.gradle create mode 100644 settings-common/src/main/java/module-info.java create mode 100644 settings-common/src/main/java/org/xbib/settings/common/BaseSettings.java create mode 100644 settings-common/src/main/java/org/xbib/settings/common/BaseSettingsBuilder.java create mode 100644 settings-common/src/main/resources/META-INF/services/org.xbib.settings.SettingsBuilder rename settings-datastructures/src/main/java/org/xbib/settings/datastructures/{PropertiesSettingsLoader.java => DatastructurePropertiesSettingsLoader.java} (91%) diff --git a/build.gradle b/build.gradle index 31470e4..2d4d7ae 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'maven-publish' id 'signing' - id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1" + id "io.github.gradle-nexus.publish-plugin" version "2.0.0" } wrapper { diff --git a/config/src/main/java/org/xbib/config/ConfigLoader.java b/config/src/main/java/org/xbib/config/ConfigLoader.java index e2a7952..17d5da2 100644 --- a/config/src/main/java/org/xbib/config/ConfigLoader.java +++ b/config/src/main/java/org/xbib/config/ConfigLoader.java @@ -10,7 +10,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -124,15 +123,6 @@ public class ConfigLoader { } } } - if (!params.jdbcLookups.isEmpty()) { - for (ConfigParams.JdbcLookup jdbcLookup : params.jdbcLookups) { - try { - settings.fromJdbc(jdbcLookup.connection, jdbcLookup.statement, jdbcLookup.params); - } catch (SQLException sqlException) { - throw new ConfigException(sqlException); - } - } - } if (params.includeAll) { return overrideFromProperties(params, settings); } diff --git a/config/src/main/java/org/xbib/config/ConfigParams.java b/config/src/main/java/org/xbib/config/ConfigParams.java index 2caa363..32498ef 100644 --- a/config/src/main/java/org/xbib/config/ConfigParams.java +++ b/config/src/main/java/org/xbib/config/ConfigParams.java @@ -2,7 +2,6 @@ package org.xbib.config; import java.io.IOException; import java.io.Reader; -import java.sql.Connection; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -32,8 +31,6 @@ public class ConfigParams implements Comparable { final List reader = new ArrayList<>(); - final List jdbcLookups = new ArrayList<>(); - final List settings = new ArrayList<>(); List args = null; @@ -121,15 +118,6 @@ public class ConfigParams implements Comparable { return this; } - public ConfigParams withJdbc(Connection connection, String statement, String[] params) { - JdbcLookup jdbcLookup = new JdbcLookup(); - jdbcLookup.connection = connection; - jdbcLookup.statement = statement; - jdbcLookup.params = params; - jdbcLookups.add(jdbcLookup); - return this; - } - @Override public int hashCode() { return toString().hashCode(); @@ -152,7 +140,6 @@ public class ConfigParams implements Comparable { withSystemPropertiesOverride == that.withSystemPropertiesOverride && Objects.equals(classLoaders, that.classLoaders) && Objects.equals(reader, that.reader) && - Objects.equals(jdbcLookups, that.jdbcLookups) && Objects.equals(settings, that.settings) && Objects.equals(args, that.args) && Objects.equals(directoryName, that.directoryName) && @@ -183,10 +170,4 @@ public class ConfigParams implements Comparable { Reader reader; String suffix; } - - static class JdbcLookup { - Connection connection; - String statement; - String[] params; - } } diff --git a/config/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader b/config/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader index c25e931..72203af 100644 --- a/config/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader +++ b/config/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader @@ -1,3 +1,3 @@ -org.xbib.settings.datastructures.PropertiesSettingsLoader +org.xbib.settings.datastructures.DatastructurePropertiesSettingsLoader org.xbib.settings.datastructures.json.JsonSettingsLoader org.xbib.settings.datastructures.yaml.YamlSettingsLoader diff --git a/datastructures-csv/src/test/java/org/xbib/datastructures/csv/test/GeneratorTest.java b/datastructures-csv/src/test/java/org/xbib/datastructures/csv/test/GeneratorTest.java index d04f68b..3ba6d40 100644 --- a/datastructures-csv/src/test/java/org/xbib/datastructures/csv/test/GeneratorTest.java +++ b/datastructures-csv/src/test/java/org/xbib/datastructures/csv/test/GeneratorTest.java @@ -46,7 +46,7 @@ public class GeneratorTest { gen.write("hey look a line seperator \n"); } gen.close(); - assertEquals("a,b,c,\nval0,\"\"\"Hello, World\"\"\",\"hey look a line seperator \n\"\n", writer.toString()); + assertEquals("a,b,c\nval0,\"\"\"Hello, World\"\"\",\"hey look a line seperator \n\"\n", writer.toString()); } @Test diff --git a/gradle.properties b/gradle.properties index 46890f4..b015c5d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = org.xbib name = datastructures -version = 5.2.2 +version = 5.2.3 diff --git a/settings-api/src/main/java/module-info.java b/settings-api/src/main/java/module-info.java index 3996b9d..c5f8a05 100644 --- a/settings-api/src/main/java/module-info.java +++ b/settings-api/src/main/java/module-info.java @@ -5,6 +5,5 @@ module org.xbib.settings.api { exports org.xbib.settings; uses SettingsBuilder; uses SettingsLoader; - requires transitive org.xbib.datastructures.api; - requires transitive java.sql; + requires org.xbib.datastructures.api; } diff --git a/settings-datastructures/src/main/java/org/xbib/settings/datastructures/AbstractSettingsLoader.java b/settings-api/src/main/java/org/xbib/settings/AbstractSettingsLoader.java similarity index 67% rename from settings-datastructures/src/main/java/org/xbib/settings/datastructures/AbstractSettingsLoader.java rename to settings-api/src/main/java/org/xbib/settings/AbstractSettingsLoader.java index 96b182a..9eaec0c 100644 --- a/settings-datastructures/src/main/java/org/xbib/settings/datastructures/AbstractSettingsLoader.java +++ b/settings-api/src/main/java/org/xbib/settings/AbstractSettingsLoader.java @@ -1,6 +1,5 @@ -package org.xbib.settings.datastructures; +package org.xbib.settings; -import org.xbib.settings.SettingsLoader; import org.xbib.datastructures.api.Builder; import org.xbib.datastructures.api.DataStructure; import org.xbib.datastructures.api.ListNode; @@ -8,12 +7,10 @@ import org.xbib.datastructures.api.MapNode; import org.xbib.datastructures.api.Node; import org.xbib.datastructures.api.Parser; import org.xbib.datastructures.api.ValueNode; -import org.xbib.datastructures.tiny.TinyMap; import java.io.IOException; import java.io.Reader; import java.io.StringReader; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -37,20 +34,13 @@ public abstract class AbstractSettingsLoader implements SettingsLoader { return load(parser, new StringReader(source)); } - private Map load(Parser parser, Reader reader) throws IOException { - List path = new ArrayList<>(); - TinyMap.Builder map = TinyMap.builder(); - Node node = parser.parse(reader); - parseObject(node, map, path, null); - return map.build(); - } + protected abstract Map load(Parser parser, Reader reader) throws IOException; - private void parseObject(Node node, - TinyMap.Builder map, + protected void parseObject(Node node, + Map map, List path, CharSequence name) { - if (node instanceof ValueNode) { - ValueNode valueNode = (ValueNode) node; + if (node instanceof ValueNode valueNode) { StringBuilder sb = new StringBuilder(); for (CharSequence s : path) { sb.append(s).append('.'); @@ -58,22 +48,20 @@ public abstract class AbstractSettingsLoader implements SettingsLoader { sb.append(name); Object object = valueNode.get(); map.put(sb.toString(), object != null ? object.toString() : null); - } else if (node instanceof ListNode) { - ListNode listNode = (ListNode) node; + } else if (node instanceof ListNode listNode) { int counter = 0; for (Node nn : listNode.get()) { parseObject(nn, map, path, name + "." + (counter++)); } - } else if (node instanceof MapNode) { + } else if (node instanceof MapNode mapNode) { if (name != null) { path.add(name); } - MapNode mapNode = (MapNode) node; for (Map.Entry> me : mapNode.get().entrySet()) { parseObject(me.getValue(), map, path, me.getKey()); } if (name != null) { - path.remove(path.size() - 1); + path.removeLast(); } } } diff --git a/settings-api/src/main/java/org/xbib/settings/PropertyPlaceholder.java b/settings-api/src/main/java/org/xbib/settings/PropertyPlaceholder.java index b308c5f..ca69bc9 100644 --- a/settings-api/src/main/java/org/xbib/settings/PropertyPlaceholder.java +++ b/settings-api/src/main/java/org/xbib/settings/PropertyPlaceholder.java @@ -3,9 +3,6 @@ package org.xbib.settings; import java.util.HashSet; import java.util.Set; -/** - * - */ public class PropertyPlaceholder { private final String placeholderPrefix; diff --git a/settings-api/src/main/java/org/xbib/settings/Settings.java b/settings-api/src/main/java/org/xbib/settings/Settings.java index 142c809..db4a1d4 100644 --- a/settings-api/src/main/java/org/xbib/settings/Settings.java +++ b/settings-api/src/main/java/org/xbib/settings/Settings.java @@ -17,7 +17,10 @@ public interface Settings extends AutoCloseable { private static SettingsBuilder createBuilder() { ServiceLoader serviceLoader = ServiceLoader.load(SettingsBuilder.class); Optional optionalSettingsBuilder = serviceLoader.findFirst(); - return optionalSettingsBuilder.orElse(null); + if (optionalSettingsBuilder.isPresent()) { + return optionalSettingsBuilder.get(); + } + throw new IllegalArgumentException("no settings-datastructures included for Settings API"); } private static final Settings emptySettings = createBuilder().build(); diff --git a/settings-api/src/main/java/org/xbib/settings/SettingsBuilder.java b/settings-api/src/main/java/org/xbib/settings/SettingsBuilder.java index 1666e37..384ab9c 100644 --- a/settings-api/src/main/java/org/xbib/settings/SettingsBuilder.java +++ b/settings-api/src/main/java/org/xbib/settings/SettingsBuilder.java @@ -1,12 +1,7 @@ package org.xbib.settings; -import java.io.IOException; 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; @@ -41,18 +36,6 @@ public interface SettingsBuilder { SettingsBuilder loadFromResource(String resourceName, InputStream inputStream); - default SettingsBuilder fromJdbc(Connection connection, String statement, String[] params) throws SQLException { - try (PreparedStatement preparedStatement = connection.prepareStatement(statement, params); - ResultSet resultSet = preparedStatement.executeQuery()) { - while (resultSet.next()) { - String key = resultSet.getString("key"); - String value = resultSet.getString("value"); - put(key, value); - } - } - return this; - } - SettingsBuilder loadFromSystemProperties(); SettingsBuilder loadFromSystemEnvironment(); diff --git a/settings-common/build.gradle b/settings-common/build.gradle new file mode 100644 index 0000000..526bfdd --- /dev/null +++ b/settings-common/build.gradle @@ -0,0 +1,3 @@ +dependencies { + api project(':settings-api') +} diff --git a/settings-common/src/main/java/module-info.java b/settings-common/src/main/java/module-info.java new file mode 100644 index 0000000..b04af24 --- /dev/null +++ b/settings-common/src/main/java/module-info.java @@ -0,0 +1,10 @@ +import org.xbib.settings.SettingsBuilder; +import org.xbib.settings.common.BaseSettingsBuilder; + +module org.xbib.settings.common { + uses SettingsBuilder; + provides SettingsBuilder with BaseSettingsBuilder; + exports org.xbib.settings.common; + requires org.xbib.settings.api; + requires org.xbib.datastructures.api; +} diff --git a/settings-common/src/main/java/org/xbib/settings/common/BaseSettings.java b/settings-common/src/main/java/org/xbib/settings/common/BaseSettings.java new file mode 100644 index 0000000..62e64ff --- /dev/null +++ b/settings-common/src/main/java/org/xbib/settings/common/BaseSettings.java @@ -0,0 +1,367 @@ +package org.xbib.settings.common; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.xbib.datastructures.api.ByteSizeValue; +import org.xbib.datastructures.api.TimeValue; +import org.xbib.settings.Settings; +import org.xbib.settings.SettingsException; + +public class BaseSettings implements Settings { + + private static final String[] EMPTY_ARRAY = new String[0]; + + private final Map map; + + BaseSettings(Map map) { + this.map = map; + } + + public static BaseSettingsBuilder builder() { + return new BaseSettingsBuilder(); + } + + public static Settings fromMap(Map map) { + BaseSettingsBuilder builder = new BaseSettingsBuilder(); + for (Map.Entry entry : map.entrySet()) { + builder.put(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : null); + } + return builder.build(); + } + + public static void toMap(Settings settings, Map map) { + for (String key : settings.getAsMap().keySet()) { + map.put(key, settings.get(key)); + } + } + + public static String[] splitStringByCommaToArray(String s) { + return splitStringToArray(s, ','); + } + + public static String[] splitStringToArray(String s, char c) { + if (s.isEmpty()) { + 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 getAsMap() { + return this.map; + } + + @Override + public Map getAsStructuredMap() { + Map stringObjectMap = new LinkedHashMap<>(); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + processSetting(stringObjectMap, "", key, value); + } + for (Map.Entry entry : stringObjectMap.entrySet()) { + String key = entry.getKey(); + Object object = entry.getValue(); + if (object instanceof Map) { + @SuppressWarnings("unchecked") + Map valMap = (Map) object; + stringObjectMap.put(key, convertMapsToArrays(valMap)); + } + } + return stringObjectMap; + } + + @Override + public Settings getByPrefix(String prefix) { + BaseSettingsBuilder builder = new BaseSettingsBuilder(); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + 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 int getAsInt(String setting, int 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 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 getGroups(String prefix) { + String settingPrefix = prefix; + if (settingPrefix.charAt(settingPrefix.length() - 1) != '.') { + settingPrefix = settingPrefix + "."; + } + // we don't really care that it might happen twice + Map> hashMap = new LinkedHashMap<>(); + 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 groupSettings = hashMap.computeIfAbsent(name, k -> new LinkedHashMap<>()); + groupSettings.put(value, get(o)); + } + } + Map retVal = new LinkedHashMap<>(); + for (Map.Entry> entry : hashMap.entrySet()) { + String key = entry.getKey(); + Map value = entry.getValue(); + retVal.put(key, new BaseSettings(value)); + } + return retVal; + } + + @Override + public boolean equals(Object o) { + return this == o || !(o == null || getClass() != o.getClass()) && map.equals(((BaseSettings) o).map); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public void close() throws IOException { + // do nothing + } + + private void processSetting(Map map, String prefix, String setting, String value) { + int prefixLength = setting.indexOf('.'); + if (prefixLength == -1) { + @SuppressWarnings("unchecked") + Map innerMap = (Map) map.get(prefix + setting); + if (innerMap != null) { + for (Map.Entry e : innerMap.entrySet()) { + String k = e.getKey(); + Object v = e.getValue(); + 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 newMap = new LinkedHashMap<>(); + processSetting(newMap, "", rest, value); + map.put(key, newMap); + } else { + if (existingValue instanceof Map) { + @SuppressWarnings("unchecked") + Map innerMap = (Map) existingValue; + processSetting(innerMap, "", rest, value); + map.put(key, innerMap); + } else { + processSetting(map, prefix + key + ".", rest, value); + } + } + } + } + + private Object convertMapsToArrays(Map map) { + if (map.isEmpty()) { + return map; + } + boolean isArray = true; + int maxIndex = -1; + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + 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 valMap = (Map) value; + map.put(key, convertMapsToArrays(valMap)); + } + } + if (isArray && (maxIndex + 1) == map.size()) { + ArrayList 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; + } +} diff --git a/settings-common/src/main/java/org/xbib/settings/common/BaseSettingsBuilder.java b/settings-common/src/main/java/org/xbib/settings/common/BaseSettingsBuilder.java new file mode 100644 index 0000000..5b7cf75 --- /dev/null +++ b/settings-common/src/main/java/org/xbib/settings/common/BaseSettingsBuilder.java @@ -0,0 +1,328 @@ +package org.xbib.settings.common; + +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.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.xbib.settings.PlaceholderResolver; +import org.xbib.settings.PropertyPlaceholder; +import org.xbib.settings.Settings; +import org.xbib.settings.SettingsBuilder; +import org.xbib.settings.SettingsException; +import org.xbib.settings.SettingsLoader; +import org.xbib.settings.SettingsLoaderService; + +public class BaseSettingsBuilder implements SettingsBuilder { + + private final SettingsLoaderService settingsLoaderService; + + private final Map map; + + public BaseSettingsBuilder() { + this.settingsLoaderService = SettingsLoaderService.getInstance(); + this.map = new LinkedHashMap<>(); + } + + public String remove(String key) { + return map.remove(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 BaseSettingsBuilder put(String key, String value) { + map.put(key, value); + 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 BaseSettingsBuilder 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 BaseSettingsBuilder 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 BaseSettingsBuilder 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 BaseSettingsBuilder 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 BaseSettingsBuilder 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 BaseSettingsBuilder 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 BaseSettingsBuilder putArray(String setting, List 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 BaseSettingsBuilder 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 BaseSettingsBuilder put(Settings settings) { + map.putAll(settings.getAsMap()); + return this; + } + + /** + * Sets all the provided settings. + * + * @param settings settings + * @return a builder + */ + @Override + public BaseSettingsBuilder put(Map settings) { + map.putAll(settings); + return this; + } + + /** + * Loads settings from a resource. + * + * @param resourceName resource name + * @param inputStream input stream + * @return builder + */ + @Override + public BaseSettingsBuilder loadFromResource(String resourceName, InputStream inputStream) { + SettingsLoader settingsLoader = settingsLoaderService.loaderFromResource(resourceName); + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + Map 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; + } + + /** + * Loads settings from the actual string content that represents them using the + * {@link SettingsLoaderService#loaderFromResource(String)} (String)}. + * + * @param resourceName the resource name + * @param source the source + * @return builder + */ + @Override + public BaseSettingsBuilder loadFromString(String resourceName, String source) { + SettingsLoader settingsLoader = settingsLoaderService.loaderFromResource(resourceName); + try { + put(settingsLoader.load(source)); + } catch (Exception e) { + throw new SettingsException("failed to load settings from [" + source + "]", e); + } + return this; + } + + /** + * Load system properties to this settings. + * + * @return builder + */ + @Override + public BaseSettingsBuilder loadFromSystemProperties() { + for (Map.Entry entry : System.getProperties().entrySet()) { + put((String) entry.getKey(), (String) entry.getValue()); + } + return this; + } + + /** + * Load system environment to this settings. + * + * @return builder + */ + @Override + public BaseSettingsBuilder loadFromSystemEnvironment() { + for (Map.Entry entry : System.getenv().entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + @Override + public BaseSettingsBuilder replacePropertyPlaceholders(PropertyPlaceholder propertyPlaceholder, + PlaceholderResolver placeholderResolver) { + map.replaceAll((k, v) -> propertyPlaceholder.replacePlaceholders(v, placeholderResolver)); + return this; + } + + @Override + public BaseSettingsBuilder 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 BaseSettingsBuilder setRefresh(Path path, long initialDelay, long period, TimeUnit timeUnit) { + return this; + } + + @Override + public SettingsBuilder map(Function, Map.Entry> function) { + map.entrySet().stream().map(function).forEach(e -> put(e.getKey(), e.getValue())); + return this; + } + + @Override + public Settings build() { + return new BaseSettings(map); + } +} diff --git a/settings-common/src/main/resources/META-INF/services/org.xbib.settings.SettingsBuilder b/settings-common/src/main/resources/META-INF/services/org.xbib.settings.SettingsBuilder new file mode 100644 index 0000000..4a54b7a --- /dev/null +++ b/settings-common/src/main/resources/META-INF/services/org.xbib.settings.SettingsBuilder @@ -0,0 +1 @@ +org.xbib.settings.common.BaseSettingsBuilder \ No newline at end of file diff --git a/settings-datastructures-json/src/main/java/module-info.java b/settings-datastructures-json/src/main/java/module-info.java index 418127f..852083b 100644 --- a/settings-datastructures-json/src/main/java/module-info.java +++ b/settings-datastructures-json/src/main/java/module-info.java @@ -5,6 +5,7 @@ module org.xbib.settings.datastructures.json { exports org.xbib.settings.datastructures.json; requires transitive org.xbib.settings.datastructures; requires org.xbib.datastructures.json.tiny; + requires org.xbib.datastructures.tiny; uses SettingsLoader; provides SettingsLoader with JsonSettingsLoader; } diff --git a/settings-datastructures-json/src/main/java/org/xbib/settings/datastructures/json/JsonSettingsLoader.java b/settings-datastructures-json/src/main/java/org/xbib/settings/datastructures/json/JsonSettingsLoader.java index b023e99..f3c5ce8 100644 --- a/settings-datastructures-json/src/main/java/org/xbib/settings/datastructures/json/JsonSettingsLoader.java +++ b/settings-datastructures-json/src/main/java/org/xbib/settings/datastructures/json/JsonSettingsLoader.java @@ -1,6 +1,14 @@ package org.xbib.settings.datastructures.json; -import org.xbib.settings.datastructures.AbstractSettingsLoader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.api.Parser; +import org.xbib.datastructures.tiny.TinyMap; +import org.xbib.settings.AbstractSettingsLoader; import org.xbib.datastructures.api.DataStructure; import org.xbib.datastructures.json.tiny.Json; import java.util.Set; @@ -19,4 +27,13 @@ public class JsonSettingsLoader extends AbstractSettingsLoader { public Set suffixes() { return Set.of("json"); } + + @Override + protected Map load(Parser parser, Reader reader) throws IOException { + List path = new ArrayList<>(); + TinyMap.Builder map = TinyMap.builder(); + Node node = parser.parse(reader); + parseObject(node, map, path, null); + return map.build(); + } } diff --git a/settings-datastructures-yaml/src/main/java/module-info.java b/settings-datastructures-yaml/src/main/java/module-info.java index 5310e4c..8e3eea6 100644 --- a/settings-datastructures-yaml/src/main/java/module-info.java +++ b/settings-datastructures-yaml/src/main/java/module-info.java @@ -5,6 +5,7 @@ module org.xbib.settings.datastructures.yaml { exports org.xbib.settings.datastructures.yaml; requires transitive org.xbib.settings.datastructures; requires org.xbib.datastructures.yaml.tiny; + requires org.xbib.datastructures.tiny; uses SettingsLoader; provides SettingsLoader with YamlSettingsLoader; } diff --git a/settings-datastructures-yaml/src/main/java/org/xbib/settings/datastructures/yaml/YamlSettingsLoader.java b/settings-datastructures-yaml/src/main/java/org/xbib/settings/datastructures/yaml/YamlSettingsLoader.java index 95b72c4..aa8d81a 100644 --- a/settings-datastructures-yaml/src/main/java/org/xbib/settings/datastructures/yaml/YamlSettingsLoader.java +++ b/settings-datastructures-yaml/src/main/java/org/xbib/settings/datastructures/yaml/YamlSettingsLoader.java @@ -1,6 +1,12 @@ package org.xbib.settings.datastructures.yaml; -import org.xbib.settings.datastructures.AbstractSettingsLoader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.api.Parser; +import org.xbib.datastructures.tiny.TinyMap; +import org.xbib.settings.AbstractSettingsLoader; import org.xbib.datastructures.api.DataStructure; import org.xbib.datastructures.yaml.tiny.Yaml; @@ -28,4 +34,13 @@ public class YamlSettingsLoader extends AbstractSettingsLoader { // replace tabs with two whitespace (yaml does not accept tabs, but many users might use it still...) return super.load(source.replace("\t", " ")); } + + @Override + protected Map load(Parser parser, Reader reader) throws IOException { + List path = new ArrayList<>(); + TinyMap.Builder map = TinyMap.builder(); + Node node = parser.parse(reader); + parseObject(node, map, path, null); + return map.build(); + } } diff --git a/settings-datastructures/src/main/java/module-info.java b/settings-datastructures/src/main/java/module-info.java index 6bacf68..6833969 100644 --- a/settings-datastructures/src/main/java/module-info.java +++ b/settings-datastructures/src/main/java/module-info.java @@ -1,11 +1,11 @@ import org.xbib.settings.SettingsBuilder; import org.xbib.settings.SettingsLoader; import org.xbib.settings.datastructures.DatastructureSettingsBuilder; -import org.xbib.settings.datastructures.PropertiesSettingsLoader; +import org.xbib.settings.datastructures.DatastructurePropertiesSettingsLoader; module org.xbib.settings.datastructures { uses SettingsLoader; - provides SettingsLoader with PropertiesSettingsLoader; + provides SettingsLoader with DatastructurePropertiesSettingsLoader; uses SettingsBuilder; provides SettingsBuilder with DatastructureSettingsBuilder; exports org.xbib.settings.datastructures; diff --git a/settings-datastructures/src/main/java/org/xbib/settings/datastructures/PropertiesSettingsLoader.java b/settings-datastructures/src/main/java/org/xbib/settings/datastructures/DatastructurePropertiesSettingsLoader.java similarity index 91% rename from settings-datastructures/src/main/java/org/xbib/settings/datastructures/PropertiesSettingsLoader.java rename to settings-datastructures/src/main/java/org/xbib/settings/datastructures/DatastructurePropertiesSettingsLoader.java index 7e21857..0325e60 100644 --- a/settings-datastructures/src/main/java/org/xbib/settings/datastructures/PropertiesSettingsLoader.java +++ b/settings-datastructures/src/main/java/org/xbib/settings/datastructures/DatastructurePropertiesSettingsLoader.java @@ -11,9 +11,9 @@ import java.util.Set; /** * Settings loader that loads (parses) the settings in a properties format. */ -public class PropertiesSettingsLoader implements SettingsLoader { +public class DatastructurePropertiesSettingsLoader implements SettingsLoader { - public PropertiesSettingsLoader() { + public DatastructurePropertiesSettingsLoader() { } @Override diff --git a/settings-datastructures/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader b/settings-datastructures/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader index d3ebb87..d8f7dca 100644 --- a/settings-datastructures/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader +++ b/settings-datastructures/src/test/resources/META-INF/services/org.xbib.settings.SettingsLoader @@ -1 +1 @@ -org.xbib.settings.datastructures.PropertiesSettingsLoader +org.xbib.settings.datastructures.DatastructurePropertiesSettingsLoader diff --git a/settings.gradle b/settings.gradle index ea883ab..e224a9d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,14 +22,14 @@ dependencyResolutionManagement { library('orgjson', 'org.json', 'json').version('20210307') } testLibs { - version('junit', '5.10.2') - version('jackson', '2.17.3') + version('junit', '5.12.0') + version('jackson', '2.18.3') library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit') library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') library('junit-vintage-engine', 'org.junit.vintage', 'junit-vintage-engine').versionRef('junit') - library('junit-jupiter-platform-launcher', 'org.junit.platform', 'junit-platform-launcher').version('1.10.0') - library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2') + library('junit-jupiter-platform-launcher', 'org.junit.platform', 'junit-platform-launcher').version('1.12.0') + library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('3.0') library('junit4', 'junit', 'junit').version('4.13.2') library('burst-junit4', 'com.squareup.burst', 'burst-junit4').version('1.1.1') library('mockito-core', 'org.mockito', 'mockito-core').version('3.12.4') @@ -71,6 +71,7 @@ include 'datastructures-xml' include 'datastructures-xslx' include 'datastructures-yaml-tiny' include 'settings-api' +include 'settings-common' include 'settings-datastructures' include 'settings-datastructures-json' include 'settings-datastructures-yaml'