The main case that motivated this was:
a.b.c = 1
a.b.x = 2
where the two a.b objects are merged. With this patch, ConfigOrigin
still reports the filename only once, and correctly, for the a.b
object.
When we see a newline token, its number is the line it was on
(i.e. the line just ended) not the line we are now on.
This was correct in one place in Parser.java but not
in the other place.
Also updated the spec; in this patch, the spec says ${?missing} will be
undefined, but the code still evaluates it to null as in the old
behavior. A follow-up patch will introduce the undefined behavior
that is now specified.
This change is needed because missing substitutions are probably
a problem in most cases, and evaluating missing to null is probably
not as useful as evaluating it to undefined. If it turns out that
we need the null behavior, a possible syntax is ${foo.bar,null}
or something like that.
This makes the resource in the ConfigOrigin nice and predictable.
It also lets us use the full ClassLoader API instead of only the
Class methods that have the special resource name handling.
The general idea here is to permit future syntax extensions;
as a rule, new syntax can only be added by using one of these
characters that can't be a string.
Backtick ` in particular is in the current ECMAScript drafts
for multiline strings, so supporting multiline strings with
that would be a very natural thing to do.
The other characters added here (^, !, ?, @, *, &) are
more or less arbitrary. &, ! and * have special meaning
in YAML so might be natural ones to use for similar
purposes. ? would have some natural "let this be missing"
meaning, @ is often used in template-like things, and ^
could be used for some unforeseen need.
This is clearer about what the object represents and how it relates
to the Config. I didn't name it root() before because it might have
implied some relationship to ConfigRoot. But since ConfigRoot
is now gone, toObject() can have the nicer name root().
This was intended to make resolve() typesafe (only allowed on root objects).
However, in practice it was not safe because it was too easy to create a
root by merging a non-root object into a root object. To fix it, we'd
have to prohibit that (root objects can only merge with other roots).
But it seems simpler to just document that resolving on subtrees
will do the wrong thing, and hopefully it won't be an issue in practice.
Also add some tests and fix some correctness issues.
If we fall back to a value that has ignoresFallbacks()=true, then we need
to "catch" its ignoresFallbacks.
Don't create new RootConfig objects if the underlying has not changed.
Get rid of ConfigImpl.merge() utility, no longer useful.
Had previously fixed AbstractConfigObject.merge() in the same way. For this
patch, factor out the code from AbstractConfigObject and use it everywhere.
Add some tests for merges with substitutions involved.
Also write some docs for ConfigMergeable explaining the easy way
to do it wrong.