If all concatenation elements are undefined, undefine the field

This is a spec change but the spec was wrong and the implementation
previously just threw an exception.

The spec claimed that ${?foo}${?bar} would become empty string,
but it's more useful for it to just become undefined.
You can add an explicit empty string with "" if you want it
to be empty string.

Fixes .
This commit is contained in:
Havoc Pennington 2014-05-05 11:02:49 -04:00
parent 6218174704
commit 08c4fe3950
3 changed files with 73 additions and 9 deletions
HOCON.md
config/src
main/java/com/typesafe/config/impl
test/scala/com/typesafe/config/impl

View File

@ -575,12 +575,13 @@ If a substitution with the `${?foo}` syntax is undefined:
be created. If the field would have overridden a previously-set
value for the same field, then the previous value remains.
- if it is an array element then the element should not be added.
- if it is part of a value concatenation then it should become an
empty string.
- if it is part of a value concatenation with another string then
it should become an empty string; if part of a value
concatenation with an object or array it should become an empty
object or array.
- `foo : ${?bar}` would avoid creating field `foo` if `bar` is
undefined, but `foo : ${?bar}${?baz}` would be a value
concatenation so if `bar` or `baz` are not defined, the result
is an empty string.
undefined. `foo : ${?bar}${?baz}` would also avoid creating the
field if _both_ `bar` and `baz` are undefined.
Substitutions are only allowed in field values and array
elements (value concatenations), they are not allowed in keys or

View File

@ -189,11 +189,13 @@ final class ConfigConcatenation extends AbstractConfigValue implements Unmergeab
// ConfigConcatenation
if (joined.size() > 1 && context.options().getAllowUnresolved())
return new ConfigConcatenation(this.origin(), joined);
else if (joined.size() != 1)
throw new ConfigException.BugOrBroken(
"Resolved list should always join to exactly one value, not " + joined);
else
else if (joined.isEmpty())
return null; // we had just a list of optional references using ${?}
else if (joined.size() == 1)
return joined.get(0);
else
throw new ConfigException.BugOrBroken("Bug in the library; resolved list was joined to too many values: "
+ joined);
}
@Override

View File

@ -368,4 +368,65 @@ class ConcatenationTest extends TestUtils {
assertTrue(e.getMessage.contains("limitation"))
System.err.println("==============")
}
@Test
def concatUndefinedSubstitutionWithString() {
val conf = parseConfig("""a = foo${?bar}""").resolve()
assertEquals("foo", conf.getString("a"))
}
@Test
def concatDefinedOptionalSubstitutionWithString() {
val conf = parseConfig("""bar=bar, a = foo${?bar}""").resolve()
assertEquals("foobar", conf.getString("a"))
}
@Test
def concatUndefinedSubstitutionWithArray() {
val conf = parseConfig("""a = [1] ${?bar}""").resolve()
assertEquals(Seq(1), conf.getIntList("a").asScala.toList)
}
@Test
def concatDefinedOptionalSubstitutionWithArray() {
val conf = parseConfig("""bar=[2], a = [1] ${?bar}""").resolve()
assertEquals(Seq(1, 2), conf.getIntList("a").asScala.toList)
}
@Test
def concatUndefinedSubstitutionWithObject() {
val conf = parseConfig("""a = { x : "foo" } ${?bar}""").resolve()
assertEquals("foo", conf.getString("a.x"))
}
@Test
def concatDefinedOptionalSubstitutionWithObject() {
val conf = parseConfig("""bar={ y : 42 }, a = { x : "foo" } ${?bar}""").resolve()
assertEquals("foo", conf.getString("a.x"))
assertEquals(42, conf.getInt("a.y"))
}
@Test
def concatTwoUndefinedSubstitutions() {
val conf = parseConfig("""a = ${?foo}${?bar}""").resolve()
assertFalse("no field 'a'", conf.hasPath("a"))
}
@Test
def concatSeveralUndefinedSubstitutions() {
val conf = parseConfig("""a = ${?foo}${?bar}${?baz}${?woooo}""").resolve()
assertFalse("no field 'a'", conf.hasPath("a"))
}
@Test
def concatTwoUndefinedSubstitutionsWithASpace() {
val conf = parseConfig("""a = ${?foo} ${?bar}""").resolve()
assertEquals(" ", conf.getString("a"))
}
@Test
def concatTwoUndefinedSubstitutionsWithEmptyString() {
val conf = parseConfig("""a = ""${?foo}${?bar}""").resolve()
assertEquals("", conf.getString("a"))
}
}