mirror of
https://github.com/lightbend/config.git
synced 2025-02-24 02:00:46 +08:00
Fix code to find includes relative to other includes. Support ParseOptions for load().
This commit is contained in:
parent
d257c3bbd9
commit
6ca952e516
@ -52,6 +52,12 @@ public final class Config {
|
|||||||
return loadWithoutResolving(rootPath).resolve();
|
return loadWithoutResolving(rootPath).resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ConfigRoot load(String rootPath,
|
||||||
|
ConfigParseOptions parseOptions, ConfigResolveOptions resolveOptions) {
|
||||||
|
return loadWithoutResolving(rootPath, parseOptions).resolve(
|
||||||
|
resolveOptions);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like load() but does not resolve the object, so you can go ahead and add
|
* Like load() but does not resolve the object, so you can go ahead and add
|
||||||
* more fallbacks and stuff and have them seen by substitutions when you do
|
* more fallbacks and stuff and have them seen by substitutions when you do
|
||||||
@ -61,11 +67,15 @@ public final class Config {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static ConfigRoot loadWithoutResolving(String rootPath) {
|
public static ConfigRoot loadWithoutResolving(String rootPath) {
|
||||||
|
return loadWithoutResolving(rootPath, ConfigParseOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigRoot loadWithoutResolving(String rootPath,
|
||||||
|
ConfigParseOptions options) {
|
||||||
ConfigRoot system = systemPropertiesRoot(rootPath);
|
ConfigRoot system = systemPropertiesRoot(rootPath);
|
||||||
|
|
||||||
ConfigValue mainFiles = parse(rootPath, ConfigParseOptions.defaults());
|
ConfigValue mainFiles = parse(rootPath, options);
|
||||||
ConfigValue referenceFiles = parse(rootPath + ".reference",
|
ConfigValue referenceFiles = parse(rootPath + ".reference", options);
|
||||||
ConfigParseOptions.defaults());
|
|
||||||
|
|
||||||
return system.withFallbacks(mainFiles, referenceFiles);
|
return system.withFallbacks(mainFiles, referenceFiles);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ public class ConfigImpl {
|
|||||||
if (name.endsWith(".conf") || name.endsWith(".json")
|
if (name.endsWith(".conf") || name.endsWith(".json")
|
||||||
|| name.endsWith(".properties")) {
|
|| name.endsWith(".properties")) {
|
||||||
ConfigParseable p = source.nameToParseable(name);
|
ConfigParseable p = source.nameToParseable(name);
|
||||||
|
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
obj = p.parse(p.options().setAllowMissing(
|
obj = p.parse(p.options().setAllowMissing(
|
||||||
options.getAllowMissing()));
|
options.getAllowMissing()));
|
||||||
@ -47,17 +48,30 @@ public class ConfigImpl {
|
|||||||
"No config files {.conf,.json,.properties} found");
|
"No config files {.conf,.json,.properties} found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigSyntax syntax = options.getSyntax();
|
||||||
|
|
||||||
obj = SimpleConfigObject.empty(new SimpleConfigOrigin(name));
|
obj = SimpleConfigObject.empty(new SimpleConfigOrigin(name));
|
||||||
if (confHandle != null)
|
if (confHandle != null
|
||||||
|
&& (syntax == null || syntax == ConfigSyntax.CONF)) {
|
||||||
obj = confHandle.parse(confHandle.options()
|
obj = confHandle.parse(confHandle.options()
|
||||||
.setAllowMissing(true).setSyntax(ConfigSyntax.CONF));
|
.setAllowMissing(true).setSyntax(ConfigSyntax.CONF));
|
||||||
if (jsonHandle != null)
|
}
|
||||||
obj = obj.withFallback(jsonHandle.parse(jsonHandle.options()
|
|
||||||
.setAllowMissing(true).setSyntax(ConfigSyntax.JSON)));
|
if (jsonHandle != null
|
||||||
if (propsHandle != null)
|
&& (syntax == null || syntax == ConfigSyntax.JSON)) {
|
||||||
obj = obj.withFallback(propsHandle.parse(propsHandle.options()
|
ConfigObject parsed = jsonHandle.parse(jsonHandle
|
||||||
|
.options().setAllowMissing(true)
|
||||||
|
.setSyntax(ConfigSyntax.JSON));
|
||||||
|
obj = obj.withFallback(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propsHandle != null
|
||||||
|
&& (syntax == null || syntax == ConfigSyntax.PROPERTIES)) {
|
||||||
|
ConfigObject parsed = propsHandle.parse(propsHandle.options()
|
||||||
.setAllowMissing(true)
|
.setAllowMissing(true)
|
||||||
.setSyntax(ConfigSyntax.PROPERTIES)));
|
.setSyntax(ConfigSyntax.PROPERTIES));
|
||||||
|
obj = obj.withFallback(parsed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -200,39 +200,28 @@ public abstract class Parseable implements ConfigParseable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static URL urlParent(URL url) {
|
|
||||||
String path = url.getPath();
|
|
||||||
if (path == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
File f = new File(path);
|
|
||||||
|
|
||||||
String parent = f.getParent();
|
|
||||||
|
|
||||||
try {
|
|
||||||
return new URL(url.getProtocol(), url.getHost(), url.getPort(),
|
|
||||||
parent);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static URL relativeTo(URL url, String filename) {
|
static URL relativeTo(URL url, String filename) {
|
||||||
// I'm guessing this completely fails on Windows, help wanted
|
// I'm guessing this completely fails on Windows, help wanted
|
||||||
if (new File(filename).isAbsolute())
|
if (new File(filename).isAbsolute())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
URL parentURL = urlParent(url);
|
|
||||||
if (parentURL == null)
|
|
||||||
return null;
|
|
||||||
try {
|
try {
|
||||||
URI parent = parentURL.toURI();
|
URI siblingURI = url.toURI();
|
||||||
URI relative = new URI(null, null, "/" + filename, null);
|
URI relative = new URI(filename);
|
||||||
return parent.relativize(relative).toURL();
|
|
||||||
|
// this seems wrong, but it's documented that the last
|
||||||
|
// element of the path in siblingURI gets stripped out,
|
||||||
|
// so to get something in the same directory as
|
||||||
|
// siblingURI we just call resolve().
|
||||||
|
URL resolved = siblingURI.resolve(relative).toURL();
|
||||||
|
|
||||||
|
return resolved;
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
return null;
|
return null;
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
return null;
|
return null;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +324,10 @@ public abstract class Parseable implements ConfigParseable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
ConfigParseable relativeTo(String filename) {
|
ConfigParseable relativeTo(String filename) {
|
||||||
return newURL(relativeTo(input, filename), options()
|
URL url = relativeTo(input, filename);
|
||||||
|
if (url == null)
|
||||||
|
return null;
|
||||||
|
return newURL(url, options()
|
||||||
.setOriginDescription(null));
|
.setOriginDescription(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,8 +374,10 @@ public abstract class Parseable implements ConfigParseable {
|
|||||||
@Override
|
@Override
|
||||||
ConfigParseable relativeTo(String filename) {
|
ConfigParseable relativeTo(String filename) {
|
||||||
try {
|
try {
|
||||||
return newURL(relativeTo(input.toURI().toURL(), filename),
|
URL url = relativeTo(input.toURI().toURL(), filename);
|
||||||
options().setOriginDescription(null));
|
if (url == null)
|
||||||
|
return null;
|
||||||
|
return newURL(url, options().setOriginDescription(null));
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
5
src/test/resources/equiv03/includes.conf
Normal file
5
src/test/resources/equiv03/includes.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
letters {
|
||||||
|
include "letters/a.conf"
|
||||||
|
include "letters/b.json"
|
||||||
|
include "letters/c"
|
||||||
|
}
|
6
src/test/resources/equiv03/letters/a.conf
Normal file
6
src/test/resources/equiv03/letters/a.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
numbers {
|
||||||
|
include "numbers/1.conf"
|
||||||
|
include "numbers/2"
|
||||||
|
}
|
||||||
|
|
||||||
|
a=ok
|
3
src/test/resources/equiv03/letters/b.json
Normal file
3
src/test/resources/equiv03/letters/b.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"b" : 507
|
||||||
|
}
|
2
src/test/resources/equiv03/letters/c.conf
Normal file
2
src/test/resources/equiv03/letters/c.conf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
c.fromConf=89
|
||||||
|
|
1
src/test/resources/equiv03/letters/c.properties
Normal file
1
src/test/resources/equiv03/letters/c.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
c.fromProp=true
|
1
src/test/resources/equiv03/letters/numbers/1.conf
Normal file
1
src/test/resources/equiv03/letters/numbers/1.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
"1"=1
|
1
src/test/resources/equiv03/letters/numbers/2.properties
Normal file
1
src/test/resources/equiv03/letters/numbers/2.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
2=abcd
|
14
src/test/resources/equiv03/original.json
Normal file
14
src/test/resources/equiv03/original.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"letters" : {
|
||||||
|
"numbers" : {
|
||||||
|
"2" : "abcd",
|
||||||
|
"1" : 1
|
||||||
|
},
|
||||||
|
"a" : "ok",
|
||||||
|
"b" : 507,
|
||||||
|
"c" : {
|
||||||
|
"fromConf" : 89,
|
||||||
|
"fromProp" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,5 +13,13 @@
|
|||||||
|
|
||||||
"equiv01" : {
|
"equiv01" : {
|
||||||
include "equiv01/original.json"
|
include "equiv01/original.json"
|
||||||
|
},
|
||||||
|
|
||||||
|
# missing includes are supposed to be silently ignored
|
||||||
|
nonexistent {
|
||||||
|
include "nothere"
|
||||||
|
include "nothere.conf"
|
||||||
|
include "nothere.json"
|
||||||
|
include "nothere.properties"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,8 @@ class EquivalentsTest extends TestUtils {
|
|||||||
|
|
||||||
// This is a little "checksum" to be sure we really tested what we were expecting.
|
// This is a little "checksum" to be sure we really tested what we were expecting.
|
||||||
// it breaks every time you add a file, so you have to update it.
|
// it breaks every time you add a file, so you have to update it.
|
||||||
assertEquals(2, dirCount)
|
assertEquals(3, dirCount)
|
||||||
// this is the number of files not named original.*
|
// this is the number of files not named original.*
|
||||||
assertEquals(12, fileCount)
|
assertEquals(13, fileCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
74
src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
Normal file
74
src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.typesafe.config.impl
|
||||||
|
|
||||||
|
import org.junit.Assert._
|
||||||
|
import org.junit._
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
import com.typesafe.config._
|
||||||
|
|
||||||
|
class PublicApiTest extends TestUtils {
|
||||||
|
@Test
|
||||||
|
def basicLoadAndGet() {
|
||||||
|
val conf = Config.load("test01")
|
||||||
|
|
||||||
|
val a = conf.getInt("ints.fortyTwo")
|
||||||
|
val obj = conf.getObject("ints")
|
||||||
|
val c = obj.getInt("fortyTwo")
|
||||||
|
val ms = conf.getMilliseconds("durations.halfSecond")
|
||||||
|
|
||||||
|
// should have used system variables
|
||||||
|
if (System.getenv("HOME") != null)
|
||||||
|
assertEquals(System.getenv("HOME"), conf.getString("system.home"))
|
||||||
|
|
||||||
|
assertEquals(System.getProperty("java.version"), conf.getString("system.javaversion"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def noSystemVariables() {
|
||||||
|
// should not have used system variables
|
||||||
|
val conf = Config.load("test01", ConfigParseOptions.defaults(),
|
||||||
|
ConfigResolveOptions.noSystem())
|
||||||
|
|
||||||
|
intercept[ConfigException.Null] {
|
||||||
|
conf.getString("system.home")
|
||||||
|
}
|
||||||
|
intercept[ConfigException.Null] {
|
||||||
|
conf.getString("system.javaversion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def canLimitLoadToJson {
|
||||||
|
val options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON);
|
||||||
|
val conf = Config.load("test01", options, ConfigResolveOptions.defaults())
|
||||||
|
|
||||||
|
assertEquals(1, conf.getInt("fromJson1"))
|
||||||
|
intercept[ConfigException.Missing] {
|
||||||
|
conf.getInt("ints.fortyTwo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def canLimitLoadToProperties {
|
||||||
|
val options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES);
|
||||||
|
val conf = Config.load("test01", options, ConfigResolveOptions.defaults())
|
||||||
|
|
||||||
|
assertEquals(1, conf.getInt("fromProps.one"))
|
||||||
|
intercept[ConfigException.Missing] {
|
||||||
|
conf.getInt("ints.fortyTwo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def canLimitLoadToConf {
|
||||||
|
val options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF);
|
||||||
|
val conf = Config.load("test01", options, ConfigResolveOptions.defaults())
|
||||||
|
|
||||||
|
assertEquals(42, conf.getInt("ints.fortyTwo"))
|
||||||
|
intercept[ConfigException.Missing] {
|
||||||
|
conf.getInt("fromJson1")
|
||||||
|
}
|
||||||
|
intercept[ConfigException.Missing] {
|
||||||
|
conf.getInt("fromProps.one")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user