mirror of
https://github.com/lightbend/config.git
synced 2025-01-15 23:01:05 +08:00
attempt to rationalize the loader/includer code
Introduce ConfigIncluder pluggable includer interface. Move all the parse-something logic from Config, Loader, ConfigImpl, Parser into the Parseable abstraction.
This commit is contained in:
parent
9b3efbd2d0
commit
d257c3bbd9
@ -14,6 +14,11 @@ import com.typesafe.config.impl.Parseable;
|
||||
|
||||
/**
|
||||
* This class holds some global static methods for the config package.
|
||||
*
|
||||
* The methods with "load" in the name do some sort of higher-level operation
|
||||
* potentially parsing multiple resources and resolving substitutions, while the
|
||||
* ones with "parse" in the name just create a ConfigValue from a resource and
|
||||
* nothing else.
|
||||
*/
|
||||
public final class Config {
|
||||
|
||||
@ -87,33 +92,24 @@ public final class Config {
|
||||
|
||||
public static ConfigObject parse(Properties properties,
|
||||
ConfigParseOptions options) {
|
||||
return ConfigImpl.parse(properties,
|
||||
options.withFallbackOriginDescription("properties"));
|
||||
return Parseable.newProperties(properties, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(Reader reader, ConfigParseOptions options) {
|
||||
Parseable p = Parseable.newReader(reader);
|
||||
return ConfigImpl.parse(p,
|
||||
options.withFallbackOriginDescription("Reader"));
|
||||
return Parseable.newReader(reader, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(URL url, ConfigParseOptions options) {
|
||||
Parseable p = Parseable.newURL(url);
|
||||
return ConfigImpl.parse(p,
|
||||
options.withFallbackOriginDescription(url.toExternalForm()));
|
||||
return Parseable.newURL(url, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(File file, ConfigParseOptions options) {
|
||||
Parseable p = Parseable.newFile(file);
|
||||
return ConfigImpl.parse(p,
|
||||
options.withFallbackOriginDescription(file.getPath()));
|
||||
return Parseable.newFile(file, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(Class<?> klass, String resource,
|
||||
ConfigParseOptions options) {
|
||||
Parseable p = Parseable.newResource(klass, resource);
|
||||
return ConfigImpl.parse(p,
|
||||
options.withFallbackOriginDescription(resource));
|
||||
return Parseable.newResource(klass, resource, options).parse();
|
||||
}
|
||||
|
||||
/**
|
||||
|
26
src/main/java/com/typesafe/config/ConfigIncludeContext.java
Normal file
26
src/main/java/com/typesafe/config/ConfigIncludeContext.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
|
||||
/**
|
||||
* A ConfigIncludeContext is passed to a ConfigIncluder. This interface is not
|
||||
* intended for apps to implement.
|
||||
*/
|
||||
public interface ConfigIncludeContext {
|
||||
/**
|
||||
* Tries to find a name relative to whatever is doing the including, for
|
||||
* example in the same directory as the file doing the including. Returns
|
||||
* null if it can't meaningfully create a relative name. The returned
|
||||
* parseable may not exist; this function is not required to do any IO, just
|
||||
* compute what the name would be.
|
||||
*
|
||||
* The passed-in filename has to be a complete name (with extension), not
|
||||
* just a basename. (Include statements in config files are allowed to give
|
||||
* just a basename.)
|
||||
*
|
||||
* @param filename
|
||||
* the name to make relative to the resource doing the including
|
||||
* @return parseable item relative to the resource doing the including, or
|
||||
* null
|
||||
*/
|
||||
ConfigParseable relativeTo(String filename);
|
||||
}
|
37
src/main/java/com/typesafe/config/ConfigIncluder.java
Normal file
37
src/main/java/com/typesafe/config/ConfigIncluder.java
Normal file
@ -0,0 +1,37 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
/**
|
||||
* Interface you have to implement to customize "include" statements in config
|
||||
* files.
|
||||
*/
|
||||
public interface ConfigIncluder {
|
||||
/**
|
||||
* Returns a new includer that falls back to the given includer. This is how
|
||||
* you can obtain the default includer; it will be provided as a fallback.
|
||||
* It's up to your includer to chain to it if you want to. You might want to
|
||||
* merge any files found by the fallback includer with any objects you load
|
||||
* yourself.
|
||||
*
|
||||
* It's important to handle the case where you already have the fallback
|
||||
* with a "return this", i.e. this method should not create a new object if
|
||||
* the fallback is the same one you already have. The same fallback may be
|
||||
* added repeatedly.
|
||||
*
|
||||
* @param fallback
|
||||
* @return a new includer
|
||||
*/
|
||||
ConfigIncluder withFallback(ConfigIncluder fallback);
|
||||
|
||||
/**
|
||||
* Parses another item to be included. The returned object typically would
|
||||
* not have substitutions resolved. You can throw a ConfigException here to
|
||||
* abort parsing, or return an empty object, but may not return null.
|
||||
*
|
||||
* @param context
|
||||
* some info about the include context
|
||||
* @param what
|
||||
* the include statement's argument
|
||||
* @return a non-null ConfigObject
|
||||
*/
|
||||
ConfigObject include(ConfigIncludeContext context, String what);
|
||||
}
|
@ -5,16 +5,18 @@ public final class ConfigParseOptions {
|
||||
final ConfigSyntax syntax;
|
||||
final String originDescription;
|
||||
final boolean allowMissing;
|
||||
final ConfigIncluder includer;
|
||||
|
||||
protected ConfigParseOptions(ConfigSyntax syntax, String originDescription,
|
||||
boolean allowMissing) {
|
||||
boolean allowMissing, ConfigIncluder includer) {
|
||||
this.syntax = syntax;
|
||||
this.originDescription = originDescription;
|
||||
this.allowMissing = allowMissing;
|
||||
this.includer = includer;
|
||||
}
|
||||
|
||||
public static ConfigParseOptions defaults() {
|
||||
return new ConfigParseOptions(null, null, true);
|
||||
return new ConfigParseOptions(null, null, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -29,7 +31,7 @@ public final class ConfigParseOptions {
|
||||
return this;
|
||||
else
|
||||
return new ConfigParseOptions(syntax, this.originDescription,
|
||||
this.allowMissing);
|
||||
this.allowMissing, this.includer);
|
||||
}
|
||||
|
||||
public ConfigSyntax getSyntax() {
|
||||
@ -53,7 +55,7 @@ public final class ConfigParseOptions {
|
||||
return this;
|
||||
else
|
||||
return new ConfigParseOptions(this.syntax, originDescription,
|
||||
this.allowMissing);
|
||||
this.allowMissing, this.includer);
|
||||
}
|
||||
|
||||
public String getOriginDescription() {
|
||||
@ -81,10 +83,47 @@ public final class ConfigParseOptions {
|
||||
return this;
|
||||
else
|
||||
return new ConfigParseOptions(this.syntax, this.originDescription,
|
||||
allowMissing);
|
||||
allowMissing, this.includer);
|
||||
}
|
||||
|
||||
public boolean getAllowMissing() {
|
||||
return allowMissing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a ConfigIncluder which customizes how includes are handled.
|
||||
*
|
||||
* @param includer
|
||||
* @return new version of the parse options with different includer
|
||||
*/
|
||||
public ConfigParseOptions setIncluder(ConfigIncluder includer) {
|
||||
if (this.includer == includer)
|
||||
return this;
|
||||
else
|
||||
return new ConfigParseOptions(this.syntax, this.originDescription,
|
||||
this.allowMissing, includer);
|
||||
}
|
||||
|
||||
public ConfigParseOptions prependIncluder(ConfigIncluder includer) {
|
||||
if (this.includer == includer)
|
||||
return this;
|
||||
else if (this.includer != null)
|
||||
return setIncluder(includer.withFallback(this.includer));
|
||||
else
|
||||
return setIncluder(includer);
|
||||
}
|
||||
|
||||
public ConfigParseOptions appendIncluder(ConfigIncluder includer) {
|
||||
if (this.includer == includer)
|
||||
return this;
|
||||
else if (this.includer != null)
|
||||
return setIncluder(this.includer.withFallback(includer));
|
||||
else
|
||||
return setIncluder(includer);
|
||||
}
|
||||
|
||||
public ConfigIncluder getIncluder() {
|
||||
return includer;
|
||||
}
|
||||
|
||||
}
|
||||
|
20
src/main/java/com/typesafe/config/ConfigParseable.java
Normal file
20
src/main/java/com/typesafe/config/ConfigParseable.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/** An opaque handle to something that can be parsed. */
|
||||
public interface ConfigParseable {
|
||||
/**
|
||||
* Parse whatever it is.
|
||||
*
|
||||
* @param options
|
||||
* parse options, should be based on the ones from options()
|
||||
*/
|
||||
ConfigObject parse(ConfigParseOptions options);
|
||||
|
||||
/** Possibly return a URL representing the resource; this may return null. */
|
||||
URL url();
|
||||
|
||||
/** Get the initial options, which can be modified then passed to parse(). */
|
||||
ConfigParseOptions options();
|
||||
}
|
@ -2,38 +2,65 @@ package com.typesafe.config.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigIncludeContext;
|
||||
import com.typesafe.config.ConfigIncluder;
|
||||
import com.typesafe.config.ConfigObject;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigParseOptions;
|
||||
import com.typesafe.config.ConfigParseable;
|
||||
import com.typesafe.config.ConfigRoot;
|
||||
import com.typesafe.config.ConfigSyntax;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
/** This is public but is only supposed to be used by the "config" package */
|
||||
public class ConfigImpl {
|
||||
private static AbstractConfigObject forceParsedToObject(ConfigValue value) {
|
||||
if (value instanceof AbstractConfigObject) {
|
||||
return (AbstractConfigObject) value;
|
||||
|
||||
private interface NameSource {
|
||||
ConfigParseable nameToParseable(String name);
|
||||
}
|
||||
|
||||
// this function is a little tricky because there are two places we're
|
||||
// trying to use it; for 'include "basename"' in a .conf file, and for
|
||||
// loading app.{conf,json,properties} from classpath.
|
||||
private static ConfigObject fromBasename(NameSource source, String name,
|
||||
ConfigParseOptions options) {
|
||||
ConfigObject obj;
|
||||
if (name.endsWith(".conf") || name.endsWith(".json")
|
||||
|| name.endsWith(".properties")) {
|
||||
ConfigParseable p = source.nameToParseable(name);
|
||||
if (p != null) {
|
||||
obj = p.parse(p.options().setAllowMissing(
|
||||
options.getAllowMissing()));
|
||||
} else {
|
||||
obj = SimpleConfigObject.emptyMissing(new SimpleConfigOrigin(
|
||||
name));
|
||||
}
|
||||
} else {
|
||||
throw new ConfigException.WrongType(value.origin(), "",
|
||||
"object at file root", value.valueType().name());
|
||||
ConfigParseable confHandle = source.nameToParseable(name + ".conf");
|
||||
ConfigParseable jsonHandle = source.nameToParseable(name + ".json");
|
||||
ConfigParseable propsHandle = source.nameToParseable(name
|
||||
+ ".properties");
|
||||
|
||||
if (!options.getAllowMissing() && confHandle == null
|
||||
&& jsonHandle == null && propsHandle == null) {
|
||||
throw new ConfigException.IO(new SimpleConfigOrigin(name),
|
||||
"No config files {.conf,.json,.properties} found");
|
||||
}
|
||||
|
||||
obj = SimpleConfigObject.empty(new SimpleConfigOrigin(name));
|
||||
if (confHandle != null)
|
||||
obj = confHandle.parse(confHandle.options()
|
||||
.setAllowMissing(true).setSyntax(ConfigSyntax.CONF));
|
||||
if (jsonHandle != null)
|
||||
obj = obj.withFallback(jsonHandle.parse(jsonHandle.options()
|
||||
.setAllowMissing(true).setSyntax(ConfigSyntax.JSON)));
|
||||
if (propsHandle != null)
|
||||
obj = obj.withFallback(propsHandle.parse(propsHandle.options()
|
||||
.setAllowMissing(true)
|
||||
.setSyntax(ConfigSyntax.PROPERTIES)));
|
||||
}
|
||||
}
|
||||
|
||||
static AbstractConfigValue parseValue(Parseable parseable,
|
||||
ConfigParseOptions options) {
|
||||
ConfigOrigin origin = new SimpleConfigOrigin(
|
||||
options.getOriginDescription());
|
||||
return Parser.parse(parseable, origin, options);
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static ConfigObject parse(Parseable parseable,
|
||||
ConfigParseOptions options) {
|
||||
return forceParsedToObject(parseValue(parseable, options));
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static String makeResourceBasename(Path path) {
|
||||
@ -54,108 +81,19 @@ public class ConfigImpl {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// ConfigParseOptions has a package-private method for this but I don't want
|
||||
// to make it public
|
||||
static ConfigOrigin originWithSuffix(ConfigParseOptions options,
|
||||
String suffix) {
|
||||
return new SimpleConfigOrigin(options.getOriginDescription() + suffix);
|
||||
}
|
||||
|
||||
static String syntaxSuffix(ConfigSyntax syntax) {
|
||||
switch (syntax) {
|
||||
case PROPERTIES:
|
||||
return ".properties";
|
||||
case CONF:
|
||||
return ".conf";
|
||||
case JSON:
|
||||
return ".json";
|
||||
}
|
||||
throw new ConfigException.BugOrBroken("not a valid ConfigSyntax: "
|
||||
+ syntax);
|
||||
}
|
||||
|
||||
static AbstractConfigObject loadForResource(Class<?> loadClass,
|
||||
String basename, ConfigSyntax syntax, ConfigParseOptions options) {
|
||||
String suffix = syntaxSuffix(syntax);
|
||||
String resource = basename + suffix;
|
||||
|
||||
// we want null rather than empty object if missingness is allowed,
|
||||
// so we can handle it.
|
||||
if (options.getAllowMissing()
|
||||
&& loadClass.getResource(resource) == null) {
|
||||
return null;
|
||||
} else {
|
||||
return forceParsedToObject(Parser.parse(
|
||||
Parseable.newResource(loadClass, resource),
|
||||
originWithSuffix(options, suffix),
|
||||
options.setSyntax(syntax)));
|
||||
}
|
||||
}
|
||||
|
||||
static AbstractConfigObject checkAllowMissing(AbstractConfigObject obj,
|
||||
ConfigOrigin origin, ConfigParseOptions options) {
|
||||
if (obj == null) {
|
||||
if (options.getAllowMissing()) {
|
||||
return SimpleConfigObject.emptyMissing(origin);
|
||||
} else {
|
||||
throw new ConfigException.IO(origin,
|
||||
"Resource not found on classpath");
|
||||
}
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static ConfigObject parseResourcesForPath(String expression,
|
||||
ConfigParseOptions baseOptions) {
|
||||
final ConfigParseOptions baseOptions) {
|
||||
Path path = Parser.parsePath(expression);
|
||||
String basename = makeResourceBasename(path);
|
||||
|
||||
Class<?> loadClass = ConfigImpl.class;
|
||||
|
||||
ConfigParseOptions options;
|
||||
if (baseOptions.getOriginDescription() != null)
|
||||
options = baseOptions;
|
||||
else
|
||||
options = baseOptions.setOriginDescription(basename);
|
||||
|
||||
if (options.getSyntax() != null) {
|
||||
ConfigSyntax syntax = options.getSyntax();
|
||||
AbstractConfigObject obj = loadForResource(loadClass, basename,
|
||||
syntax, options);
|
||||
return checkAllowMissing(obj, originWithSuffix(options, syntaxSuffix(syntax)), options);
|
||||
} else {
|
||||
// we want to try all three then
|
||||
|
||||
ConfigParseOptions allowMissing = options.setAllowMissing(true);
|
||||
AbstractConfigObject conf = loadForResource(loadClass, basename,
|
||||
ConfigSyntax.CONF, allowMissing);
|
||||
AbstractConfigObject json = loadForResource(loadClass, basename,
|
||||
ConfigSyntax.JSON, allowMissing);
|
||||
AbstractConfigObject props = loadForResource(loadClass, basename,
|
||||
ConfigSyntax.PROPERTIES, allowMissing);
|
||||
|
||||
ConfigOrigin baseOrigin = new SimpleConfigOrigin(options
|
||||
.getOriginDescription());
|
||||
|
||||
if (!options.getAllowMissing() && conf == null && json == null && props == null) {
|
||||
throw new ConfigException.IO(baseOrigin,
|
||||
"No config files {.conf,.json,.properties} found on classpath");
|
||||
NameSource source = new NameSource() {
|
||||
@Override
|
||||
public ConfigParseable nameToParseable(String name) {
|
||||
return Parseable.newResource(ConfigImpl.class, name,
|
||||
baseOptions);
|
||||
}
|
||||
|
||||
AbstractConfigObject merged = SimpleConfigObject
|
||||
.empty(baseOrigin);
|
||||
|
||||
if (conf != null)
|
||||
merged = merged.withFallback(conf);
|
||||
if (json != null)
|
||||
merged = merged.withFallback(json);
|
||||
if (props != null)
|
||||
merged = merged.withFallback(props);
|
||||
|
||||
return merged;
|
||||
}
|
||||
};
|
||||
return fromBasename(source, basename, baseOptions);
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
@ -179,13 +117,6 @@ public class ConfigImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static ConfigObject parse(Properties properties,
|
||||
ConfigParseOptions options) {
|
||||
return Loader
|
||||
.fromProperties(options.getOriginDescription(), properties);
|
||||
}
|
||||
|
||||
private static ConfigTransformer defaultTransformer = null;
|
||||
|
||||
synchronized static ConfigTransformer defaultConfigTransformer() {
|
||||
@ -195,17 +126,56 @@ public class ConfigImpl {
|
||||
return defaultTransformer;
|
||||
}
|
||||
|
||||
private static IncludeHandler defaultIncluder = null;
|
||||
private static class SimpleIncluder implements ConfigIncluder {
|
||||
|
||||
synchronized static IncludeHandler defaultIncluder() {
|
||||
if (defaultIncluder == null) {
|
||||
defaultIncluder = new IncludeHandler() {
|
||||
private ConfigIncluder fallback;
|
||||
|
||||
SimpleIncluder(ConfigIncluder fallback) {
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigObject include(final ConfigIncludeContext context,
|
||||
String name) {
|
||||
NameSource source = new NameSource() {
|
||||
@Override
|
||||
public AbstractConfigObject include(String name) {
|
||||
return Loader.load(name, this);
|
||||
public ConfigParseable nameToParseable(String name) {
|
||||
return context.relativeTo(name);
|
||||
}
|
||||
};
|
||||
|
||||
ConfigObject obj = fromBasename(source, name, ConfigParseOptions
|
||||
.defaults().setAllowMissing(true));
|
||||
|
||||
// now use the fallback includer if any and merge
|
||||
// its result.
|
||||
if (fallback != null) {
|
||||
return obj.withFallback(fallback.include(context, name));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigIncluder withFallback(ConfigIncluder fallback) {
|
||||
if (this == fallback) {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"trying to create includer cycle");
|
||||
} else if (this.fallback == fallback) {
|
||||
return this;
|
||||
} else if (this.fallback != null) {
|
||||
return new SimpleIncluder(this.fallback.withFallback(fallback));
|
||||
} else {
|
||||
return new SimpleIncluder(fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ConfigIncluder defaultIncluder = null;
|
||||
|
||||
synchronized static ConfigIncluder defaultIncluder() {
|
||||
if (defaultIncluder == null) {
|
||||
defaultIncluder = new SimpleIncluder(null);
|
||||
}
|
||||
return defaultIncluder;
|
||||
}
|
||||
@ -221,7 +191,10 @@ public class ConfigImpl {
|
||||
}
|
||||
|
||||
private static AbstractConfigObject loadSystemProperties() {
|
||||
return Loader.fromProperties("system property", System.getProperties());
|
||||
return (AbstractConfigObject) Parseable.newProperties(
|
||||
System.getProperties(),
|
||||
ConfigParseOptions.defaults().setOriginDescription(
|
||||
"system properties")).parse();
|
||||
}
|
||||
|
||||
// this is a hack to let us set system props in the test suite
|
||||
|
@ -1,11 +0,0 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
/**
|
||||
* This is sort of a placeholder so that something per-config-load is passed in
|
||||
* to the parser to handle includes. The eventual idea is to let apps customize
|
||||
* how an included name gets searched for, which would involve some nicer
|
||||
* interface in the public API.
|
||||
*/
|
||||
interface IncludeHandler {
|
||||
AbstractConfigObject include(String name);
|
||||
}
|
@ -10,19 +10,64 @@ import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigIncludeContext;
|
||||
import com.typesafe.config.ConfigObject;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigParseOptions;
|
||||
import com.typesafe.config.ConfigParseable;
|
||||
import com.typesafe.config.ConfigSyntax;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
/**
|
||||
* This is public but it's only for use by the config package; DO NOT TOUCH. The
|
||||
* point of this class is to avoid "propagating" each overload on
|
||||
* "thing which can be parsed" through multiple interfaces. Most interfaces can
|
||||
* have just one overload that takes a Parseable.
|
||||
* have just one overload that takes a Parseable. Also it's used as an abstract
|
||||
* "resource handle" in the ConfigIncluder interface.
|
||||
*/
|
||||
public abstract class Parseable {
|
||||
public abstract class Parseable implements ConfigParseable {
|
||||
private ConfigIncludeContext includeContext;
|
||||
private ConfigParseOptions options;
|
||||
|
||||
protected Parseable() {
|
||||
|
||||
}
|
||||
|
||||
private ConfigParseOptions fixupOptions(ConfigParseOptions baseOptions) {
|
||||
ConfigSyntax syntax = baseOptions.getSyntax();
|
||||
if (syntax == null) {
|
||||
syntax = guessSyntax();
|
||||
}
|
||||
if (syntax == null) {
|
||||
syntax = ConfigSyntax.CONF;
|
||||
}
|
||||
ConfigParseOptions modified = baseOptions.setSyntax(syntax);
|
||||
|
||||
if (modified.getOriginDescription() == null)
|
||||
modified = modified.setOriginDescription(originDescription());
|
||||
|
||||
modified = modified.appendIncluder(ConfigImpl.defaultIncluder());
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
protected void postConstruct(ConfigParseOptions baseOptions) {
|
||||
this.options = fixupOptions(baseOptions);
|
||||
|
||||
this.includeContext = new ConfigIncludeContext() {
|
||||
@Override
|
||||
public ConfigParseable relativeTo(String filename) {
|
||||
return Parseable.this.relativeTo(filename);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// the general idea is that any work should be in here, not in the
|
||||
@ -32,12 +77,90 @@ public abstract class Parseable {
|
||||
// The parser should close the reader when it's done with it.
|
||||
// ALSO, IMPORTANT: if the file or URL is not found, this must throw.
|
||||
// to support the "allow missing" feature.
|
||||
abstract Reader reader() throws IOException;
|
||||
protected abstract Reader reader() throws IOException;
|
||||
|
||||
ConfigSyntax guessSyntax() {
|
||||
return null;
|
||||
}
|
||||
|
||||
ConfigParseable relativeTo(String filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ConfigIncludeContext includeContext() {
|
||||
return includeContext;
|
||||
}
|
||||
|
||||
static AbstractConfigObject forceParsedToObject(ConfigValue value) {
|
||||
if (value instanceof AbstractConfigObject) {
|
||||
return (AbstractConfigObject) value;
|
||||
} else {
|
||||
throw new ConfigException.WrongType(value.origin(), "",
|
||||
"object at file root", value.valueType().name());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigObject parse(ConfigParseOptions baseOptions) {
|
||||
return forceParsedToObject(parseValue(baseOptions));
|
||||
}
|
||||
|
||||
AbstractConfigValue parseValue(ConfigParseOptions baseOptions) {
|
||||
// note that we are NOT using our "options" and "origin" fields,
|
||||
// but using the ones from the passed-in options. The idea is that
|
||||
// callers can get our original options and then parse with different
|
||||
// ones if they want.
|
||||
ConfigParseOptions options = fixupOptions(baseOptions);
|
||||
ConfigOrigin origin = new SimpleConfigOrigin(
|
||||
options.getOriginDescription());
|
||||
return parseValue(origin, options);
|
||||
}
|
||||
|
||||
protected AbstractConfigValue parseValue(ConfigOrigin origin,
|
||||
ConfigParseOptions finalOptions) {
|
||||
try {
|
||||
Reader reader = reader();
|
||||
try {
|
||||
if (options.getSyntax() == ConfigSyntax.PROPERTIES) {
|
||||
return PropertiesParser.parse(reader, origin);
|
||||
} else {
|
||||
Iterator<Token> tokens = Tokenizer.tokenize(origin, reader,
|
||||
options.getSyntax());
|
||||
return Parser.parse(tokens, origin, options,
|
||||
includeContext());
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (options.getAllowMissing()) {
|
||||
return SimpleConfigObject.emptyMissing(origin);
|
||||
} else {
|
||||
throw new ConfigException.IO(origin, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigObject parse() {
|
||||
return forceParsedToObject(parseValue(options()));
|
||||
}
|
||||
|
||||
AbstractConfigValue parseValue() {
|
||||
return parseValue(options());
|
||||
}
|
||||
|
||||
abstract String originDescription();
|
||||
|
||||
@Override
|
||||
public URL url() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigParseOptions options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName();
|
||||
@ -77,74 +200,130 @@ public abstract class Parseable {
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
// I'm guessing this completely fails on Windows, help wanted
|
||||
if (new File(filename).isAbsolute())
|
||||
return null;
|
||||
|
||||
URL parentURL = urlParent(url);
|
||||
if (parentURL == null)
|
||||
return null;
|
||||
try {
|
||||
URI parent = parentURL.toURI();
|
||||
URI relative = new URI(null, null, "/" + filename, null);
|
||||
return parent.relativize(relative).toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
} catch (URISyntaxException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final static class ParseableInputStream extends Parseable {
|
||||
final private InputStream input;
|
||||
|
||||
ParseableInputStream(InputStream input) {
|
||||
ParseableInputStream(InputStream input, ConfigParseOptions options) {
|
||||
this.input = input;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
Reader reader() {
|
||||
protected Reader reader() {
|
||||
return doNotClose(readerFromStream(input));
|
||||
}
|
||||
|
||||
@Override
|
||||
String originDescription() {
|
||||
return "InputStream";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* note that we will never close this stream; you have to do it when parsing
|
||||
* is complete.
|
||||
*/
|
||||
public static Parseable newInputStream(InputStream input) {
|
||||
return new ParseableInputStream(input);
|
||||
public static Parseable newInputStream(InputStream input,
|
||||
ConfigParseOptions options) {
|
||||
return new ParseableInputStream(input, options);
|
||||
}
|
||||
|
||||
private final static class ParseableReader extends Parseable {
|
||||
final private Reader reader;
|
||||
|
||||
ParseableReader(Reader reader) {
|
||||
ParseableReader(Reader reader, ConfigParseOptions options) {
|
||||
this.reader = reader;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
Reader reader() {
|
||||
protected Reader reader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
String originDescription() {
|
||||
return "Reader";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* note that we will never close this reader; you have to do it when parsing
|
||||
* is complete.
|
||||
*/
|
||||
public static Parseable newReader(Reader reader) {
|
||||
return new ParseableReader(doNotClose(reader));
|
||||
public static Parseable newReader(Reader reader, ConfigParseOptions options) {
|
||||
return new ParseableReader(doNotClose(reader), options);
|
||||
}
|
||||
|
||||
private final static class ParseableString extends Parseable {
|
||||
final private String input;
|
||||
|
||||
ParseableString(String input) {
|
||||
ParseableString(String input, ConfigParseOptions options) {
|
||||
this.input = input;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
Reader reader() {
|
||||
protected Reader reader() {
|
||||
return new StringReader(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
String originDescription() {
|
||||
return "String";
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newString(String input) {
|
||||
return new ParseableString(input);
|
||||
public static Parseable newString(String input, ConfigParseOptions options) {
|
||||
return new ParseableString(input, options);
|
||||
}
|
||||
|
||||
private final static class ParseableURL extends Parseable {
|
||||
final private URL input;
|
||||
|
||||
ParseableURL(URL input) {
|
||||
ParseableURL(URL input, ConfigParseOptions options) {
|
||||
this.input = input;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
Reader reader() throws IOException {
|
||||
protected Reader reader() throws IOException {
|
||||
InputStream stream = input.openStream();
|
||||
return readerFromStream(stream);
|
||||
}
|
||||
@ -154,6 +333,22 @@ public abstract class Parseable {
|
||||
return syntaxFromExtension(input.getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
ConfigParseable relativeTo(String filename) {
|
||||
return newURL(relativeTo(input, filename), options()
|
||||
.setOriginDescription(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
String originDescription() {
|
||||
return input.toExternalForm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL url() {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + input.toExternalForm()
|
||||
@ -161,19 +356,20 @@ public abstract class Parseable {
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newURL(URL input) {
|
||||
return new ParseableURL(input);
|
||||
public static Parseable newURL(URL input, ConfigParseOptions options) {
|
||||
return new ParseableURL(input, options);
|
||||
}
|
||||
|
||||
private final static class ParseableFile extends Parseable {
|
||||
final private File input;
|
||||
|
||||
ParseableFile(File input) {
|
||||
ParseableFile(File input, ConfigParseOptions options) {
|
||||
this.input = input;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
Reader reader() throws IOException {
|
||||
protected Reader reader() throws IOException {
|
||||
InputStream stream = new FileInputStream(input);
|
||||
return readerFromStream(stream);
|
||||
}
|
||||
@ -183,28 +379,58 @@ public abstract class Parseable {
|
||||
return syntaxFromExtension(input.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
ConfigParseable relativeTo(String filename) {
|
||||
try {
|
||||
return newURL(relativeTo(input.toURI().toURL(), filename),
|
||||
options().setOriginDescription(null));
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String originDescription() {
|
||||
return input.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL url() {
|
||||
try {
|
||||
return input.toURI().toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + input.getPath() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newFile(File input) {
|
||||
return new ParseableFile(input);
|
||||
public static Parseable newFile(File input, ConfigParseOptions options) {
|
||||
return new ParseableFile(input, options);
|
||||
}
|
||||
|
||||
private final static class ParseableResource extends Parseable {
|
||||
final private Class<?> klass;
|
||||
final private String resource;
|
||||
|
||||
ParseableResource(Class<?> klass, String resource) {
|
||||
ParseableResource(Class<?> klass, String resource,
|
||||
ConfigParseOptions options) {
|
||||
this.klass = klass;
|
||||
this.resource = resource;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
Reader reader() throws IOException {
|
||||
protected Reader reader() throws IOException {
|
||||
InputStream stream = klass.getResourceAsStream(resource);
|
||||
if (stream == null) {
|
||||
throw new IOException("resource not found on classpath: "
|
||||
+ resource);
|
||||
}
|
||||
return readerFromStream(stream);
|
||||
}
|
||||
|
||||
@ -213,6 +439,37 @@ public abstract class Parseable {
|
||||
return syntaxFromExtension(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
ConfigParseable relativeTo(String filename) {
|
||||
// not using File.isAbsolute because resource paths always use '/'
|
||||
// (?)
|
||||
if (filename.startsWith("/"))
|
||||
return null;
|
||||
|
||||
// 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();
|
||||
if (parent == null)
|
||||
return newResource(klass, "/" + filename, options()
|
||||
.setOriginDescription(null));
|
||||
else
|
||||
return newResource(klass, new File(parent, filename).getPath(),
|
||||
options().setOriginDescription(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
String originDescription() {
|
||||
return resource + " on classpath";
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL url() {
|
||||
return klass.getResource(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + resource + ","
|
||||
@ -221,7 +478,54 @@ public abstract class Parseable {
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newResource(Class<?> klass, String resource) {
|
||||
return new ParseableResource(klass, resource);
|
||||
public static Parseable newResource(Class<?> klass, String resource,
|
||||
ConfigParseOptions options) {
|
||||
return new ParseableResource(klass, resource, options);
|
||||
}
|
||||
|
||||
private final static class ParseableProperties extends Parseable {
|
||||
final private Properties props;
|
||||
|
||||
ParseableProperties(Properties props, ConfigParseOptions options) {
|
||||
this.props = props;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Reader reader() throws IOException {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"reader() should not be called on props");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigObject parseValue(ConfigOrigin origin,
|
||||
ConfigParseOptions finalOptions) {
|
||||
return PropertiesParser.fromProperties(origin, props);
|
||||
}
|
||||
|
||||
@Override
|
||||
ConfigSyntax guessSyntax() {
|
||||
return ConfigSyntax.PROPERTIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
String originDescription() {
|
||||
return "properties";
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL url() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + props.size() + " props)";
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newProperties(Properties properties,
|
||||
ConfigParseOptions options) {
|
||||
return new ParseableProperties(properties, options);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -14,6 +12,8 @@ import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigIncludeContext;
|
||||
import com.typesafe.config.ConfigIncluder;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigParseOptions;
|
||||
import com.typesafe.config.ConfigSyntax;
|
||||
@ -21,45 +21,11 @@ import com.typesafe.config.ConfigValueType;
|
||||
|
||||
final class Parser {
|
||||
|
||||
static AbstractConfigValue parse(Parseable input, ConfigOrigin origin,
|
||||
ConfigParseOptions options) {
|
||||
return parse(input, origin, options, ConfigImpl.defaultIncluder());
|
||||
}
|
||||
|
||||
static AbstractConfigValue parse(Parseable input, ConfigOrigin origin,
|
||||
ConfigParseOptions baseOptions, IncludeHandler includer) {
|
||||
ConfigSyntax syntax = baseOptions.getSyntax();
|
||||
if (syntax == null) {
|
||||
syntax = input.guessSyntax();
|
||||
}
|
||||
if (syntax == null) {
|
||||
syntax = ConfigSyntax.CONF;
|
||||
}
|
||||
ConfigParseOptions options = baseOptions.setSyntax(syntax);
|
||||
|
||||
try {
|
||||
Reader reader = input.reader();
|
||||
try {
|
||||
Iterator<Token> tokens = Tokenizer.tokenize(origin, reader,
|
||||
syntax);
|
||||
return parse(tokens, origin, options, includer);
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (options.getAllowMissing()) {
|
||||
return SimpleConfigObject.emptyMissing(origin);
|
||||
} else {
|
||||
throw new ConfigException.IO(origin, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static AbstractConfigValue parse(Iterator<Token> tokens,
|
||||
static AbstractConfigValue parse(Iterator<Token> tokens,
|
||||
ConfigOrigin origin, ConfigParseOptions options,
|
||||
IncludeHandler includer) {
|
||||
ConfigIncludeContext includeContext) {
|
||||
ParseContext context = new ParseContext(options.getSyntax(), origin,
|
||||
tokens, includer);
|
||||
tokens, options.getIncluder(), includeContext);
|
||||
return context.parse();
|
||||
}
|
||||
|
||||
@ -67,19 +33,22 @@ final class Parser {
|
||||
private int lineNumber;
|
||||
final private Stack<Token> buffer;
|
||||
final private Iterator<Token> tokens;
|
||||
final private IncludeHandler includer;
|
||||
final private ConfigIncluder includer;
|
||||
final private ConfigIncludeContext includeContext;
|
||||
final private ConfigSyntax flavor;
|
||||
final private ConfigOrigin baseOrigin;
|
||||
final private LinkedList<Path> pathStack;
|
||||
|
||||
ParseContext(ConfigSyntax flavor, ConfigOrigin origin,
|
||||
Iterator<Token> tokens, IncludeHandler includer) {
|
||||
Iterator<Token> tokens, ConfigIncluder includer,
|
||||
ConfigIncludeContext includeContext) {
|
||||
lineNumber = 0;
|
||||
buffer = new Stack<Token>();
|
||||
this.tokens = tokens;
|
||||
this.flavor = flavor;
|
||||
this.baseOrigin = origin;
|
||||
this.includer = includer;
|
||||
this.includeContext = includeContext;
|
||||
this.pathStack = new LinkedList<Path>();
|
||||
}
|
||||
|
||||
@ -349,7 +318,8 @@ final class Parser {
|
||||
|
||||
if (Tokens.isValueWithType(t, ConfigValueType.STRING)) {
|
||||
String name = (String) Tokens.getValue(t).unwrapped();
|
||||
AbstractConfigObject obj = includer.include(name);
|
||||
AbstractConfigObject obj = (AbstractConfigObject) includer
|
||||
.include(includeContext, name);
|
||||
|
||||
if (!pathStack.isEmpty()) {
|
||||
Path prefix = new Path(pathStack);
|
||||
|
@ -1,9 +1,7 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
@ -14,78 +12,13 @@ import java.util.Properties;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigParseOptions;
|
||||
|
||||
/**
|
||||
* FIXME this file needs to die; the load() part should use the code in
|
||||
* ConfigImpl instead and the properties stuff should be in its own file.
|
||||
*
|
||||
*/
|
||||
final class Loader {
|
||||
static AbstractConfigObject load(String name, IncludeHandler includer) {
|
||||
List<AbstractConfigObject> stack = new ArrayList<AbstractConfigObject>();
|
||||
|
||||
// if name has an extension, only use that; otherwise merge all three
|
||||
if (name.endsWith(".conf") || name.endsWith(".json")
|
||||
|| name.endsWith(".properties")) {
|
||||
addResource(name, includer, stack);
|
||||
} else {
|
||||
// .conf wins over .json wins over .properties;
|
||||
// arbitrary, but deterministic
|
||||
addResource(name + ".conf", includer, stack);
|
||||
addResource(name + ".json", includer, stack);
|
||||
addResource(name + ".properties", includer, stack);
|
||||
}
|
||||
|
||||
AbstractConfigObject merged = AbstractConfigObject.merge(stack);
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private static void addResource(String name, IncludeHandler includer,
|
||||
List<AbstractConfigObject> stack) {
|
||||
URL url = ConfigImpl.class.getResource("/" + name);
|
||||
if (url != null) {
|
||||
stack.add(loadURL(url, includer));
|
||||
}
|
||||
}
|
||||
|
||||
private static AbstractConfigObject loadURL(URL url, IncludeHandler includer) {
|
||||
if (url.getPath().endsWith(".properties")) {
|
||||
ConfigOrigin origin = new SimpleConfigOrigin(url.toExternalForm());
|
||||
Properties props = new Properties();
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = url.openStream();
|
||||
stream = new BufferedInputStream(stream);
|
||||
props.load(stream);
|
||||
} catch (IOException e) {
|
||||
throw new ConfigException.IO(origin, "failed to open url", e);
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return fromProperties(url.toExternalForm(), props);
|
||||
} else {
|
||||
return forceParsedToObject(Parser.parse(Parseable.newURL(url),
|
||||
new SimpleConfigOrigin(url.toExternalForm()),
|
||||
ConfigParseOptions.defaults(),
|
||||
includer));
|
||||
}
|
||||
}
|
||||
|
||||
private static AbstractConfigObject forceParsedToObject(
|
||||
AbstractConfigValue value) {
|
||||
if (value instanceof AbstractConfigObject) {
|
||||
return (AbstractConfigObject) value;
|
||||
} else {
|
||||
throw new ConfigException.WrongType(value.origin(), "",
|
||||
"object at file root", value.valueType().name());
|
||||
}
|
||||
final class PropertiesParser {
|
||||
static AbstractConfigObject parse(Reader reader,
|
||||
ConfigOrigin origin) throws IOException {
|
||||
Properties props = new Properties();
|
||||
props.load(reader);
|
||||
return fromProperties(origin, props);
|
||||
}
|
||||
|
||||
static void verifyPath(String path) {
|
||||
@ -116,7 +49,7 @@ final class Loader {
|
||||
return path.substring(0, i);
|
||||
}
|
||||
|
||||
static AbstractConfigObject fromProperties(String originPrefix,
|
||||
static AbstractConfigObject fromProperties(ConfigOrigin origin,
|
||||
Properties props) {
|
||||
Map<String, Map<String, AbstractConfigValue>> scopes = new HashMap<String, Map<String, AbstractConfigValue>>();
|
||||
Enumeration<?> i = props.propertyNames();
|
||||
@ -136,8 +69,7 @@ final class Loader {
|
||||
scopes.put(exceptLast, scope);
|
||||
}
|
||||
String value = props.getProperty(path);
|
||||
scope.put(last, new ConfigString(new SimpleConfigOrigin(
|
||||
originPrefix + " " + path), value));
|
||||
scope.put(last, new ConfigString(origin, value));
|
||||
} catch (ConfigException.BadPath e) {
|
||||
// just skip this one (log it?)
|
||||
}
|
||||
@ -169,8 +101,7 @@ final class Loader {
|
||||
// Also we assume here that any info based on the map that
|
||||
// SimpleConfigObject computes and caches in its constructor
|
||||
// will not change. Basically this is a bad hack.
|
||||
AbstractConfigObject o = new SimpleConfigObject(
|
||||
new SimpleConfigOrigin(originPrefix + " " + path),
|
||||
AbstractConfigObject o = new SimpleConfigObject(origin,
|
||||
scopes.get(path), ResolveStatus.RESOLVED);
|
||||
String basename = lastElement(path);
|
||||
parent.put(basename, o);
|
||||
@ -184,7 +115,6 @@ final class Loader {
|
||||
}
|
||||
|
||||
// return root config object
|
||||
return new SimpleConfigObject(new SimpleConfigOrigin(originPrefix),
|
||||
root, ResolveStatus.RESOLVED);
|
||||
return new SimpleConfigObject(origin, root, ResolveStatus.RESOLVED);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ class ConfParserTest extends TestUtils {
|
||||
val options = ConfigParseOptions.defaults().
|
||||
setOriginDescription("test conf string").
|
||||
setSyntax(ConfigSyntax.CONF);
|
||||
ConfigImpl.parseValue(Parseable.newString(s), options);
|
||||
Parseable.newString(s, options).parseValue().asInstanceOf[AbstractConfigValue]
|
||||
}
|
||||
|
||||
def parse(s: String) = {
|
||||
|
@ -15,14 +15,14 @@ class JsonTest extends TestUtils {
|
||||
val options = ConfigParseOptions.defaults().
|
||||
setOriginDescription("test json string").
|
||||
setSyntax(ConfigSyntax.JSON);
|
||||
ConfigImpl.parseValue(Parseable.newString(s), options);
|
||||
Parseable.newString(s, options).parseValue();
|
||||
}
|
||||
|
||||
def parseAsConf(s: String): ConfigValue = {
|
||||
val options = ConfigParseOptions.defaults().
|
||||
setOriginDescription("test conf string").
|
||||
setSyntax(ConfigSyntax.CONF);
|
||||
ConfigImpl.parseValue(Parseable.newString(s), options);
|
||||
Parseable.newString(s, options).parseValue();
|
||||
}
|
||||
|
||||
private[this] def toLift(value: ConfigValue): lift.JValue = {
|
||||
|
Loading…
Reference in New Issue
Block a user