implement ConfigObject.hasPath()

This commit is contained in:
Havoc Pennington 2011-11-15 23:30:03 -05:00
parent f32c8bdd78
commit f81ab20caa
3 changed files with 66 additions and 3 deletions

View File

@ -18,10 +18,13 @@ import java.util.Map;
* files, sometimes double quotes are needed around special characters.)
*
* ConfigObject implements the standard Java Map interface, but the mutator
* methods all throw UnsupportedOperationException.
* methods all throw UnsupportedOperationException. This Map is immutable.
*
* TODO add OrNull variants of all these getters? Or better to avoid convenience
* API for that?
* The Map may contain null values, which will have ConfigValue.valueType() ==
* ConfigValueType.NULL. When using methods from the Map interface, such as
* get() or containsKey(), these null ConfigValue will be visible. But hasPath()
* returns false for null values, and getInt() etc. throw ConfigException.Null
* for null values.
*/
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
@ -38,6 +41,25 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
@Override
ConfigObject withFallbacks(ConfigValue... others);
/**
* Checks whether a value is present and non-null at the given path. This
* differs in two ways from containsKey(): it looks for a path expression,
* not a key; and it returns false for null values, while containsKey()
* returns true indicating that the object contains a null value for the
* key.
*
* If a path exists according to hasPath(), then getValue() will never throw
* an exception. However, the typed getters, such as getInt(), will still
* throw if the value is not convertible to the requested type.
*
* @param path
* the path expression
* @return true if a non-null value is present at the path
* @throws ConfigException.BadPath
* if the path expression is invalid
*/
boolean hasPath(String path);
boolean getBoolean(String path);
Number getNumber(String path);

View File

@ -121,6 +121,13 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
return ConfigValueType.OBJECT;
}
@Override
public boolean hasPath(String pathExpression) {
Path path = Path.newPath(pathExpression);
ConfigValue peeked = peekPath(path, null, 0, null);
return peeked != null && peeked.valueType() != ConfigValueType.NULL;
}
@Override
AbstractConfigObject transformed(ConfigTransformer newTransformer) {
if (newTransformer != transformer)

View File

@ -371,4 +371,38 @@ class ConfigValueTest extends TestUtils {
// merge three
assertEquals("merge of a,b,c", m(o("a", false), o("b", false), o("c", false)))
}
@Test
def hasPathWorks() {
val empty = parseObject("{}")
assertFalse(empty.hasPath("foo"))
val obj = parseObject("a=null, b.c.d=11, foo=bar")
// returns true for the non-null values
assertTrue(obj.hasPath("foo"))
assertTrue(obj.hasPath("b.c.d"))
assertTrue(obj.hasPath("b.c"))
assertTrue(obj.hasPath("b"))
// hasPath() is false for null values but containsKey is true
assertEquals(nullValue(), obj.get("a"))
assertTrue(obj.containsKey("a"))
assertFalse(obj.hasPath("a"))
// false for totally absent values
assertFalse(obj.containsKey("notinhere"))
assertFalse(obj.hasPath("notinhere"))
// throws proper exceptions
intercept[ConfigException.BadPath] {
empty.hasPath("a.")
}
intercept[ConfigException.BadPath] {
empty.hasPath("..")
}
}
}