diff --git a/fig-core/src/main/java/twigkit/fig/Fig.java b/fig-core/src/main/java/twigkit/fig/Fig.java index ce9724b..4616d5d 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; @@ -112,16 +113,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 d26e13f..dc3d80d 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} */ @@ -66,6 +71,10 @@ public void load(Fig fig) { logger.error("Both the path to the primary fig: \"{}\" and the path to the secondary fig: \"{}\" are invalid. Unable to load configurations.", pathToPrimaryFig, pathToSecondaryFig); } } + + if (configsToMerge != null) { + FigUtils.mergeConfigs(fig, ConfigUtils.asList(configsToMerge, this)); + } } public void write(Config config) throws IOException { @@ -75,4 +84,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 a1491e8..3bf576a 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; @@ -136,6 +139,22 @@ public void testExtensionsWithTheSameNameUnderDifferentParentConfigsAreHandledCo 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()); + } + @Test public void testFallbackFigIsReturnedWhenTheOtherIsEmpty() { Fig fig1 = Fig.getInstance(new PropertiesLoader("confs")); 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()); + } }