mirror of
https://github.com/lightbend/config.git
synced 2025-01-15 23:01:05 +08:00
when loading resource, keep ClassLoader not class and pre-canonicalize resource name
This makes the resource in the ConfigOrigin nice and predictable. It also lets us use the full ClassLoader API instead of only the Class methods that have the special resource name handling.
This commit is contained in:
parent
7e22880cd0
commit
a7d6c23a9e
@ -385,19 +385,19 @@ public abstract class Parseable implements ConfigParseable {
|
||||
}
|
||||
|
||||
private final static class ParseableResource extends Parseable {
|
||||
final private Class<?> klass;
|
||||
final private ClassLoader loader;
|
||||
final private String resource;
|
||||
|
||||
ParseableResource(Class<?> klass, String resource,
|
||||
ParseableResource(ClassLoader loader, String resource,
|
||||
ConfigParseOptions options) {
|
||||
this.klass = klass;
|
||||
this.loader = loader;
|
||||
this.resource = resource;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Reader reader() throws IOException {
|
||||
InputStream stream = klass.getResourceAsStream(resource);
|
||||
InputStream stream = loader.getResourceAsStream(resource);
|
||||
if (stream == null) {
|
||||
throw new IOException("resource not found on classpath: "
|
||||
+ resource);
|
||||
@ -410,24 +410,28 @@ public abstract class Parseable implements ConfigParseable {
|
||||
return syntaxFromExtension(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
ConfigParseable relativeTo(String filename) {
|
||||
// not using File.isAbsolute because resource paths always use '/'
|
||||
// on all platforms
|
||||
if (filename.startsWith("/"))
|
||||
static String parent(String resource) {
|
||||
int i = resource.lastIndexOf('/');
|
||||
if (i < 0) {
|
||||
return null;
|
||||
} else {
|
||||
return resource.substring(0, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
ConfigParseable relativeTo(String sibling) {
|
||||
// here we want to build a new resource name and let
|
||||
// the class loader have it, rather than getting the
|
||||
// url with getResource() and relativizing to that url.
|
||||
// This is needed in case the class loader is going to
|
||||
// search a classpath.
|
||||
File parent = new File(resource).getParentFile();
|
||||
String parent = parent(resource);
|
||||
if (parent == null)
|
||||
return newResource(klass, filename, options()
|
||||
return newResource(loader, sibling, options()
|
||||
.setOriginDescription(null));
|
||||
else
|
||||
return newResource(klass, new File(parent, filename).getPath(),
|
||||
return newResource(loader, parent + "/" + sibling,
|
||||
options().setOriginDescription(null));
|
||||
}
|
||||
|
||||
@ -438,20 +442,49 @@ public abstract class Parseable implements ConfigParseable {
|
||||
|
||||
@Override
|
||||
public URL url() {
|
||||
return klass.getResource(resource);
|
||||
return loader.getResource(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + resource + ","
|
||||
+ klass.getName()
|
||||
+ ")";
|
||||
+ loader.getClass().getSimpleName() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newResource(Class<?> klass, String resource,
|
||||
ConfigParseOptions options) {
|
||||
return new ParseableResource(klass, resource, options);
|
||||
return newResource(klass.getClassLoader(), convertResourceName(klass, resource), options);
|
||||
}
|
||||
|
||||
// this function is supposed to emulate the difference
|
||||
// between Class.getResource and ClassLoader.getResource
|
||||
// (unfortunately there doesn't seem to be public API for it).
|
||||
// We're using it because the Class API is more limited,
|
||||
// for example it lacks getResources(). So we want to be able to
|
||||
// use ClassLoader directly.
|
||||
private static String convertResourceName(Class<?> klass, String resource) {
|
||||
if (resource.startsWith("/")) {
|
||||
// "absolute" resource, chop the slash
|
||||
return resource.substring(1);
|
||||
} else {
|
||||
String className = klass.getName();
|
||||
int i = className.lastIndexOf('.');
|
||||
if (i < 0) {
|
||||
// no package
|
||||
return resource;
|
||||
} else {
|
||||
// need to be relative to the package
|
||||
String packageName = className.substring(0, i);
|
||||
String packagePath = packageName.replace('.', '/');
|
||||
return packagePath + "/" + resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newResource(ClassLoader loader, String resource,
|
||||
ConfigParseOptions options) {
|
||||
return new ParseableResource(loader, resource, options);
|
||||
}
|
||||
|
||||
private final static class ParseableProperties extends Parseable {
|
||||
|
@ -766,18 +766,18 @@ class ConfigTest extends TestUtils {
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
val o1 = conf.getValue("ints.fortyTwo").origin()
|
||||
assertEquals("/test01.conf: 3", o1.description)
|
||||
assertEquals("/test01.conf", o1.resource)
|
||||
assertEquals("test01.conf: 3", o1.description)
|
||||
assertEquals("test01.conf", o1.resource)
|
||||
assertEquals(3, o1.lineNumber)
|
||||
|
||||
val o2 = conf.getValue("fromJson1").origin()
|
||||
assertEquals("/test01.json: 2", o2.description)
|
||||
assertEquals("/test01.json", o2.resource)
|
||||
assertEquals("test01.json: 2", o2.description)
|
||||
assertEquals("test01.json", o2.resource)
|
||||
assertEquals(2, o2.lineNumber)
|
||||
|
||||
val o3 = conf.getValue("fromProps.bool").origin()
|
||||
assertEquals("/test01.properties", o3.description)
|
||||
assertEquals("/test01.properties", o3.resource)
|
||||
assertEquals("test01.properties", o3.description)
|
||||
assertEquals("test01.properties", o3.resource)
|
||||
// we don't have line numbers for properties files
|
||||
assertEquals(-1, o3.lineNumber)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user