mirror of
https://github.com/lightbend/config.git
synced 2025-03-23 07:40:25 +08:00
use full path parser for ConfigObject getters
This commit is contained in:
parent
cfff60131e
commit
a70bb89c2a
@ -13,11 +13,9 @@ import java.util.Set;
|
||||
* a type that the value can't be converted to. ConfigException.Null is a
|
||||
* subclass of ConfigException.WrongType thrown if the value is null. The "path"
|
||||
* parameters for all the getters have periods between the key names, so the
|
||||
* path "a.b.c" looks for key c in object b in object a in the root object.
|
||||
*
|
||||
*
|
||||
* TODO If you need to look up a key with a period in its name, there isn't a
|
||||
* way to do it right now.
|
||||
* path "a.b.c" looks for key c in object b in object a in the root object. (The
|
||||
* syntax for paths is the same as in ${} substitution expressions in config
|
||||
* files, sometimes double quotes are needed around special characters.)
|
||||
*
|
||||
* TODO add OrNull variants of all these getters? Or better to avoid convenience
|
||||
* API for that?
|
||||
|
@ -112,37 +112,53 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||
}
|
||||
|
||||
static private AbstractConfigValue resolve(AbstractConfigObject self,
|
||||
String path,
|
||||
String pathExpression,
|
||||
ConfigValueType expected, ConfigTransformer transformer,
|
||||
String originalPath) {
|
||||
String key = ConfigUtil.firstElement(path);
|
||||
String next = ConfigUtil.otherElements(path);
|
||||
Path path = Path.newPath(pathExpression);
|
||||
return resolve(self, path, expected, transformer, originalPath);
|
||||
}
|
||||
|
||||
static private AbstractConfigValue resolveKey(AbstractConfigObject self,
|
||||
String key, ConfigValueType expected,
|
||||
ConfigTransformer transformer, String originalPath) {
|
||||
AbstractConfigValue v = self.peek(key);
|
||||
if (v == null)
|
||||
throw new ConfigException.Missing(originalPath);
|
||||
|
||||
if (expected != null && transformer != null)
|
||||
v = transformer.transform(v, expected);
|
||||
|
||||
if (v.valueType() == ConfigValueType.NULL)
|
||||
throw new ConfigException.Null(v.origin(), originalPath,
|
||||
expected != null ? expected.name() : null);
|
||||
else if (expected != null && v.valueType() != expected)
|
||||
throw new ConfigException.WrongType(v.origin(), originalPath,
|
||||
expected.name(), v.valueType().name());
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
static private AbstractConfigValue resolve(AbstractConfigObject self,
|
||||
Path path, ConfigValueType expected, ConfigTransformer transformer,
|
||||
String originalPath) {
|
||||
String key = path.first();
|
||||
Path next = path.remainder();
|
||||
if (next == null) {
|
||||
AbstractConfigValue v = self.peek(key);
|
||||
if (v == null)
|
||||
throw new ConfigException.Missing(originalPath);
|
||||
|
||||
if (expected != null && transformer != null)
|
||||
v = transformer.transform(v, expected);
|
||||
|
||||
if (v.valueType() == ConfigValueType.NULL)
|
||||
throw new ConfigException.Null(v.origin(), originalPath,
|
||||
expected != null ? expected.name() : null);
|
||||
else if (expected != null && v.valueType() != expected)
|
||||
throw new ConfigException.WrongType(v.origin(), originalPath,
|
||||
expected.name(), v.valueType().name());
|
||||
else
|
||||
return v;
|
||||
return resolveKey(self, key, expected, transformer, originalPath);
|
||||
} else {
|
||||
AbstractConfigObject o = self.getObject(key);
|
||||
AbstractConfigObject o = (AbstractConfigObject) resolveKey(self,
|
||||
key, ConfigValueType.OBJECT, transformer, originalPath);
|
||||
assert (o != null); // missing was supposed to throw
|
||||
return resolve(o, next, expected, transformer, originalPath);
|
||||
}
|
||||
}
|
||||
|
||||
AbstractConfigValue resolve(String path, ConfigValueType expected,
|
||||
AbstractConfigValue resolve(String pathExpression,
|
||||
ConfigValueType expected,
|
||||
String originalPath) {
|
||||
return resolve(this, path, expected, transformer, originalPath);
|
||||
return resolve(this, pathExpression, expected, transformer,
|
||||
originalPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,54 +1,7 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
|
||||
final class ConfigUtil {
|
||||
static void verifyPath(String path) {
|
||||
if (path.startsWith("."))
|
||||
throw new ConfigException.BadPath(path, "Path starts with '.'");
|
||||
if (path.endsWith("."))
|
||||
throw new ConfigException.BadPath(path, "Path ends with '.'");
|
||||
if (path.contains(".."))
|
||||
throw new ConfigException.BadPath(path,
|
||||
"Path contains '..' (empty element)");
|
||||
}
|
||||
|
||||
static String firstElement(String path) {
|
||||
verifyPath(path);
|
||||
int i = path.indexOf('.');
|
||||
if (i < 0)
|
||||
return path;
|
||||
else
|
||||
return path.substring(0, i);
|
||||
}
|
||||
|
||||
static String otherElements(String path) {
|
||||
verifyPath(path);
|
||||
int i = path.indexOf('.');
|
||||
if (i < 0)
|
||||
return null;
|
||||
else
|
||||
return path.substring(i + 1);
|
||||
}
|
||||
|
||||
static String lastElement(String path) {
|
||||
verifyPath(path);
|
||||
int i = path.lastIndexOf('.');
|
||||
if (i < 0)
|
||||
return path;
|
||||
else
|
||||
return path.substring(i + 1);
|
||||
}
|
||||
|
||||
static String exceptLastElement(String path) {
|
||||
verifyPath(path);
|
||||
int i = path.lastIndexOf('.');
|
||||
if (i < 0)
|
||||
return null;
|
||||
else
|
||||
return path.substring(0, i);
|
||||
}
|
||||
|
||||
static boolean equalsHandlingNull(Object a, Object b) {
|
||||
if (a == null && b != null)
|
||||
return false;
|
||||
|
@ -79,6 +79,34 @@ final class Loader {
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyPath(String path) {
|
||||
if (path.startsWith("."))
|
||||
throw new ConfigException.BadPath(path, "Path starts with '.'");
|
||||
if (path.endsWith("."))
|
||||
throw new ConfigException.BadPath(path, "Path ends with '.'");
|
||||
if (path.contains(".."))
|
||||
throw new ConfigException.BadPath(path,
|
||||
"Path contains '..' (empty element)");
|
||||
}
|
||||
|
||||
static String lastElement(String path) {
|
||||
verifyPath(path);
|
||||
int i = path.lastIndexOf('.');
|
||||
if (i < 0)
|
||||
return path;
|
||||
else
|
||||
return path.substring(i + 1);
|
||||
}
|
||||
|
||||
static String exceptLastElement(String path) {
|
||||
verifyPath(path);
|
||||
int i = path.lastIndexOf('.');
|
||||
if (i < 0)
|
||||
return null;
|
||||
else
|
||||
return path.substring(0, i);
|
||||
}
|
||||
|
||||
static AbstractConfigObject fromProperties(String originPrefix,
|
||||
Properties props) {
|
||||
Map<String, Map<String, AbstractConfigValue>> scopes = new HashMap<String, Map<String, AbstractConfigValue>>();
|
||||
@ -88,8 +116,8 @@ final class Loader {
|
||||
if (o instanceof String) {
|
||||
try {
|
||||
String path = (String) o;
|
||||
String last = ConfigUtil.lastElement(path);
|
||||
String exceptLast = ConfigUtil.exceptLastElement(path);
|
||||
String last = lastElement(path);
|
||||
String exceptLast = exceptLastElement(path);
|
||||
if (exceptLast == null)
|
||||
exceptLast = "";
|
||||
Map<String, AbstractConfigValue> scope = scopes
|
||||
@ -116,7 +144,7 @@ final class Loader {
|
||||
|
||||
// put everything in its parent, ensuring all parents exist
|
||||
for (String path : childPaths) {
|
||||
String parentPath = ConfigUtil.exceptLastElement(path);
|
||||
String parentPath = exceptLastElement(path);
|
||||
if (parentPath == null)
|
||||
parentPath = "";
|
||||
|
||||
@ -132,7 +160,7 @@ final class Loader {
|
||||
AbstractConfigObject o = new SimpleConfigObject(
|
||||
new SimpleConfigOrigin(originPrefix + " " + path), null,
|
||||
scopes.get(path));
|
||||
String basename = ConfigUtil.lastElement(path);
|
||||
String basename = lastElement(path);
|
||||
parent.put(basename, o);
|
||||
}
|
||||
|
||||
|
@ -19,24 +19,6 @@ final class PathBuilder {
|
||||
"Adding to PathBuilder after getting result");
|
||||
}
|
||||
|
||||
void appendPath(String path) {
|
||||
checkCanAppend();
|
||||
ConfigUtil.verifyPath(path);
|
||||
|
||||
String next = ConfigUtil.firstElement(path);
|
||||
String remainder = ConfigUtil.otherElements(path);
|
||||
|
||||
while (next != null) {
|
||||
keys.push(next);
|
||||
if (remainder != null) {
|
||||
next = ConfigUtil.firstElement(remainder);
|
||||
remainder = ConfigUtil.otherElements(remainder);
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void appendKey(String key) {
|
||||
checkCanAppend();
|
||||
|
||||
|
@ -63,6 +63,7 @@ class ConfParserTest extends TestUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also parse with the standalone path parser and be sure the
|
||||
// outcome is the same
|
||||
val shouldBeSame = Parser.parsePath(s)
|
||||
@ -93,6 +94,7 @@ class ConfParserTest extends TestUtils {
|
||||
assertEquals(path(""), parsePath("\"\"\"\""))
|
||||
assertEquals(path("a", ""), parsePath("a.\"\"\"\""))
|
||||
assertEquals(path("", "b"), parsePath("\"\"\"\".b"))
|
||||
assertEquals(path("", "", ""), parsePath(""" "".""."" """))
|
||||
|
||||
for (invalid <- Seq("a.", ".b", "a..b", "a${b}c", "\"\".", ".\"\"")) {
|
||||
try {
|
||||
|
@ -370,7 +370,7 @@ class ConfigTest extends TestUtils {
|
||||
}
|
||||
|
||||
@Test
|
||||
def test02WeirdPaths() {
|
||||
def test02SubstitutionsWithWeirdPaths() {
|
||||
val conf = Config.load("test02")
|
||||
|
||||
assertEquals(42, conf.getInt("42_a"))
|
||||
@ -380,4 +380,16 @@ class ConfigTest extends TestUtils {
|
||||
assertEquals(57, conf.getInt("57_b"))
|
||||
assertEquals(103, conf.getInt("103_a"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def test02UseWeirdPathsWithConfigObject() {
|
||||
val conf = Config.load("test02")
|
||||
|
||||
// we're checking that the getters in ConfigObject support
|
||||
// these weird path expressions
|
||||
assertEquals(42, conf.getInt(""" "".""."" """))
|
||||
assertEquals(57, conf.getInt("a.b.c"))
|
||||
assertEquals(57, conf.getInt(""" "a"."b"."c" """))
|
||||
assertEquals(103, conf.getInt(""" "a.b.c" """))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user