diff --git a/src/main/java/com/typesafe/config/Config.java b/src/main/java/com/typesafe/config/Config.java index 04518802..b6c0380d 100644 --- a/src/main/java/com/typesafe/config/Config.java +++ b/src/main/java/com/typesafe/config/Config.java @@ -69,6 +69,10 @@ public final class Config { return ConfigImpl.emptyRoot(rootPath); } + public static ConfigObject empty() { + return ConfigImpl.empty(); + } + public static ConfigRoot systemPropertiesRoot(String rootPath) { return ConfigImpl.systemPropertiesRoot(rootPath); } diff --git a/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java b/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java index 2c4d2621..a573d8e4 100644 --- a/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java +++ b/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java @@ -233,18 +233,39 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements if (stack.isEmpty()) throw new ConfigException.BugOrBroken( "can't merge origins on empty list"); + final String prefix = "merge of "; StringBuilder sb = new StringBuilder(); - String prefix = "merge of "; - sb.append(prefix); + ConfigOrigin firstOrigin = null; + int numMerged = 0; for (AbstractConfigValue v : stack) { + if (firstOrigin == null) + firstOrigin = v.origin(); + String desc = v.origin().description(); if (desc.startsWith(prefix)) desc = desc.substring(prefix.length()); - sb.append(desc); - sb.append(","); + + if (v instanceof ConfigObject && ((ConfigObject) v).isEmpty()) { + // don't include empty files or the .empty() + // config in the description, since they are + // likely to be "implementation details" + } else { + sb.append(desc); + sb.append(","); + numMerged += 1; + } + } + if (numMerged > 0) { + sb.setLength(sb.length() - 1); // chop comma + if (numMerged > 1) { + return new SimpleConfigOrigin(prefix + sb.toString()); + } else { + return new SimpleConfigOrigin(sb.toString()); + } + } else { + // the configs were all empty. + return firstOrigin; } - sb.setLength(sb.length() - 1); // chop comma - return new SimpleConfigOrigin(sb.toString()); } static ConfigOrigin mergeOrigins(AbstractConfigObject... stack) { diff --git a/src/main/java/com/typesafe/config/impl/ConfigImpl.java b/src/main/java/com/typesafe/config/impl/ConfigImpl.java index b54495bb..65d686fd 100644 --- a/src/main/java/com/typesafe/config/impl/ConfigImpl.java +++ b/src/main/java/com/typesafe/config/impl/ConfigImpl.java @@ -160,7 +160,13 @@ public class ConfigImpl { /** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */ public static ConfigRoot emptyRoot(String rootPath) { - return SimpleConfigObject.empty().asRoot(Parser.parsePath(rootPath)); + return SimpleConfigObject.empty(new SimpleConfigOrigin(rootPath)) + .asRoot( + Parser.parsePath(rootPath)); + } + + public static ConfigObject empty() { + return SimpleConfigObject.empty(); } /** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */ diff --git a/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java b/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java index 83ca5d91..b5fda36f 100644 --- a/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java +++ b/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java @@ -113,8 +113,10 @@ final class SimpleConfigObject extends AbstractConfigObject { return new HashSet(value.values()); } + final static String EMPTY_NAME = "empty config"; + final static SimpleConfigObject empty() { - return new SimpleConfigObject(new SimpleConfigOrigin("empty config"), + return new SimpleConfigObject(new SimpleConfigOrigin(EMPTY_NAME), Collections. emptyMap()); } diff --git a/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala b/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala index 0099288e..620963c6 100644 --- a/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala +++ b/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala @@ -9,6 +9,7 @@ import com.typesafe.config.ConfigObject import com.typesafe.config.ConfigList import com.typesafe.config.ConfigException import com.typesafe.config.ConfigValueType +import com.typesafe.config.ConfigOrigin class ConfigValueTest extends TestUtils { @@ -330,4 +331,31 @@ class ConfigValueTest extends TestUtils { assertEquals(Seq(a, b, c, d) map { "xx " + _ + " yy" }, Seq("a", "b", "c", "d") map { obj2.getString(_) }) } + + @Test + def mergeOriginsWorks() { + def o(desc: String, empty: Boolean) = { + val values = new java.util.HashMap[String, AbstractConfigValue]() + if (!empty) + values.put("hello", intValue(37)) + new SimpleConfigObject(new SimpleConfigOrigin(desc), values); + } + def m(values: AbstractConfigObject*) = { + AbstractConfigObject.mergeOrigins(values: _*).description() + } + + // simplest case + assertEquals("merge of a,b", m(o("a", false), o("b", false))) + // combine duplicate "merge of" + assertEquals("merge of a,x,y", m(o("a", false), o("merge of x,y", false))) + assertEquals("merge of a,b,x,y", m(o("merge of a,b", false), o("merge of x,y", false))) + // ignore empty objects + assertEquals("a", m(o("foo", true), o("a", false))) + // unless they are all empty, pick the first one + assertEquals("foo", m(o("foo", true), o("a", true))) + // merge just one + assertEquals("foo", m(o("foo", false))) + // merge three + assertEquals("merge of a,b,c", m(o("a", false), o("b", false), o("c", false))) + } }