From 3dab738dee2135b8381cd02182ceec6748860190 Mon Sep 17 00:00:00 2001 From: Andrei Pozolotin Date: Fri, 10 Aug 2012 10:33:17 -0500 Subject: [PATCH 1/4] solution for https://github.com/typesafehub/config/issues/24 --- .../config/impl/ConfigConcatenation.java | 23 ++- .../config/impl/ArrayWithObjectTest.java | 132 ++++++++++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java b/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java index bde6fb64b..c171fddd1 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java @@ -9,6 +9,7 @@ import com.typesafe.config.ConfigObject; import com.typesafe.config.ConfigOrigin; import com.typesafe.config.ConfigRenderOptions; +import com.typesafe.config.ConfigValue; import com.typesafe.config.ConfigValueType; /** @@ -95,7 +96,27 @@ private static void join(ArrayList builder, joined = right.withFallback(left); } else if (left instanceof SimpleConfigList && right instanceof SimpleConfigList) { joined = ((SimpleConfigList)left).concatenate((SimpleConfigList)right); - } else if (left instanceof ConfigConcatenation || right instanceof ConfigConcatenation) { + } else + + // XXX + // experimental + // https://github.com/typesafehub/config/issues/24 + if (left instanceof SimpleConfigList && right instanceof AbstractConfigObject) { + + SimpleConfigList source = (SimpleConfigList) left; + + List target = new ArrayList(source.size()); + + for(ConfigValue value: source){ + target.add(((AbstractConfigObject) value).withFallback(right)); + } + + joined = new SimpleConfigList(source.origin(), target); + + } else + // XXX + + if (left instanceof ConfigConcatenation || right instanceof ConfigConcatenation) { throw new ConfigException.BugOrBroken("unflattened ConfigConcatenation"); } else if (left instanceof Unmergeable || right instanceof Unmergeable) { // leave joined=null, cannot join diff --git a/config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java b/config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java new file mode 100644 index 000000000..4b4256910 --- /dev/null +++ b/config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java @@ -0,0 +1,132 @@ +package com.typesafe.config.impl; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.ConfigList; +import com.typesafe.config.ConfigObject; +import com.typesafe.config.ConfigValue; + +public class ArrayWithObjectTest { + + static void log(Object text) { + System.err.println("" + text); + } + + static Config config(ConfigValue value) { + return ((ConfigObject) value).toConfig(); + } + + @Test + public void test1() { + + final String text = "{ list = [] {} }"; + + Config config = ConfigFactory.parseString(text); + + log(config); + + ConfigList list = config.getList("list"); + + assertEquals("emtpy list", list.size(), 0); + + } + + @Test + public void test2() { + + final String text = "{ list = [ {} ] {} }"; + + Config config = ConfigFactory.parseString(text); + + log(config); + + ConfigList list = config.getList("list"); + + assertEquals("one item list", list.size(), 1); + + } + + @Test + public void test3() { + + final String text = "{ list = [ { a:1 } ] {} }"; + + Config config = ConfigFactory.parseString(text); + + log(config); + + ConfigList list = config.getList("list"); + assertEquals("one item list", list.size(), 1); + + Config entry = config(list.get(0)); + assertEquals("provided a", entry.getNumber("a"), 1); + + } + + @Test + public void test4() { + + final String text = "{ list = [ { a:1 } ] { a:2, b:2 } }"; + + Config config = ConfigFactory.parseString(text); + + log(config); + + ConfigList list = config.getList("list"); + assertEquals("one item list", list.size(), 1); + + Config entry = config(list.get(0)); + assertEquals("provided a", entry.getNumber("a"), 1); + assertEquals("default b", entry.getNumber("b"), 2); + + } + + @Test + public void test5() { + + final String text = "{ item = {} , list = [] ${item} }"; + + Config config = ConfigFactory.parseString(text).resolve(); + + log(config); + + ConfigList list = config.getList("list"); + assertEquals("empty list", list.size(), 0); + + } + + @Test + public void test6() { + + final String text = "{ item = {} , list = [ {} ] ${item} }"; + + Config config = ConfigFactory.parseString(text).resolve(); + + log(config); + + ConfigList list = config.getList("list"); + assertEquals("one item list", list.size(), 1); + + } + + @Test + public void test7() { + + final String text = "{ item = { a:1, b:2 } , list = [ { a:2} ] ${item} }"; + + Config config = ConfigFactory.parseString(text).resolve(); + + ConfigList list = config.getList("list"); + assertEquals("one item list", list.size(), 1); + + Config entry = config(list.get(0)); + assertEquals("provided a", entry.getNumber("a"), 2); + assertEquals("default b", entry.getNumber("b"), 2); + + } + +} From b5ebd96bd366382b3fe17bfdc0bc58071b5e6a11 Mon Sep 17 00:00:00 2001 From: Andrei Pozolotin Date: Fri, 10 Aug 2012 11:57:36 -0500 Subject: [PATCH 2/4] fix format --- .../config/impl/ConfigConcatenation.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java b/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java index c171fddd1..7f2d92ed5 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigConcatenation.java @@ -96,27 +96,22 @@ private static void join(ArrayList builder, joined = right.withFallback(left); } else if (left instanceof SimpleConfigList && right instanceof SimpleConfigList) { joined = ((SimpleConfigList)left).concatenate((SimpleConfigList)right); - } else - - // XXX - // experimental - // https://github.com/typesafehub/config/issues/24 - if (left instanceof SimpleConfigList && right instanceof AbstractConfigObject) { - + } else if (left instanceof SimpleConfigList && right instanceof AbstractConfigObject) { + + // experimental + // https://github.com/typesafehub/config/issues/24 + SimpleConfigList source = (SimpleConfigList) left; List target = new ArrayList(source.size()); - for(ConfigValue value: source){ + for( ConfigValue value: source ) { target.add(((AbstractConfigObject) value).withFallback(right)); } joined = new SimpleConfigList(source.origin(), target); - } else - // XXX - - if (left instanceof ConfigConcatenation || right instanceof ConfigConcatenation) { + } else if (left instanceof ConfigConcatenation || right instanceof ConfigConcatenation) { throw new ConfigException.BugOrBroken("unflattened ConfigConcatenation"); } else if (left instanceof Unmergeable || right instanceof Unmergeable) { // leave joined=null, cannot join From ac60eae2a0926db351576de45db7fb27d3ae5739 Mon Sep 17 00:00:00 2001 From: Andrei Pozolotin Date: Fri, 10 Aug 2012 12:15:37 -0500 Subject: [PATCH 3/4] left-associative: test with list and test with object --- .../config/impl/ArrayWithObjectTest.java | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java b/config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java index 4b4256910..a9c6a8419 100644 --- a/config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java +++ b/config/src/test/java/com/typesafe/config/impl/ArrayWithObjectTest.java @@ -116,7 +116,7 @@ public void test6() { @Test public void test7() { - final String text = "{ item = { a:1, b:2 } , list = [ { a:2} ] ${item} }"; + final String text = "{ item = { a:1, b:2 } , list = [ { a:2 } ] ${item} }"; Config config = ConfigFactory.parseString(text).resolve(); @@ -129,4 +129,46 @@ public void test7() { } + /** left-associative: fall-back to object then concatenate with list */ + @Test + public void test8() { + + final String text = "{ list = [ { a:1 } ] { b:2 } [ { c:3 } ] }"; + + Config config = ConfigFactory.parseString(text); + + log(config); + + ConfigList list = config.getList("list"); + assertEquals("two item list", list.size(), 2); + + Config entry0 = config(list.get(0)); + assertEquals("provided a", entry0.getNumber("a"), 1); + assertEquals("default b", entry0.getNumber("b"), 2); + + Config entry1 = config(list.get(1)); + assertEquals("concat c", entry1.getNumber("c"), 3); + + } + + /** left-associative: fall-back to object 1 then fall-back to object 2 */ + @Test + public void test9() { + + final String text = "{ list = [ { a:1 } ] { b:2 } { c:3 } }"; + + Config config = ConfigFactory.parseString(text); + + log(config); + + ConfigList list = config.getList("list"); + assertEquals("one item list", list.size(), 1); + + Config entry = config(list.get(0)); + assertEquals("provided a", entry.getNumber("a"), 1); + assertEquals("object 1 b", entry.getNumber("b"), 2); + assertEquals("object 2 c", entry.getNumber("c"), 3); + + } + } From 4ed8865840ced6464b617b712ba3af9ef8c255fd Mon Sep 17 00:00:00 2001 From: Andrei Pozolotin Date: Fri, 10 Aug 2012 12:56:07 -0500 Subject: [PATCH 4/4] document ```[]{}``` construct --- HOCON.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/HOCON.md b/HOCON.md index 11babb556..a246af888 100644 --- a/HOCON.md +++ b/HOCON.md @@ -249,7 +249,7 @@ of at least three quotes ends the multi-line string, and any ### Value concatenation The value of an object field or array element may consist of -multiple values which are combined. There are three kinds of value +multiple values which are combined. There are 4 kinds of value concatenation: - if all the values are simple values (neither objects nor @@ -258,6 +258,7 @@ concatenation: one array. - if all the values are objects, they are merged (as with duplicate keys) into one object. + - array can use a fall back object for each of its elements String value concatenation is allowed in field keys, in addition to field values and array elements. Objects and arrays do not make @@ -332,7 +333,7 @@ concatenation and converted to a string. #### Array and object concatenation Arrays can be concatenated with arrays, and objects with objects, -but it is an error if they are mixed. +but it is an error if they are mixed, (except for ```[]{}``` construct, see below) For purposes of concatenation, "array" also means "substitution that resolves to an array" and "object" also means "substitution @@ -382,6 +383,23 @@ A common use of array concatenation is to add to paths: path = [ /bin ] path = ${path} [ /usr/bin ] +#### Array with element fall back object concatenation + +the ```[]{}``` construct is permitted and means: +"apply object as a fall back to each array element" + +for example + + list = [ {a:1}, {b:2}, {c:3} ] { a:-1, b:-2, c:-3 } + +is equivalent to + + list = [ + { a: 1, b:-2, c:-3 } + { a:-1, b: 2, c:-3 } + { a:-1, b:-2, c: 3 } + ] + #### Note: Arrays without commas or newlines Arrays allow you to use newlines instead of commas, but not