mirror of
https://github.com/lightbend/config.git
synced 2025-01-16 07:10:21 +08:00
make ConfigObject implement java.util.Map<String, ConfigValue>
This commit is contained in:
parent
c677a23b3b
commit
be63b620f6
@ -1,7 +1,7 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A ConfigObject is a read-only configuration object, which may have nested
|
||||
@ -17,13 +17,13 @@ import java.util.Set;
|
||||
* syntax for paths is the same as in ${} substitution expressions in config
|
||||
* files, sometimes double quotes are needed around special characters.)
|
||||
*
|
||||
* ConfigObject implements the standard Java Map interface, but the mutator
|
||||
* methods all throw UnsupportedOperationException.
|
||||
*
|
||||
* TODO add OrNull variants of all these getters? Or better to avoid convenience
|
||||
* API for that?
|
||||
*
|
||||
* TODO should it implement Map<String, ? extends ConfigValue> with the mutators
|
||||
* throwing ?
|
||||
*/
|
||||
public interface ConfigObject extends ConfigValue {
|
||||
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||
|
||||
boolean getBoolean(String path);
|
||||
|
||||
@ -46,16 +46,14 @@ public interface ConfigObject extends ConfigValue {
|
||||
Object getAny(String path);
|
||||
|
||||
/**
|
||||
* Gets the value at the path as a ConfigValue.
|
||||
*
|
||||
* TODO conceptually if we want to match a read-only subset of the
|
||||
* Map<String, ? extends ConfigValue> interface, we would need to take a key
|
||||
* instead of a path here and return null instead of throwing an exception.
|
||||
* Gets the value at the given path, unless the value is a null value or
|
||||
* missing, in which case it throws just like the other getters. Use get()
|
||||
* from the Map interface if you want an unprocessed value.
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
ConfigValue get(String path);
|
||||
ConfigValue getValue(String path);
|
||||
|
||||
/** Get value as a size in bytes (parses special strings like "128M") */
|
||||
// rename getSizeInBytes ? clearer. allows a megabyte version
|
||||
@ -76,6 +74,7 @@ public interface ConfigObject extends ConfigValue {
|
||||
*/
|
||||
Long getNanoseconds(String path);
|
||||
|
||||
/* TODO should this return an iterator instead? */
|
||||
List<? extends ConfigValue> getList(String path);
|
||||
|
||||
List<Boolean> getBooleanList(String path);
|
||||
@ -100,7 +99,12 @@ public interface ConfigObject extends ConfigValue {
|
||||
|
||||
List<Long> getNanosecondsList(String path);
|
||||
|
||||
boolean containsKey(String key);
|
||||
|
||||
Set<String> keySet();
|
||||
/**
|
||||
* Gets a ConfigValue at the given key, or returns null if there is no
|
||||
* value. The returned ConfigValue may have ConfigValueType.NULL or any
|
||||
* other type, and the passed-in key must be a key in this object, rather
|
||||
* than a path expression.
|
||||
*/
|
||||
@Override
|
||||
ConfigValue get(Object key);
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||
this.transformer = transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract public Map<String, Object> unwrapped();
|
||||
|
||||
/**
|
||||
* This looks up the key with no transformation or type conversion of any
|
||||
* kind, and returns null if the key is not present.
|
||||
@ -244,7 +247,15 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValue get(String path) {
|
||||
public ConfigValue get(Object key) {
|
||||
if (key instanceof String)
|
||||
return peek((String) key);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValue getValue(String path) {
|
||||
return find(path, null, path);
|
||||
}
|
||||
|
||||
@ -487,4 +498,30 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static UnsupportedOperationException weAreImmutable(String method) {
|
||||
return new UnsupportedOperationException(
|
||||
"ConfigObject is immutable, you can't call Map.'" + method
|
||||
+ "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw weAreImmutable("clear");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValue put(String arg0, ConfigValue arg1) {
|
||||
throw weAreImmutable("put");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends ConfigValue> arg0) {
|
||||
throw weAreImmutable("putAll");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValue remove(Object arg0) {
|
||||
throw weAreImmutable("remove");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -10,6 +13,7 @@ import java.util.Set;
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigObject;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
class SimpleConfigObject extends AbstractConfigObject {
|
||||
|
||||
@ -31,16 +35,16 @@ class SimpleConfigObject extends AbstractConfigObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrapped() {
|
||||
public Map<String, Object> unwrapped() {
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
for (String k : value.keySet()) {
|
||||
m.put(k, value.get(k).unwrapped());
|
||||
for (Map.Entry<String, AbstractConfigValue> e : value.entrySet()) {
|
||||
m.put(e.getKey(), e.getValue().unwrapped());
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
public boolean containsKey(Object key) {
|
||||
return value.containsKey(key);
|
||||
}
|
||||
|
||||
@ -104,4 +108,37 @@ class SimpleConfigObject extends AbstractConfigObject {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
return mapHash(this.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object v) {
|
||||
return value.containsValue(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<String, ConfigValue>> entrySet() {
|
||||
// total bloat just to work around lack of type variance
|
||||
|
||||
HashSet<java.util.Map.Entry<String, ConfigValue>> entries = new HashSet<Map.Entry<String, ConfigValue>>();
|
||||
for (Map.Entry<String, AbstractConfigValue> e : value.entrySet()) {
|
||||
entries.add(new AbstractMap.SimpleImmutableEntry<String, ConfigValue>(
|
||||
e.getKey(), e
|
||||
.getValue()));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return value.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return value.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigValue> values() {
|
||||
return new HashSet<ConfigValue>(value.values());
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
class TransformedConfigObject extends AbstractConfigObject {
|
||||
|
||||
private AbstractConfigObject underlying;
|
||||
@ -13,7 +17,7 @@ class TransformedConfigObject extends AbstractConfigObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
public boolean containsKey(Object key) {
|
||||
return underlying.containsKey(key);
|
||||
}
|
||||
|
||||
@ -23,7 +27,7 @@ class TransformedConfigObject extends AbstractConfigObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrapped() {
|
||||
public Map<String, Object> unwrapped() {
|
||||
return underlying.unwrapped();
|
||||
}
|
||||
|
||||
@ -31,4 +35,29 @@ class TransformedConfigObject extends AbstractConfigObject {
|
||||
protected AbstractConfigValue peek(String key) {
|
||||
return underlying.peek(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return underlying.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<java.util.Map.Entry<String, ConfigValue>> entrySet() {
|
||||
return underlying.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return underlying.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return underlying.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigValue> values() {
|
||||
return underlying.values();
|
||||
}
|
||||
}
|
||||
|
@ -156,8 +156,15 @@ class ConfigTest extends TestUtils {
|
||||
assertEquals("null bar 42 baz true 3.14 hi", conf.getString("strings.concatenated"))
|
||||
assertEquals(true, conf.getBoolean("booleans.trueAgain"))
|
||||
assertEquals(false, conf.getBoolean("booleans.falseAgain"))
|
||||
// FIXME need to add a way to get a null
|
||||
//assertEquals(null, conf.getAny("nulls.null"))
|
||||
|
||||
// to get null we have to use the get() method from Map,
|
||||
// which takes a key and not a path
|
||||
assertEquals(nullValue(), conf.getObject("nulls").get("null"))
|
||||
assertNull(conf.get("notinthefile"))
|
||||
|
||||
// get stuff with getValue
|
||||
assertEquals(intValue(42), conf.getValue("ints.fortyTwo"))
|
||||
assertEquals(stringValue("abcd"), conf.getValue("strings.abcd"))
|
||||
|
||||
// get stuff with getAny
|
||||
assertEquals(42L, conf.getAny("ints.fortyTwo"))
|
||||
|
@ -4,6 +4,8 @@ import org.junit.Assert._
|
||||
import org.junit._
|
||||
import com.typesafe.config.ConfigValue
|
||||
import java.util.Collections
|
||||
import scala.collection.JavaConverters._
|
||||
import com.typesafe.config.ConfigObject
|
||||
|
||||
class ConfigValueTest extends TestUtils {
|
||||
|
||||
@ -90,4 +92,49 @@ class ConfigValueTest extends TestUtils {
|
||||
subst("a").toString()
|
||||
substInString("b").toString()
|
||||
}
|
||||
|
||||
private def unsupported(body: => Unit) {
|
||||
intercept[UnsupportedOperationException] {
|
||||
body
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def configObjectUnwraps() {
|
||||
val m = new SimpleConfigObject(fakeOrigin(), null, configMap("a" -> 1, "b" -> 2, "c" -> 3))
|
||||
assertEquals(Map("a" -> 1, "b" -> 2, "c" -> 3), m.unwrapped().asScala)
|
||||
}
|
||||
|
||||
@Test
|
||||
def configObjectImplementsMap() {
|
||||
val m: ConfigObject = new SimpleConfigObject(fakeOrigin(), null, configMap("a" -> 1, "b" -> 2, "c" -> 3))
|
||||
|
||||
assertEquals(intValue(1), m.get("a"))
|
||||
assertEquals(intValue(2), m.get("b"))
|
||||
assertEquals(intValue(3), m.get("c"))
|
||||
assertNull(m.get("d"))
|
||||
|
||||
assertTrue(m.containsKey("a"))
|
||||
assertFalse(m.containsKey("z"))
|
||||
|
||||
assertTrue(m.containsValue(intValue(1)))
|
||||
assertFalse(m.containsValue(intValue(10)))
|
||||
|
||||
assertFalse(m.isEmpty())
|
||||
|
||||
assertEquals(3, m.size())
|
||||
|
||||
val values = Set(intValue(1), intValue(2), intValue(3))
|
||||
assertEquals(values, m.values().asScala.toSet)
|
||||
assertEquals(values, m.entrySet().asScala map { _.getValue() } toSet)
|
||||
|
||||
val keys = Set("a", "b", "c")
|
||||
assertEquals(keys, m.keySet().asScala.toSet)
|
||||
assertEquals(keys, m.entrySet().asScala map { _.getKey() } toSet)
|
||||
|
||||
unsupported { m.clear() }
|
||||
unsupported { m.put("hello", intValue(42)) }
|
||||
unsupported { m.putAll(Collections.emptyMap[String, AbstractConfigValue]()) }
|
||||
unsupported { m.remove("a") }
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user