From fcb169e6664ab036185fb8adfd982cd193277388 Mon Sep 17 00:00:00 2001 From: Scott Brown Date: Wed, 20 Jan 2016 16:17:41 +0000 Subject: [PATCH] Adding support for merging a list of configs external to the second fig --- fig-core/src/main/java/twigkit/fig/Fig.java | 12 +-- .../fig/loader/MergedPropertiesLoader.java | 30 +++++- .../java/twigkit/fig/util/ConfigUtils.java | 101 ++++++++++++++++++ .../main/java/twigkit/fig/util/FigUtils.java | 11 ++ .../loader/MergedPropertiesLoaderTest.java | 19 ++++ .../twigkit.fig/util/ConfigUtilsTest.java | 37 +++++++ .../java/twigkit.fig/util/FigUtilsTest.java | 20 ++++ 7 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 fig-core/src/main/java/twigkit/fig/util/ConfigUtils.java create mode 100644 fig-core/src/test/java/twigkit.fig/util/ConfigUtilsTest.java diff --git a/fig-core/src/main/java/twigkit/fig/Fig.java b/fig-core/src/main/java/twigkit/fig/Fig.java index 041bc05..1bf2bfa 100644 --- a/fig-core/src/main/java/twigkit/fig/Fig.java +++ b/fig-core/src/main/java/twigkit/fig/Fig.java @@ -2,6 +2,7 @@ import twigkit.fig.annotation.InjectionConfigurator; import twigkit.fig.loader.Loader; +import twigkit.fig.util.ConfigUtils; import twigkit.fig.visitor.ConfigFinder; import java.util.Arrays; @@ -110,16 +111,9 @@ public Fig add(Config config, String... path) { if (!configs.containsKey(path[0])) { add(new Config(path[0], config.loader)); } + Config parent = configs.get(path[0]); - for (int i = 1; i < path.length - 1; i++) { - Config c = parent.extension(path[i]); - if (c == null && i < path.length) { - c = new Config(path[i]); - parent.extend_with(c); - } - parent = c; - } - parent.extend_with(config); + ConfigUtils.addToParent(config, parent, path); } return this; diff --git a/fig-core/src/main/java/twigkit/fig/loader/MergedPropertiesLoader.java b/fig-core/src/main/java/twigkit/fig/loader/MergedPropertiesLoader.java index 6a5e850..71bd80c 100644 --- a/fig-core/src/main/java/twigkit/fig/loader/MergedPropertiesLoader.java +++ b/fig-core/src/main/java/twigkit/fig/loader/MergedPropertiesLoader.java @@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory; import twigkit.fig.Config; import twigkit.fig.Fig; +import twigkit.fig.util.ConfigUtils; import twigkit.fig.util.FigUtils; import twigkit.fig.util.FileUtils; @@ -23,6 +24,7 @@ public class MergedPropertiesLoader implements Loader { private String pathToPrimaryFig; private String pathToSecondaryFig; + private String configsToMerge; public MergedPropertiesLoader(String pathToPrimaryFig, String pathToSecondaryFig) { this.pathToPrimaryFig = pathToPrimaryFig; @@ -30,8 +32,11 @@ public MergedPropertiesLoader(String pathToPrimaryFig, String pathToSecondaryFig } /** - * This method loads the given primary {@link Fig} and a second {@link Fig} then merges the second {@link Fig} - * with the primary {@link Fig}. + * This method first loads the given primary {@link Fig} and a second {@link Fig} then merges the + * second {@link Fig} with the primary {@link Fig}. + * + * Any additional configurations set on the {@link Loader} that are not part of the secondary {@link Fig} + * will also be merged with the primary {@link Fig} once the secondary {@link Fig} has been merged. * * @param fig The primary {@link Fig} */ @@ -54,6 +59,10 @@ public void load(Fig fig) { logger.trace("Primary fig {} not found. Falling back to secondary", pathToPrimaryFig); new PropertiesLoader(pathToSecondaryFig).load(fig); } + + if (configsToMerge != null) { + FigUtils.mergeConfigs(fig, ConfigUtils.asList(configsToMerge, this)); + } } public void write(Config config) throws IOException { @@ -63,4 +72,21 @@ public void write(Config config) throws IOException { public void delete(Config config) throws IOException { throw new IOException("Delete operation not supported."); } + + /** + * Set a config or sequence of configs that should be merged with the primary {@link Fig} after merging the + * secondary {@link Fig}. A config should be given in the following form: + * + * parent.child[property:value + * + * If more than one config is to be merged, subsequent configs should be separated with an ampersand, for + * example: + * + * parent.child[property:value&parent[property:value + * + * @param configsToMerge The config or sequence of configs to be merged with the primary {@link Fig}. + */ + public void setConfigsToMerge(String configsToMerge) { + this.configsToMerge = configsToMerge; + } } diff --git a/fig-core/src/main/java/twigkit/fig/util/ConfigUtils.java b/fig-core/src/main/java/twigkit/fig/util/ConfigUtils.java new file mode 100644 index 0000000..d2ff017 --- /dev/null +++ b/fig-core/src/main/java/twigkit/fig/util/ConfigUtils.java @@ -0,0 +1,101 @@ +package twigkit.fig.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import twigkit.fig.Config; +import twigkit.fig.Value; +import twigkit.fig.loader.Loader; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class to perform operations on a {@link Config} or set of {@link Config}s. + * + * @author scottbrown + */ +public class ConfigUtils { + + private static final Logger logger = LoggerFactory.getLogger(ConfigUtils.class); + + /** + * Convert the given config or sequence of configs into a {@link List} of {@link Config}s. + * + * A config should be given in the following form: + * + * parent.child[property:value + * + * If more than one config is to be merged, subsequent configs should be separated with an ampersand, for + * example: + * + * parent.child[property:value&parent[property:value + * + * @param configurations The config or sequence of configs to be converted into a {@link List} of {@link Config}s. + * @param loader The {@link Loader} that backs each instance of the {@link Config}. + * @return A {@link List} of {@link Config}s. + */ + public static List asList(String configurations, Loader loader) { + List configs = null; + + if (configurations != null) { + configs = new ArrayList(); + + for (String c : configurations.split("&")) { + + String[] configDetails = c.split(":"); + + String property = configDetails[0].substring(configDetails[0].indexOf("[") + 1, configDetails[0].length()); + String[] path = configDetails[0].substring(0, configDetails[0].indexOf("[")).split("\\."); + + Config config = null; + if (path.length == 1) { + config = new Config(path[0], loader); + } else if (path.length > 1) { + config = new Config(path[path.length - 1], loader); + } else { + logger.warn("No configuration given for property {}. This property will be ignored", property); + } + + if (config != null) { + config.set(new Value(property, configDetails[1], false)); + + if (path.length == 1) { + configs.add(config); + } else { + Config parent = new Config(path[0], loader); + addToParent(config, parent, path); + configs.add(config.parents().get(0)); + } + } + } + } + + return configs; + } + + /** + * Add the given {@link Config} to the given parent {@link Config}. + * + * @param config The {@link Config} to be added as a child of the given parent {@link Config}. + * @param parent The {@link Config} that will be a parent of the given {@link Config} + * @param path The configuration path pointing from the parent down to the child. + */ + public static void addToParent(Config config, Config parent, String... path) { + if (parent != null) { + for (int i = 1; i < path.length - 1; i++) { + Config child = parent.extension(path[i]); + + if (child == null && i < path.length) { + child = new Config(path[i]); + parent.extend_with(child); + } + + parent = child; + } + + if (config != null) { + parent.extend_with(config); + } + } + } +} diff --git a/fig-core/src/main/java/twigkit/fig/util/FigUtils.java b/fig-core/src/main/java/twigkit/fig/util/FigUtils.java index 374f35e..9363e69 100644 --- a/fig-core/src/main/java/twigkit/fig/util/FigUtils.java +++ b/fig-core/src/main/java/twigkit/fig/util/FigUtils.java @@ -27,6 +27,17 @@ public static void merge(Fig primary, Fig secondary) { } } + /** + * Merge a collection of {@link Config}s into a primary {@link Fig}. + * @param primary The {@link Fig} to be updated. + * @param configs The {@link Config}s to be merged. + */ + public static void mergeConfigs(Fig primary, Collection configs) { + if (primary != null && configs != null) { + merge(primary, primary.configs(), configs); + } + } + /** * Merge a collection of secondary {@link Config}s into a collection of primary {@link Config}s. * @param fig The {@link Fig} to be updated. diff --git a/fig-core/src/test/java/twigkit.fig/loader/MergedPropertiesLoaderTest.java b/fig-core/src/test/java/twigkit.fig/loader/MergedPropertiesLoaderTest.java index 9c67d3f..b988d1d 100644 --- a/fig-core/src/test/java/twigkit.fig/loader/MergedPropertiesLoaderTest.java +++ b/fig-core/src/test/java/twigkit.fig/loader/MergedPropertiesLoaderTest.java @@ -5,10 +5,13 @@ import org.junit.rules.ExpectedException; import twigkit.fig.Config; import twigkit.fig.Fig; +import twigkit.fig.util.ConfigUtils; +import twigkit.fig.util.FigUtils; import twigkit.fig.visitor.ConfigTreeWriter; import java.io.IOException; import java.util.Iterator; +import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -135,4 +138,20 @@ public void testExtensionsWithTheSameNameUnderDifferentParentConfigsAreHandledCo new ConfigTreeWriter(fig.get("companies")); new ConfigTreeWriter(fig.get("people")); } + + @Test + public void testMergedPropertiesLoaderMergesConfigList() { + MergedPropertiesLoader loader = new MergedPropertiesLoader("confs", "confs_dev"); + + String configsToMerge = "people.detail[enabled:true&sub.group.folder-extension[group-folder-extension-key:test"; + loader.setConfigsToMerge(configsToMerge); + + Fig fig = Fig.getInstance(loader); + + assertEquals(2, fig.get("people", "detail").values().size()); + assertEquals("true", fig.get("people", "detail").value("enabled").as_string()); + + assertEquals(2, fig.get("sub", "group", "folder-extension").values().size()); + assertEquals("test", fig.get("sub", "group", "folder-extension").value("group-folder-extension-key").as_string()); + } } diff --git a/fig-core/src/test/java/twigkit.fig/util/ConfigUtilsTest.java b/fig-core/src/test/java/twigkit.fig/util/ConfigUtilsTest.java new file mode 100644 index 0000000..ca451c3 --- /dev/null +++ b/fig-core/src/test/java/twigkit.fig/util/ConfigUtilsTest.java @@ -0,0 +1,37 @@ +package twigkit.fig.util; + +import org.junit.Test; +import twigkit.fig.Config; +import twigkit.fig.loader.PropertiesLoader; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author scottbrown + */ +public class ConfigUtilsTest { + + @Test + public void testConvertingSingleConfigStringToConfigList() { + String configsToMerge = "companies[host:test"; + List configs = ConfigUtils.asList(configsToMerge, new PropertiesLoader("conf")); + + assertEquals(1, configs.size()); + assertEquals("companies", configs.get(0).name()); + assertEquals("test", configs.get(0).value("host").as_string()); + } + + @Test + public void testConvertingMultipleConfigStringToConfigList() { + String configsToMerge = "people.detail[enabled:true&sub.group.folder-extension[group-folder-extension-key:test"; + List configs = ConfigUtils.asList(configsToMerge, new PropertiesLoader("conf")); + + assertEquals(2, configs.size()); + assertEquals("people", configs.get(0).name()); + assertEquals("true", configs.get(0).extension("detail").value("enabled").as_string()); + assertEquals("sub", configs.get(1).name()); + assertEquals("test", configs.get(1).extension("group").extension("folder-extension").value("group-folder-extension-key").as_string()); + } +} diff --git a/fig-core/src/test/java/twigkit.fig/util/FigUtilsTest.java b/fig-core/src/test/java/twigkit.fig/util/FigUtilsTest.java index be5709c..6f79e80 100644 --- a/fig-core/src/test/java/twigkit.fig/util/FigUtilsTest.java +++ b/fig-core/src/test/java/twigkit.fig/util/FigUtilsTest.java @@ -3,8 +3,11 @@ import org.junit.Test; import twigkit.fig.Config; import twigkit.fig.Fig; +import twigkit.fig.loader.MergedPropertiesLoader; import twigkit.fig.loader.PropertiesLoader; +import java.util.List; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -111,4 +114,21 @@ public void testFigRemainsUnchangedWhenMergingNullFig() { FigUtils.merge(fig, null); assertEquals(5, fig.configs().size()); } + + @Test + public void testMergingCollectionOfConfigsIntoFig() { + PropertiesLoader loader = new PropertiesLoader("confs"); + Fig fig = Fig.getInstance(loader); + + String configsToMerge = "people.detail[enabled:true&sub.group.folder-extension[group-folder-extension-key:test"; + List configs = ConfigUtils.asList(configsToMerge, loader); + + FigUtils.mergeConfigs(fig, configs); + + assertEquals(2, fig.get("people", "detail").values().size()); + assertEquals("true", fig.get("people", "detail").value("enabled").as_string()); + + assertEquals(2, fig.get("sub", "group", "folder-extension").values().size()); + assertEquals("test", fig.get("sub", "group", "folder-extension").value("group-folder-extension-key").as_string()); + } }