mirror of
https://github.com/lightbend/config.git
synced 2025-01-15 23:01:05 +08:00
add withValue method to Config and ConfigObject
This makes it easier to build up a config in code.
This commit is contained in:
parent
d15ade876a
commit
bd820702e6
4
NEWS.md
4
NEWS.md
@ -14,6 +14,10 @@
|
||||
where one was an empty string, and now you can't. As far as
|
||||
I know, the empty string was always worthless in this case
|
||||
and can just be removed.
|
||||
- added methods atPath() and atKey() to ConfigValue, to wrap
|
||||
the value into a Config
|
||||
- added method withValue() to Config and ConfigObject,
|
||||
to add a value at a given path or key
|
||||
|
||||
# 0.4.1: May 22, 2012
|
||||
|
||||
|
@ -527,4 +527,18 @@ public interface Config extends ConfigMergeable {
|
||||
* key.
|
||||
*/
|
||||
Config atKey(String key);
|
||||
|
||||
/**
|
||||
* Returns a {@code Config} based on this one, but with the given path set
|
||||
* to the given value. Does not modify this instance (since it's immutable).
|
||||
* If the path already has a value, that value is replaced. To remove a
|
||||
* value, use withoutPath().
|
||||
*
|
||||
* @param path
|
||||
* path to add
|
||||
* @param value
|
||||
* value at the new path
|
||||
* @return the new instance with the new map entry
|
||||
*/
|
||||
Config withValue(String path, ConfigValue value);
|
||||
}
|
||||
|
@ -110,4 +110,18 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||
* @return a copy of the object minus the specified key
|
||||
*/
|
||||
ConfigObject withoutKey(String key);
|
||||
|
||||
/**
|
||||
* Returns a {@code ConfigObject} based on this one, but with the given key
|
||||
* set to the given value. Does not modify this instance (since it's
|
||||
* immutable). If the key already has a value, that value is replaced. To
|
||||
* remove a value, use withoutKey().
|
||||
*
|
||||
* @param key
|
||||
* key to add
|
||||
* @param value
|
||||
* value at the new key
|
||||
* @return the new instance with the new map entry
|
||||
*/
|
||||
ConfigObject withValue(String key, ConfigValue value);
|
||||
}
|
||||
|
@ -42,12 +42,17 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements Confi
|
||||
@Override
|
||||
abstract public AbstractConfigObject withoutKey(String key);
|
||||
|
||||
@Override
|
||||
abstract public AbstractConfigObject withValue(String key, ConfigValue value);
|
||||
|
||||
abstract protected AbstractConfigObject withOnlyPathOrNull(Path path);
|
||||
|
||||
abstract AbstractConfigObject withOnlyPath(Path path);
|
||||
|
||||
abstract AbstractConfigObject withoutPath(Path path);
|
||||
|
||||
abstract AbstractConfigObject withValue(Path path, ConfigValue value);
|
||||
|
||||
/**
|
||||
* This looks up the key with no transformation or type conversion of any
|
||||
* kind, and returns null if the key is not present. The object must be
|
||||
|
@ -9,7 +9,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
@ -340,10 +339,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
||||
return atKey(SimpleConfigOrigin.newSimple("atKey(" + key + ")"), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config atPath(String pathExpression) {
|
||||
SimpleConfigOrigin origin = SimpleConfigOrigin.newSimple("atPath(" + pathExpression + ")");
|
||||
Path path = Path.newPath(pathExpression);
|
||||
SimpleConfig atPath(ConfigOrigin origin, Path path) {
|
||||
Path parent = path.parent();
|
||||
SimpleConfig result = atKey(origin, path.last());
|
||||
while (parent != null) {
|
||||
@ -353,4 +349,10 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleConfig atPath(String pathExpression) {
|
||||
SimpleConfigOrigin origin = SimpleConfigOrigin.newSimple("atPath(" + pathExpression + ")");
|
||||
return atPath(origin, Path.newPath(pathExpression));
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +140,16 @@ final class ConfigDelayedMergeObject extends AbstractConfigObject implements Unm
|
||||
throw notResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigDelayedMergeObject withValue(String key, ConfigValue value) {
|
||||
throw notResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
ConfigDelayedMergeObject withValue(Path path, ConfigValue value) {
|
||||
throw notResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<AbstractConfigValue> unmergedValues() {
|
||||
return stack;
|
||||
|
@ -846,6 +846,12 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
||||
return new SimpleConfig(root().withoutPath(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleConfig withValue(String pathExpression, ConfigValue v) {
|
||||
Path path = Path.newPath(pathExpression);
|
||||
return new SimpleConfig(root().withValue(path, v));
|
||||
}
|
||||
|
||||
SimpleConfig atKey(ConfigOrigin origin, String key) {
|
||||
return root().atKey(origin, key);
|
||||
}
|
||||
|
@ -130,6 +130,45 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleConfigObject withValue(String key, ConfigValue v) {
|
||||
if (v == null)
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"Trying to store null ConfigValue in a ConfigObject");
|
||||
|
||||
Map<String, AbstractConfigValue> newMap;
|
||||
if (value.isEmpty()) {
|
||||
newMap = Collections.singletonMap(key, (AbstractConfigValue) v);
|
||||
} else {
|
||||
newMap = new HashMap<String, AbstractConfigValue>(value);
|
||||
newMap.put(key, (AbstractConfigValue) v);
|
||||
}
|
||||
|
||||
return new SimpleConfigObject(origin(), newMap, ResolveStatus.fromValues(newMap.values()),
|
||||
ignoresFallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
SimpleConfigObject withValue(Path path, ConfigValue v) {
|
||||
String key = path.first();
|
||||
Path next = path.remainder();
|
||||
|
||||
if (next == null) {
|
||||
return withValue(key, v);
|
||||
} else {
|
||||
AbstractConfigValue child = value.get(key);
|
||||
if (child != null && child instanceof AbstractConfigObject) {
|
||||
// if we have an object, add to it
|
||||
return withValue(key, ((AbstractConfigObject) child).withValue(next, v));
|
||||
} else {
|
||||
// as soon as we have a non-object, replace it entirely
|
||||
SimpleConfig subtree = ((AbstractConfigValue) v).atPath(
|
||||
SimpleConfigOrigin.newSimple("withValue(" + next.render() + ")"), next);
|
||||
return withValue(key, subtree.root());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue attemptPeekWithPartialResolve(String key) {
|
||||
return value.get(key);
|
||||
|
@ -15,6 +15,7 @@ import com.typesafe.config.ConfigException
|
||||
import com.typesafe.config.ConfigValueType
|
||||
import com.typesafe.config.ConfigOrigin
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
class ConfigValueTest extends TestUtils {
|
||||
|
||||
@ -788,6 +789,72 @@ class ConfigValueTest extends TestUtils {
|
||||
assertTrue(config.origin.description.contains("atKey"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def withValueDepth1FromEmpty() {
|
||||
val v = ConfigValueFactory.fromAnyRef(42)
|
||||
val config = ConfigFactory.empty.withValue("a", v)
|
||||
assertEquals(parseConfig("a=42"), config)
|
||||
assertTrue(config.getValue("a") eq v)
|
||||
}
|
||||
|
||||
@Test
|
||||
def withValueDepth2FromEmpty() {
|
||||
val v = ConfigValueFactory.fromAnyRef(42)
|
||||
val config = ConfigFactory.empty.withValue("a.b", v)
|
||||
assertEquals(parseConfig("a.b=42"), config)
|
||||
assertTrue(config.getValue("a.b") eq v)
|
||||
}
|
||||
|
||||
@Test
|
||||
def withValueDepth3FromEmpty() {
|
||||
val v = ConfigValueFactory.fromAnyRef(42)
|
||||
val config = ConfigFactory.empty.withValue("a.b.c", v)
|
||||
assertEquals(parseConfig("a.b.c=42"), config)
|
||||
assertTrue(config.getValue("a.b.c") eq v)
|
||||
}
|
||||
|
||||
@Test
|
||||
def withValueDepth1OverwritesExisting() {
|
||||
val v = ConfigValueFactory.fromAnyRef(47)
|
||||
val old = v.atPath("a")
|
||||
val config = old.withValue("a", ConfigValueFactory.fromAnyRef(42))
|
||||
assertEquals(parseConfig("a=42"), config)
|
||||
assertEquals(42, config.getInt("a"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def withValueDepth2OverwritesExisting() {
|
||||
val v = ConfigValueFactory.fromAnyRef(47)
|
||||
val old = v.atPath("a.b")
|
||||
val config = old.withValue("a.b", ConfigValueFactory.fromAnyRef(42))
|
||||
assertEquals(parseConfig("a.b=42"), config)
|
||||
assertEquals(42, config.getInt("a.b"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def withValueInsideExistingObject() {
|
||||
val v = ConfigValueFactory.fromAnyRef(47)
|
||||
val old = v.atPath("a.c")
|
||||
val config = old.withValue("a.b", ConfigValueFactory.fromAnyRef(42))
|
||||
assertEquals(parseConfig("a.b=42,a.c=47"), config)
|
||||
assertEquals(42, config.getInt("a.b"))
|
||||
assertEquals(47, config.getInt("a.c"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def withValueBuildComplexConfig() {
|
||||
val v1 = ConfigValueFactory.fromAnyRef(1)
|
||||
val v2 = ConfigValueFactory.fromAnyRef(2)
|
||||
val v3 = ConfigValueFactory.fromAnyRef(3)
|
||||
val v4 = ConfigValueFactory.fromAnyRef(4)
|
||||
val config = ConfigFactory.empty
|
||||
.withValue("a", v1)
|
||||
.withValue("b.c", v2)
|
||||
.withValue("b.d", v3)
|
||||
.withValue("x.y.z", v4)
|
||||
assertEquals(parseConfig("a=1,b.c=2,b.d=3,x.y.z=4"), config)
|
||||
}
|
||||
|
||||
@Test
|
||||
def configOriginsInSerialization() {
|
||||
import scala.collection.JavaConverters._
|
||||
|
Loading…
Reference in New Issue
Block a user