mirror of
https://github.com/lightbend/config.git
synced 2025-01-15 23:01:05 +08:00
Split Config out of ConfigObject.
This separates ConfigObject, which is: - a tree of maps - that can contain ConfigValueType.NULL from Config, which is: - a one-level map from path to non-map values - which never returns a ConfigValueType.NULL
This commit is contained in:
parent
2381cf7785
commit
adb80f6017
@ -1,26 +1,11 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.typesafe.config.impl.ConfigImpl;
|
||||
import com.typesafe.config.impl.ConfigUtil;
|
||||
import com.typesafe.config.impl.Parseable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class represents an immutable map from config paths to config values. It
|
||||
* also contains some static methods for creating configs.
|
||||
*
|
||||
* The static 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.
|
||||
*
|
||||
*
|
||||
* Throughout the API, there is a distinction between "keys" and "paths". A key
|
||||
* is a key in a JSON object; it's just a string that's the key in a map. A
|
||||
* "path" is a parseable expression with a syntax and it refers to a series of
|
||||
@ -29,485 +14,157 @@ import com.typesafe.config.impl.Parseable;
|
||||
* path is period-separated so "a.b.c" looks for key c in object b in object a
|
||||
* in the root object. Sometimes double quotes are needed around special
|
||||
* characters in path expressions.
|
||||
*
|
||||
*
|
||||
* The API for a Config is in terms of path expressions, while the API for a
|
||||
* ConfigObject is in terms of keys. Conceptually, Config is a one-level map
|
||||
* from paths to values, while a ConfigObject is a tree of maps from keys to
|
||||
* values.
|
||||
*
|
||||
*
|
||||
* Another difference between Config and ConfigObject is that conceptually,
|
||||
* ConfigValue with valueType() of ConfigValueType.NULL exist in a ConfigObject,
|
||||
* while a Config treats null values as if they were missing.
|
||||
*
|
||||
* Config is an immutable object and thus safe to use from multiple threads.
|
||||
*
|
||||
* The "getters" on a Config all work in the same way. They never return null,
|
||||
* or a ConfigValue with valueType() of ConfigValueType.NULL. If the value is
|
||||
* completely absent then ConfigException.Missing will be thrown, and if the
|
||||
* value is present but null, ConfigException.Null will be thrown.
|
||||
* ConfigException.Null is a subclass of ConfigException.WrongType, where
|
||||
* ConfigException.WrongType will be thrown anytime you ask for a type and the
|
||||
* value has an incompatible type. Reasonable type conversions are performed for
|
||||
* you though.
|
||||
*
|
||||
* If you want to iterate over the contents of a Config, you have to get its
|
||||
* ConfigObject with toObject, and then iterate over the ConfigObject.
|
||||
*
|
||||
*/
|
||||
public abstract class Config {
|
||||
protected Config() {
|
||||
}
|
||||
|
||||
public interface Config extends ConfigMergeable {
|
||||
/**
|
||||
* Gets the config as a tree of ConfigObject. This is a constant-time
|
||||
* operation (it is not proportional to the number of values in the Config).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract ConfigObject toObject();
|
||||
ConfigObject toObject();
|
||||
|
||||
public abstract ConfigOrigin origin();
|
||||
ConfigOrigin origin();
|
||||
|
||||
@Override
|
||||
Config withFallback(ConfigMergeable other);
|
||||
|
||||
@Override
|
||||
Config withFallbacks(ConfigMergeable... others);
|
||||
|
||||
@Override
|
||||
ConfigObject toValue();
|
||||
|
||||
/**
|
||||
* Loads a configuration for the given root path in a "standard" way.
|
||||
* Oversimplified, if your root path is foo.bar then this will load files
|
||||
* from the classpath: foo-bar.conf, foo-bar.json, foo-bar.properties,
|
||||
* foo-bar-reference.conf, foo-bar-reference.json,
|
||||
* foo-bar-reference.properties. It will override all those files with any
|
||||
* system properties that begin with "foo.bar.", as well.
|
||||
* Checks whether a value is present and non-null at the given path. This
|
||||
* differs in two ways from ConfigObject.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.
|
||||
*
|
||||
* The root path should be a path expression, usually just a single short
|
||||
* word, that scopes the package being configured; typically it's the
|
||||
* package name or something similar. System properties overriding values in
|
||||
* the configuration will have to be prefixed with the root path. The root
|
||||
* path may have periods in it if you like but other punctuation or
|
||||
* whitespace will probably cause you headaches. Example root paths: "akka",
|
||||
* "sbt", "jsoup", "heroku", "mongo", etc.
|
||||
*
|
||||
* The loaded object will already be resolved (substitutions have already
|
||||
* been processed). As a result, if you add more fallbacks then they won't
|
||||
* be seen by substitutions. Substitutions are the "${foo.bar}" syntax. If
|
||||
* you want to parse additional files or something then you need to use
|
||||
* loadWithoutResolving().
|
||||
*
|
||||
* @param rootPath
|
||||
* the configuration "domain"
|
||||
* @return configuration object for the requested root path
|
||||
*/
|
||||
public static ConfigRoot load(String rootPath) {
|
||||
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
|
||||
* more fallbacks and stuff and have them seen by substitutions when you do
|
||||
* call {@link ConfigRoot.resolve()}.
|
||||
*
|
||||
* @param rootPath
|
||||
* @return
|
||||
*/
|
||||
public static ConfigRoot loadWithoutResolving(String rootPath) {
|
||||
return loadWithoutResolving(rootPath, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
public static ConfigRoot loadWithoutResolving(String rootPath,
|
||||
ConfigParseOptions options) {
|
||||
ConfigRoot system = systemPropertiesRoot(rootPath);
|
||||
|
||||
ConfigValue mainFiles = parse(rootPath, options);
|
||||
ConfigValue referenceFiles = parse(rootPath + ".reference", options);
|
||||
|
||||
return system.withFallbacks(mainFiles, referenceFiles);
|
||||
}
|
||||
|
||||
public static ConfigRoot emptyRoot(String rootPath) {
|
||||
return emptyRoot(rootPath, null);
|
||||
}
|
||||
|
||||
public static ConfigObject empty() {
|
||||
return empty(null);
|
||||
}
|
||||
|
||||
public static ConfigRoot emptyRoot(String rootPath, String originDescription) {
|
||||
return ConfigImpl.emptyRoot(rootPath, originDescription);
|
||||
}
|
||||
|
||||
public static ConfigObject empty(String originDescription) {
|
||||
return ConfigImpl.empty(originDescription);
|
||||
}
|
||||
|
||||
public static ConfigRoot systemPropertiesRoot(String rootPath) {
|
||||
return ConfigImpl.systemPropertiesRoot(rootPath);
|
||||
}
|
||||
|
||||
public static ConfigObject systemProperties() {
|
||||
return ConfigImpl.systemPropertiesAsConfig();
|
||||
}
|
||||
|
||||
public static ConfigObject systemEnvironment() {
|
||||
return ConfigImpl.envVariablesAsConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Java Properties object to a ConfigObject using the rules
|
||||
* documented in https://github.com/havocp/config/blob/master/HOCON.md The
|
||||
* keys in the Properties object are split on the period character '.' and
|
||||
* treated as paths. The values will all end up as string values. If you
|
||||
* have both "a=foo" and "a.b=bar" in your properties file, so "a" is both
|
||||
* the object containing "b" and the string "foo", then the string value is
|
||||
* dropped.
|
||||
*
|
||||
* If you want to get System.getProperties() as a ConfigObject, it's better
|
||||
* to use the systemProperties() or systemPropertiesRoot() methods. Those
|
||||
* methods are able to use a cached global singleton ConfigObject for the
|
||||
* system properties.
|
||||
*
|
||||
* @param properties
|
||||
* a Java Properties object
|
||||
* @param options
|
||||
* @return
|
||||
*/
|
||||
public static ConfigObject parse(Properties properties,
|
||||
ConfigParseOptions options) {
|
||||
return Parseable.newProperties(properties, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(Reader reader, ConfigParseOptions options) {
|
||||
return Parseable.newReader(reader, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(URL url, ConfigParseOptions options) {
|
||||
return Parseable.newURL(url, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(File file, ConfigParseOptions options) {
|
||||
return Parseable.newFile(file, options).parse();
|
||||
}
|
||||
|
||||
public static ConfigObject parse(Class<?> klass, String resource,
|
||||
ConfigParseOptions options) {
|
||||
return Parseable.newResource(klass, resource, options).parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses classpath resources corresponding to this path expression.
|
||||
* Essentially if the path is "foo.bar" then the resources are
|
||||
* "/foo-bar.conf", "/foo-bar.json", and "/foo-bar.properties". If more than
|
||||
* one of those exists, they are merged.
|
||||
* 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 isEmpty();
|
||||
|
||||
boolean getBoolean(String path);
|
||||
|
||||
Number getNumber(String path);
|
||||
|
||||
int getInt(String path);
|
||||
|
||||
long getLong(String path);
|
||||
|
||||
double getDouble(String path);
|
||||
|
||||
String getString(String path);
|
||||
|
||||
ConfigObject getObject(String path);
|
||||
|
||||
Config getConfig(String path);
|
||||
|
||||
/**
|
||||
* Gets the value at the path as an unwrapped Java boxed value (Boolean,
|
||||
* Integer, Long, etc.)
|
||||
*/
|
||||
Object getAnyRef(String path);
|
||||
|
||||
/**
|
||||
* Gets the value at the given path, unless the value is a null value or
|
||||
* missing, in which case it throws just like the other getters. Use get()
|
||||
* from the Map interface if you want an unprocessed value.
|
||||
*
|
||||
* @param path
|
||||
* @param options
|
||||
* @return
|
||||
*/
|
||||
public static ConfigObject parse(String path, ConfigParseOptions options) {
|
||||
// null originDescription is allowed in parseResourcesForPath
|
||||
return ConfigImpl.parseResourcesForPath(path, options);
|
||||
}
|
||||
ConfigValue getValue(String path);
|
||||
|
||||
/**
|
||||
* Creates a ConfigValue from a plain Java boxed value, which may be a
|
||||
* Boolean, Number, String, Map, Iterable, or null. A Map must be a Map from
|
||||
* String to more values that can be supplied to fromAnyRef(). An Iterable
|
||||
* must iterate over more values that can be supplied to fromAnyRef(). A Map
|
||||
* will become a ConfigObject and an Iterable will become a ConfigList. If
|
||||
* the Iterable is not an ordered collection, results could be strange,
|
||||
* since ConfigList is ordered.
|
||||
*
|
||||
* In a Map passed to fromAnyRef(), the map's keys are plain keys, not path
|
||||
* expressions. So if your Map has a key "foo.bar" then you will get one
|
||||
* object with a key called "foo.bar", rather than an object with a key
|
||||
* "foo" containing another object with a key "bar".
|
||||
*
|
||||
* The originDescription will be used to set the origin() field on the
|
||||
* ConfigValue. It should normally be the name of the file the values came
|
||||
* from, or something short describing the value such as "default settings".
|
||||
* The originDescription is prefixed to error messages so users can tell
|
||||
* where problematic values are coming from.
|
||||
*
|
||||
* Supplying the result of ConfigValue.unwrapped() to this function is
|
||||
* guaranteed to work and should give you back a ConfigValue that matches
|
||||
* the one you unwrapped. The re-wrapped ConfigValue will lose some
|
||||
* information that was present in the original such as its origin, but it
|
||||
* will have matching values.
|
||||
*
|
||||
* This function throws if you supply a value that cannot be converted to a
|
||||
* ConfigValue, but supplying such a value is a bug in your program, so you
|
||||
* should never handle the exception. Just fix your program (or report a bug
|
||||
* against this library).
|
||||
*
|
||||
* @param object
|
||||
* object to convert to ConfigValue
|
||||
* @param originDescription
|
||||
* name of origin file or brief description of what the value is
|
||||
* @return a new value
|
||||
* Get value as a size in bytes (parses special strings like "128M"). The
|
||||
* size units are interpreted as for memory, not as for disk space, so they
|
||||
* are in powers of two.
|
||||
*/
|
||||
public static ConfigValue fromAnyRef(Object object, String originDescription) {
|
||||
return ConfigImpl.fromAnyRef(object, originDescription);
|
||||
}
|
||||
Long getMemorySizeInBytes(String path);
|
||||
|
||||
/**
|
||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||
* wrapper that only works on Map and returns ConfigObject rather than
|
||||
* ConfigValue.
|
||||
*
|
||||
* If your Map has a key "foo.bar" then you will get one object with a key
|
||||
* called "foo.bar", rather than an object with a key "foo" containing
|
||||
* another object with a key "bar". The keys in the map are keys; not path
|
||||
* expressions. That is, the Map corresponds exactly to a single
|
||||
* ConfigObject. The keys will not be parsed or modified, and the values are
|
||||
* wrapped in ConfigValue. To get nested ConfigObject, some of the values in
|
||||
* the map would have to be more maps.
|
||||
*
|
||||
* There is a separate fromPathMap() that interprets the keys in the map as
|
||||
* path expressions.
|
||||
*
|
||||
* @param values
|
||||
* @param originDescription
|
||||
* @return
|
||||
* Get value as a duration in milliseconds. If the value is already a
|
||||
* number, then it's left alone; if it's a string, it's parsed understanding
|
||||
* units suffixes like "10m" or "5ns"
|
||||
*/
|
||||
public static ConfigObject fromMap(Map<String, ? extends Object> values,
|
||||
String originDescription) {
|
||||
return (ConfigObject) fromAnyRef(values, originDescription);
|
||||
}
|
||||
Long getMilliseconds(String path);
|
||||
|
||||
/**
|
||||
* Similar to fromMap(), but the keys in the map are path expressions,
|
||||
* rather than keys. This is more convenient if you are writing literal maps
|
||||
* in code, and less convenient if you are getting your maps from some data
|
||||
* source such as a parser.
|
||||
*
|
||||
* An exception will be thrown (and it is a bug in the caller of the method)
|
||||
* if a path is both an object and a value, for example if you had both
|
||||
* "a=foo" and "a.b=bar", then "a" is both the string "foo" and the parent
|
||||
* object of "b". The caller of this method should ensure that doesn't
|
||||
* happen.
|
||||
*
|
||||
* @param values
|
||||
* @param originDescription
|
||||
* @return
|
||||
* Get value as a duration in nanoseconds. If the value is already a number
|
||||
* it's taken as milliseconds and converted to nanoseconds. If it's a
|
||||
* string, it's parsed understanding unit suffixes.
|
||||
*/
|
||||
public static ConfigObject fromPathMap(
|
||||
Map<String, ? extends Object> values, String originDescription) {
|
||||
return ConfigImpl.fromPathMap(values, originDescription);
|
||||
}
|
||||
Long getNanoseconds(String path);
|
||||
|
||||
/**
|
||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||
* wrapper that only works on Iterable and returns ConfigList rather than
|
||||
* ConfigValue.
|
||||
* Gets a list value (with any element type) as a ConfigList, which
|
||||
* implements java.util.List<ConfigValue>. Throws if the path is unset or
|
||||
* null.
|
||||
*
|
||||
* @param values
|
||||
* @param originDescription
|
||||
* @return
|
||||
* @param path
|
||||
* the path to the list value.
|
||||
* @return the ConfigList at the path
|
||||
*/
|
||||
public static ConfigList fromIterable(Iterable<? extends Object> values,
|
||||
String originDescription) {
|
||||
return (ConfigList) fromAnyRef(values, originDescription);
|
||||
}
|
||||
ConfigList getList(String path);
|
||||
|
||||
/**
|
||||
* See the other overload of fromAnyRef() for details, this one just uses a
|
||||
* default origin description.
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static ConfigValue fromAnyRef(Object object) {
|
||||
return fromAnyRef(object, null);
|
||||
}
|
||||
List<Boolean> getBooleanList(String path);
|
||||
|
||||
/**
|
||||
* See the other overload of fromMap() for details, this one just uses a
|
||||
* default origin description.
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static ConfigObject fromMap(Map<String, ? extends Object> values) {
|
||||
return fromMap(values, null);
|
||||
}
|
||||
List<Number> getNumberList(String path);
|
||||
|
||||
/**
|
||||
* See the other overload of fromPathMap() for details, this one just uses a
|
||||
* default origin description.
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static ConfigObject fromPathMap(Map<String, ? extends Object> values) {
|
||||
return fromPathMap(values, null);
|
||||
}
|
||||
List<Integer> getIntList(String path);
|
||||
|
||||
/**
|
||||
* See the other overload of fromIterable() for details, this one just uses
|
||||
* a default origin description.
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static ConfigList fromIterable(Collection<? extends Object> values) {
|
||||
return fromIterable(values, null);
|
||||
}
|
||||
List<Long> getLongList(String path);
|
||||
|
||||
private static String getUnits(String s) {
|
||||
int i = s.length() - 1;
|
||||
while (i >= 0) {
|
||||
char c = s.charAt(i);
|
||||
if (!Character.isLetter(c))
|
||||
break;
|
||||
i -= 1;
|
||||
}
|
||||
return s.substring(i + 1);
|
||||
}
|
||||
List<Double> getDoubleList(String path);
|
||||
|
||||
/**
|
||||
* Parses a duration string. If no units are specified in the string, it is
|
||||
* assumed to be in milliseconds. The returned duration is in nanoseconds.
|
||||
* The purpose of this function is to implement the duration-related methods
|
||||
* in the ConfigObject interface.
|
||||
*
|
||||
* @param input
|
||||
* the string to parse
|
||||
* @param originForException
|
||||
* origin of the value being parsed
|
||||
* @param pathForException
|
||||
* path to include in exceptions
|
||||
* @return duration in nanoseconds
|
||||
* @throws ConfigException
|
||||
* if string is invalid
|
||||
*/
|
||||
public static long parseDuration(String input,
|
||||
ConfigOrigin originForException, String pathForException) {
|
||||
String s = ConfigUtil.unicodeTrim(input);
|
||||
String originalUnitString = getUnits(s);
|
||||
String unitString = originalUnitString;
|
||||
String numberString = ConfigUtil.unicodeTrim(s.substring(0, s.length()
|
||||
- unitString.length()));
|
||||
TimeUnit units = null;
|
||||
List<String> getStringList(String path);
|
||||
|
||||
// this would be caught later anyway, but the error message
|
||||
// is more helpful if we check it here.
|
||||
if (numberString.length() == 0)
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "No number in duration value '" + input
|
||||
+ "'");
|
||||
List<? extends ConfigObject> getObjectList(String path);
|
||||
|
||||
if (unitString.length() > 2 && !unitString.endsWith("s"))
|
||||
unitString = unitString + "s";
|
||||
List<? extends Config> getConfigList(String path);
|
||||
|
||||
// note that this is deliberately case-sensitive
|
||||
if (unitString.equals("") || unitString.equals("ms")
|
||||
|| unitString.equals("milliseconds")) {
|
||||
units = TimeUnit.MILLISECONDS;
|
||||
} else if (unitString.equals("us") || unitString.equals("microseconds")) {
|
||||
units = TimeUnit.MICROSECONDS;
|
||||
} else if (unitString.equals("ns") || unitString.equals("nanoseconds")) {
|
||||
units = TimeUnit.NANOSECONDS;
|
||||
} else if (unitString.equals("d") || unitString.equals("days")) {
|
||||
units = TimeUnit.DAYS;
|
||||
} else if (unitString.equals("h") || unitString.equals("hours")) {
|
||||
units = TimeUnit.HOURS;
|
||||
} else if (unitString.equals("s") || unitString.equals("seconds")) {
|
||||
units = TimeUnit.SECONDS;
|
||||
} else if (unitString.equals("m") || unitString.equals("minutes")) {
|
||||
units = TimeUnit.MINUTES;
|
||||
} else {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse time unit '"
|
||||
+ originalUnitString
|
||||
+ "' (try ns, us, ms, s, m, d)");
|
||||
}
|
||||
List<? extends Object> getAnyRefList(String path);
|
||||
|
||||
try {
|
||||
// if the string is purely digits, parse as an integer to avoid
|
||||
// possible precision loss;
|
||||
// otherwise as a double.
|
||||
if (numberString.matches("[0-9]+")) {
|
||||
return units.toNanos(Long.parseLong(numberString));
|
||||
} else {
|
||||
long nanosInUnit = units.toNanos(1);
|
||||
return (long) (Double.parseDouble(numberString) * nanosInUnit);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse duration number '"
|
||||
+ numberString + "'");
|
||||
}
|
||||
}
|
||||
List<Long> getMemorySizeInBytesList(String path);
|
||||
|
||||
private static enum MemoryUnit {
|
||||
BYTES(1), KILOBYTES(1024), MEGABYTES(1024 * 1024), GIGABYTES(
|
||||
1024 * 1024 * 1024), TERABYTES(1024 * 1024 * 1024 * 1024);
|
||||
List<Long> getMillisecondsList(String path);
|
||||
|
||||
int bytes;
|
||||
|
||||
MemoryUnit(int bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a memory-size string. If no units are specified in the string, it
|
||||
* is assumed to be in bytes. The returned value is in bytes. The purpose of
|
||||
* this function is to implement the memory-size-related methods in the
|
||||
* ConfigObject interface. The units parsed are interpreted as powers of
|
||||
* two, that is, the convention for memory rather than the convention for
|
||||
* disk space.
|
||||
*
|
||||
* @param input
|
||||
* the string to parse
|
||||
* @param originForException
|
||||
* origin of the value being parsed
|
||||
* @param pathForException
|
||||
* path to include in exceptions
|
||||
* @return size in bytes
|
||||
* @throws ConfigException
|
||||
* if string is invalid
|
||||
*/
|
||||
public static long parseMemorySizeInBytes(String input,
|
||||
ConfigOrigin originForException, String pathForException) {
|
||||
String s = ConfigUtil.unicodeTrim(input);
|
||||
String unitStringMaybePlural = getUnits(s);
|
||||
String unitString;
|
||||
if (unitStringMaybePlural.endsWith("s"))
|
||||
unitString = unitStringMaybePlural.substring(0,
|
||||
unitStringMaybePlural.length() - 1);
|
||||
else
|
||||
unitString = unitStringMaybePlural;
|
||||
String unitStringLower = unitString.toLowerCase();
|
||||
String numberString = ConfigUtil.unicodeTrim(s.substring(0, s.length()
|
||||
- unitStringMaybePlural.length()));
|
||||
|
||||
// this would be caught later anyway, but the error message
|
||||
// is more helpful if we check it here.
|
||||
if (numberString.length() == 0)
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "No number in size-in-bytes value '"
|
||||
+ input + "'");
|
||||
|
||||
MemoryUnit units = null;
|
||||
|
||||
// the short abbreviations are case-insensitive but you can't write the
|
||||
// long form words in all caps.
|
||||
if (unitString.equals("") || unitStringLower.equals("b")
|
||||
|| unitString.equals("byte")) {
|
||||
units = MemoryUnit.BYTES;
|
||||
} else if (unitStringLower.equals("k") || unitString.equals("kilobyte")) {
|
||||
units = MemoryUnit.KILOBYTES;
|
||||
} else if (unitStringLower.equals("m") || unitString.equals("megabyte")) {
|
||||
units = MemoryUnit.MEGABYTES;
|
||||
} else if (unitStringLower.equals("g") || unitString.equals("gigabyte")) {
|
||||
units = MemoryUnit.GIGABYTES;
|
||||
} else if (unitStringLower.equals("t") || unitString.equals("terabyte")) {
|
||||
units = MemoryUnit.TERABYTES;
|
||||
} else {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse size unit '"
|
||||
+ unitStringMaybePlural + "' (try b, k, m, g, t)");
|
||||
}
|
||||
|
||||
try {
|
||||
// if the string is purely digits, parse as an integer to avoid
|
||||
// possible precision loss;
|
||||
// otherwise as a double.
|
||||
if (numberString.matches("[0-9]+")) {
|
||||
return Long.parseLong(numberString) * units.bytes;
|
||||
} else {
|
||||
return (long) (Double.parseDouble(numberString) * units.bytes);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse memory size number '"
|
||||
+ numberString + "'");
|
||||
}
|
||||
}
|
||||
List<Long> getNanosecondsList(String path);
|
||||
}
|
||||
|
199
src/main/java/com/typesafe/config/ConfigFactory.java
Normal file
199
src/main/java/com/typesafe/config/ConfigFactory.java
Normal file
@ -0,0 +1,199 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.typesafe.config.impl.ConfigImpl;
|
||||
import com.typesafe.config.impl.Parseable;
|
||||
|
||||
/**
|
||||
* This class contains static methods for creating Config objects.
|
||||
*
|
||||
* The static 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 ConfigFactory {
|
||||
/**
|
||||
* Loads a configuration for the given root path in a "standard" way.
|
||||
* Oversimplified, if your root path is foo.bar then this will load files
|
||||
* from the classpath: foo-bar.conf, foo-bar.json, foo-bar.properties,
|
||||
* foo-bar-reference.conf, foo-bar-reference.json,
|
||||
* foo-bar-reference.properties. It will override all those files with any
|
||||
* system properties that begin with "foo.bar.", as well.
|
||||
*
|
||||
* The root path should be a path expression, usually just a single short
|
||||
* word, that scopes the package being configured; typically it's the
|
||||
* package name or something similar. System properties overriding values in
|
||||
* the configuration will have to be prefixed with the root path. The root
|
||||
* path may have periods in it if you like but other punctuation or
|
||||
* whitespace will probably cause you headaches. Example root paths: "akka",
|
||||
* "sbt", "jsoup", "heroku", "mongo", etc.
|
||||
*
|
||||
* The loaded object will already be resolved (substitutions have already
|
||||
* been processed). As a result, if you add more fallbacks then they won't
|
||||
* be seen by substitutions. Substitutions are the "${foo.bar}" syntax. If
|
||||
* you want to parse additional files or something then you need to use
|
||||
* loadWithoutResolving().
|
||||
*
|
||||
* @param rootPath
|
||||
* the configuration "domain"
|
||||
* @return configuration object for the requested root path
|
||||
*/
|
||||
public static ConfigRoot load(String rootPath) {
|
||||
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
|
||||
* more fallbacks and stuff and have them seen by substitutions when you do
|
||||
* call {@link ConfigRoot.resolve()}.
|
||||
*
|
||||
* @param rootPath
|
||||
* @return
|
||||
*/
|
||||
public static ConfigRoot loadWithoutResolving(String rootPath) {
|
||||
return loadWithoutResolving(rootPath, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
public static ConfigRoot loadWithoutResolving(String rootPath,
|
||||
ConfigParseOptions options) {
|
||||
ConfigRoot system = systemPropertiesRoot(rootPath);
|
||||
|
||||
Config mainFiles = parse(rootPath, options);
|
||||
Config referenceFiles = parse(rootPath + ".reference", options);
|
||||
|
||||
return system.withFallbacks(mainFiles, referenceFiles);
|
||||
}
|
||||
|
||||
public static ConfigRoot emptyRoot(String rootPath) {
|
||||
return emptyRoot(rootPath, null);
|
||||
}
|
||||
|
||||
public static Config empty() {
|
||||
return empty(null);
|
||||
}
|
||||
|
||||
public static ConfigRoot emptyRoot(String rootPath, String originDescription) {
|
||||
return ConfigImpl.emptyRoot(rootPath, originDescription);
|
||||
}
|
||||
|
||||
public static Config empty(String originDescription) {
|
||||
return ConfigImpl.emptyConfig(originDescription);
|
||||
}
|
||||
|
||||
public static ConfigRoot systemPropertiesRoot(String rootPath) {
|
||||
return ConfigImpl.systemPropertiesRoot(rootPath);
|
||||
}
|
||||
|
||||
public static Config systemProperties() {
|
||||
return ConfigImpl.systemPropertiesAsConfig();
|
||||
}
|
||||
|
||||
public static Config systemEnvironment() {
|
||||
return ConfigImpl.envVariablesAsConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Java Properties object to a ConfigObject using the rules
|
||||
* documented in https://github.com/havocp/config/blob/master/HOCON.md The
|
||||
* keys in the Properties object are split on the period character '.' and
|
||||
* treated as paths. The values will all end up as string values. If you
|
||||
* have both "a=foo" and "a.b=bar" in your properties file, so "a" is both
|
||||
* the object containing "b" and the string "foo", then the string value is
|
||||
* dropped.
|
||||
*
|
||||
* If you want to get System.getProperties() as a ConfigObject, it's better
|
||||
* to use the systemProperties() or systemPropertiesRoot() methods. Those
|
||||
* methods are able to use a cached global singleton ConfigObject for the
|
||||
* system properties.
|
||||
*
|
||||
* @param properties
|
||||
* a Java Properties object
|
||||
* @param options
|
||||
* @return
|
||||
*/
|
||||
public static Config parse(Properties properties,
|
||||
ConfigParseOptions options) {
|
||||
return Parseable.newProperties(properties, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parse(Reader reader, ConfigParseOptions options) {
|
||||
return Parseable.newReader(reader, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parse(URL url, ConfigParseOptions options) {
|
||||
return Parseable.newURL(url, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parse(File file, ConfigParseOptions options) {
|
||||
return Parseable.newFile(file, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parse(Class<?> klass, String resource,
|
||||
ConfigParseOptions options) {
|
||||
return Parseable.newResource(klass, resource, options).parse()
|
||||
.toConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses classpath resources corresponding to this path expression.
|
||||
* Essentially if the path is "foo.bar" then the resources are
|
||||
* "/foo-bar.conf", "/foo-bar.json", and "/foo-bar.properties". If more than
|
||||
* one of those exists, they are merged.
|
||||
*
|
||||
* @param path
|
||||
* @param options
|
||||
* @return
|
||||
*/
|
||||
public static Config parse(String path, ConfigParseOptions options) {
|
||||
// null originDescription is allowed in parseResourcesForPath
|
||||
return ConfigImpl.parseResourcesForPath(path, options).toConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to ConfigValueFactory.fromMap(), but the keys in the map are path
|
||||
* expressions, rather than keys; and correspondingly it returns a Config
|
||||
* instead of a ConfigObject. This is more convenient if you are writing
|
||||
* literal maps in code, and less convenient if you are getting your maps
|
||||
* from some data source such as a parser.
|
||||
*
|
||||
* An exception will be thrown (and it is a bug in the caller of the method)
|
||||
* if a path is both an object and a value, for example if you had both
|
||||
* "a=foo" and "a.b=bar", then "a" is both the string "foo" and the parent
|
||||
* object of "b". The caller of this method should ensure that doesn't
|
||||
* happen.
|
||||
*
|
||||
* @param values
|
||||
* @param originDescription
|
||||
* description of what this map represents, like a filename, or
|
||||
* "default settings" (origin description is used in error
|
||||
* messages)
|
||||
* @return
|
||||
*/
|
||||
public static Config parseMap(Map<String, ? extends Object> values,
|
||||
String originDescription) {
|
||||
return ConfigImpl.fromPathMap(values, originDescription).toConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* See the other overload of parseMap() for details, this one just uses a
|
||||
* default origin description.
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static Config parseMap(Map<String, ? extends Object> values) {
|
||||
return parseMap(values, null);
|
||||
}
|
||||
}
|
38
src/main/java/com/typesafe/config/ConfigMergeable.java
Normal file
38
src/main/java/com/typesafe/config/ConfigMergeable.java
Normal file
@ -0,0 +1,38 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
/**
|
||||
* This is a marker for types that can be merged as a fallback into a Config or
|
||||
* a ConfigValue. Both Config and ConfigValue are mergeable.
|
||||
*/
|
||||
public interface ConfigMergeable {
|
||||
/**
|
||||
* Converts the mergeable to a ConfigValue to be merged.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ConfigValue toValue();
|
||||
|
||||
/**
|
||||
* Returns a new value computed by merging this value with another, with
|
||||
* keys in this value "winning" over the other one. Only ConfigObject and
|
||||
* Config instances do anything in this method (they need to merge the
|
||||
* fallback keys into themselves). All other values just return the original
|
||||
* value, since they automatically override any fallback.
|
||||
*
|
||||
* @param other
|
||||
* an object whose keys should be used if the keys are not
|
||||
* present in this one
|
||||
* @return a new object (or the original one, if the fallback doesn't get
|
||||
* used)
|
||||
*/
|
||||
ConfigMergeable withFallback(ConfigMergeable other);
|
||||
|
||||
/**
|
||||
* Convenience method just calls withFallback() on each of the values;
|
||||
* earlier values in the list win over later ones.
|
||||
*
|
||||
* @param fallbacks
|
||||
* @return a version of the object with the requested fallbacks merged in
|
||||
*/
|
||||
ConfigMergeable withFallbacks(ConfigMergeable... others);
|
||||
}
|
@ -1,12 +1,19 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A ConfigObject is a read-only configuration object, which may have nested
|
||||
* child objects. Implementations of ConfigObject should be immutable (at least
|
||||
* from the perspective of anyone using this interface).
|
||||
* from the perspective of anyone using this interface) and thus thread-safe.
|
||||
*
|
||||
* In most cases you want to use the Config interface rather than this one. Call
|
||||
* toConfig() to convert a ConfigObject to a config.
|
||||
*
|
||||
* The API for a ConfigObject is in terms of keys, while the API for a Config is
|
||||
* in terms of path expressions. Conceptually, ConfigObject is a tree of maps
|
||||
* from keys to values, while a ConfigObject is a one-level map from paths to
|
||||
* values.
|
||||
*
|
||||
* Throughout the API, there is a distinction between "keys" and "paths". A key
|
||||
* is a key in a JSON object; it's just a string that's the key in a map. A
|
||||
@ -18,29 +25,17 @@ import java.util.Map;
|
||||
* object a in the root object. Sometimes double quotes are needed around
|
||||
* special characters in path expressions.
|
||||
*
|
||||
* ConfigObject implements java.util.Map<String,ConfigValue>. For all methods
|
||||
* implementing the Map interface, the keys are just plain keys; not a parseable
|
||||
* path expression. In methods implementing Map, a ConfigValue with
|
||||
* ConfigValue.valueType() of ConfigValueType.NULL will be distinct from a
|
||||
* missing value. java.util.Map.containsKey() returns true if the map contains a
|
||||
* value of type ConfigValueType.NULL at that key.
|
||||
*
|
||||
* ConfigObject has another set of "getters", such as getValue() and getAnyRef()
|
||||
* and getInt(), with more convenient semantics than java.util.Map.get(). These
|
||||
* "getters" throw ConfigException.Missing if the value is entirely unset, and
|
||||
* ConfigException.WrongType if you ask for a type that the value can't be
|
||||
* converted to. ConfigException.Null is a subclass of ConfigException.WrongType
|
||||
* thrown if the value is null. These getters also use path expressions, rather
|
||||
* than keys, as described above.
|
||||
* ConfigObject implements java.util.Map<String,ConfigValue> and all methods
|
||||
* work with keys, not path expressions.
|
||||
*
|
||||
* While ConfigObject implements the standard Java Map interface, the mutator
|
||||
* methods all throw UnsupportedOperationException. This Map is immutable.
|
||||
*
|
||||
* 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.
|
||||
* ConfigValueType.NULL. If get() returns Java's null then the key was not
|
||||
* present in the parsed file (or wherever this value tree came from). If get()
|
||||
* returns a ConfigValue with type ConfigValueType.NULL then the key was set to
|
||||
* null explicitly.
|
||||
*/
|
||||
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||
|
||||
@ -48,7 +43,7 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||
* Converts this object to a Config instance, enabling you to use path
|
||||
* expressions to find values in the object. This is a constant-time
|
||||
* operation (it is not proportional to the size of the object).
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Config toConfig();
|
||||
@ -61,113 +56,10 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||
Map<String, Object> unwrapped();
|
||||
|
||||
@Override
|
||||
ConfigObject withFallback(ConfigValue other);
|
||||
ConfigObject withFallback(ConfigMergeable other);
|
||||
|
||||
@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);
|
||||
|
||||
int getInt(String path);
|
||||
|
||||
long getLong(String path);
|
||||
|
||||
double getDouble(String path);
|
||||
|
||||
String getString(String path);
|
||||
|
||||
ConfigObject getObject(String path);
|
||||
|
||||
/**
|
||||
* Gets the value at the path as an unwrapped Java boxed value (Boolean,
|
||||
* Integer, Long, etc.)
|
||||
*/
|
||||
Object getAnyRef(String path);
|
||||
|
||||
/**
|
||||
* Gets the value at the given path, unless the value is a null value or
|
||||
* missing, in which case it throws just like the other getters. Use get()
|
||||
* from the Map interface if you want an unprocessed value.
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
ConfigValue getValue(String path);
|
||||
|
||||
/**
|
||||
* Get value as a size in bytes (parses special strings like "128M"). The
|
||||
* size units are interpreted as for memory, not as for disk space, so they
|
||||
* are in powers of two.
|
||||
*/
|
||||
Long getMemorySizeInBytes(String path);
|
||||
|
||||
/**
|
||||
* Get value as a duration in milliseconds. If the value is already a
|
||||
* number, then it's left alone; if it's a string, it's parsed understanding
|
||||
* units suffixes like "10m" or "5ns"
|
||||
*/
|
||||
Long getMilliseconds(String path);
|
||||
|
||||
/**
|
||||
* Get value as a duration in nanoseconds. If the value is already a number
|
||||
* it's taken as milliseconds and converted to nanoseconds. If it's a
|
||||
* string, it's parsed understanding unit suffixes.
|
||||
*/
|
||||
Long getNanoseconds(String path);
|
||||
|
||||
/**
|
||||
* Gets a list value (with any element type) as a ConfigList, which
|
||||
* implements java.util.List<ConfigValue>. Throws if the path is unset or
|
||||
* null.
|
||||
*
|
||||
* @param path
|
||||
* the path to the list value.
|
||||
* @return the ConfigList at the path
|
||||
*/
|
||||
ConfigList getList(String path);
|
||||
|
||||
List<Boolean> getBooleanList(String path);
|
||||
|
||||
List<Number> getNumberList(String path);
|
||||
|
||||
List<Integer> getIntList(String path);
|
||||
|
||||
List<Long> getLongList(String path);
|
||||
|
||||
List<Double> getDoubleList(String path);
|
||||
|
||||
List<String> getStringList(String path);
|
||||
|
||||
List<? extends ConfigObject> getObjectList(String path);
|
||||
|
||||
List<? extends Object> getAnyRefList(String path);
|
||||
|
||||
List<Long> getMemorySizeInBytesList(String path);
|
||||
|
||||
List<Long> getMillisecondsList(String path);
|
||||
|
||||
List<Long> getNanosecondsList(String path);
|
||||
ConfigObject withFallbacks(ConfigMergeable... others);
|
||||
|
||||
/**
|
||||
* Gets a ConfigValue at the given key, or returns null if there is no
|
||||
|
@ -5,13 +5,13 @@ package com.typesafe.config;
|
||||
* resolve substitutions against it. So it can have a resolve() method that
|
||||
* doesn't require you to pass in an object to resolve against.
|
||||
*/
|
||||
public interface ConfigRoot extends ConfigObject {
|
||||
public interface ConfigRoot extends Config {
|
||||
/**
|
||||
* Returns a replacement root object with all substitutions (the
|
||||
* "${foo.bar}" syntax) resolved. Substitutions are looked up in this root
|
||||
* object. A configuration value tree must be resolved before you can use
|
||||
* it. This method uses ConfigResolveOptions.defaults().
|
||||
*
|
||||
*
|
||||
* @return an immutable object with substitutions resolved
|
||||
*/
|
||||
ConfigRoot resolve();
|
||||
@ -19,10 +19,10 @@ public interface ConfigRoot extends ConfigObject {
|
||||
ConfigRoot resolve(ConfigResolveOptions options);
|
||||
|
||||
@Override
|
||||
ConfigRoot withFallback(ConfigValue fallback);
|
||||
ConfigRoot withFallback(ConfigMergeable fallback);
|
||||
|
||||
@Override
|
||||
ConfigRoot withFallbacks(ConfigValue... fallbacks);
|
||||
ConfigRoot withFallbacks(ConfigMergeable... fallbacks);
|
||||
|
||||
/**
|
||||
* Gets the global app name that this root represents.
|
||||
|
@ -5,7 +5,7 @@ package com.typesafe.config;
|
||||
* users of this interface, the object is immutable. It is therefore safe to use
|
||||
* from multiple threads.
|
||||
*/
|
||||
public interface ConfigValue {
|
||||
public interface ConfigValue extends ConfigMergeable {
|
||||
/**
|
||||
* The origin of the value, for debugging and error messages.
|
||||
*
|
||||
@ -27,27 +27,9 @@ public interface ConfigValue {
|
||||
*/
|
||||
Object unwrapped();
|
||||
|
||||
/**
|
||||
* Returns a new value computed by merging this value with another, with
|
||||
* keys in this value "winning" over the other one. Only ConfigObject has
|
||||
* anything to do in this method (it merges the fallback keys into itself).
|
||||
* All other values just return the original value, since they automatically
|
||||
* override any fallback.
|
||||
*
|
||||
* @param other
|
||||
* an object whose keys should be used if the keys are not
|
||||
* present in this one
|
||||
* @return a new object (or the original one, if the fallback doesn't get
|
||||
* used)
|
||||
*/
|
||||
ConfigValue withFallback(ConfigValue other);
|
||||
@Override
|
||||
ConfigValue withFallback(ConfigMergeable other);
|
||||
|
||||
/**
|
||||
* Convenience method just calls withFallback() on each of the values;
|
||||
* earlier values in the list win over later ones.
|
||||
*
|
||||
* @param fallbacks
|
||||
* @return a version of the object with the requested fallbacks merged in
|
||||
*/
|
||||
ConfigValue withFallbacks(ConfigValue... fallbacks);
|
||||
@Override
|
||||
ConfigValue withFallbacks(ConfigMergeable... fallbacks);
|
||||
}
|
||||
|
126
src/main/java/com/typesafe/config/ConfigValueFactory.java
Normal file
126
src/main/java/com/typesafe/config/ConfigValueFactory.java
Normal file
@ -0,0 +1,126 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import com.typesafe.config.impl.ConfigImpl;
|
||||
|
||||
/**
|
||||
* This class holds some static factory methods for building ConfigValue. See
|
||||
* also ConfigFactory which has methods for parsing files and certain in-memory
|
||||
* data structures.
|
||||
*/
|
||||
public final class ConfigValueFactory {
|
||||
/**
|
||||
* Creates a ConfigValue from a plain Java boxed value, which may be a
|
||||
* Boolean, Number, String, Map, Iterable, or null. A Map must be a Map from
|
||||
* String to more values that can be supplied to fromAnyRef(). An Iterable
|
||||
* must iterate over more values that can be supplied to fromAnyRef(). A Map
|
||||
* will become a ConfigObject and an Iterable will become a ConfigList. If
|
||||
* the Iterable is not an ordered collection, results could be strange,
|
||||
* since ConfigList is ordered.
|
||||
*
|
||||
* In a Map passed to fromAnyRef(), the map's keys are plain keys, not path
|
||||
* expressions. So if your Map has a key "foo.bar" then you will get one
|
||||
* object with a key called "foo.bar", rather than an object with a key
|
||||
* "foo" containing another object with a key "bar".
|
||||
*
|
||||
* The originDescription will be used to set the origin() field on the
|
||||
* ConfigValue. It should normally be the name of the file the values came
|
||||
* from, or something short describing the value such as "default settings".
|
||||
* The originDescription is prefixed to error messages so users can tell
|
||||
* where problematic values are coming from.
|
||||
*
|
||||
* Supplying the result of ConfigValue.unwrapped() to this function is
|
||||
* guaranteed to work and should give you back a ConfigValue that matches
|
||||
* the one you unwrapped. The re-wrapped ConfigValue will lose some
|
||||
* information that was present in the original such as its origin, but it
|
||||
* will have matching values.
|
||||
*
|
||||
* This function throws if you supply a value that cannot be converted to a
|
||||
* ConfigValue, but supplying such a value is a bug in your program, so you
|
||||
* should never handle the exception. Just fix your program (or report a bug
|
||||
* against this library).
|
||||
*
|
||||
* @param object
|
||||
* object to convert to ConfigValue
|
||||
* @param originDescription
|
||||
* name of origin file or brief description of what the value is
|
||||
* @return a new value
|
||||
*/
|
||||
public static ConfigValue fromAnyRef(Object object, String originDescription) {
|
||||
return ConfigImpl.fromAnyRef(object, originDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||
* wrapper that only works on Map and returns ConfigObject rather than
|
||||
* ConfigValue.
|
||||
*
|
||||
* If your Map has a key "foo.bar" then you will get one object with a key
|
||||
* called "foo.bar", rather than an object with a key "foo" containing
|
||||
* another object with a key "bar". The keys in the map are keys; not path
|
||||
* expressions. That is, the Map corresponds exactly to a single
|
||||
* ConfigObject. The keys will not be parsed or modified, and the values are
|
||||
* wrapped in ConfigValue. To get nested ConfigObject, some of the values in
|
||||
* the map would have to be more maps.
|
||||
*
|
||||
* There is a separate fromPathMap() that interprets the keys in the map as
|
||||
* path expressions.
|
||||
*
|
||||
* @param values
|
||||
* @param originDescription
|
||||
* @return
|
||||
*/
|
||||
public static ConfigObject fromMap(Map<String, ? extends Object> values,
|
||||
String originDescription) {
|
||||
return (ConfigObject) fromAnyRef(values, originDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||
* wrapper that only works on Iterable and returns ConfigList rather than
|
||||
* ConfigValue.
|
||||
*
|
||||
* @param values
|
||||
* @param originDescription
|
||||
* @return
|
||||
*/
|
||||
public static ConfigList fromIterable(Iterable<? extends Object> values,
|
||||
String originDescription) {
|
||||
return (ConfigList) fromAnyRef(values, originDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the other overload of fromAnyRef() for details, this one just uses a
|
||||
* default origin description.
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static ConfigValue fromAnyRef(Object object) {
|
||||
return fromAnyRef(object, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the other overload of fromMap() for details, this one just uses a
|
||||
* default origin description.
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static ConfigObject fromMap(Map<String, ? extends Object> values) {
|
||||
return fromMap(values, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the other overload of fromIterable() for details, this one just uses
|
||||
* a default origin description.
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static ConfigList fromIterable(Collection<? extends Object> values) {
|
||||
return fromIterable(values, null);
|
||||
}
|
||||
}
|
@ -10,11 +10,9 @@ import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigList;
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigObject;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
@ -31,32 +29,10 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config toConfig() {
|
||||
public SimpleConfig toConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of this object that implements the ConfigRoot
|
||||
* interface.
|
||||
*
|
||||
* @return a config root
|
||||
*/
|
||||
protected ConfigRootImpl asRoot(Path rootPath) {
|
||||
return new RootConfigObject(this, rootPath);
|
||||
}
|
||||
|
||||
protected static ConfigRootImpl resolve(ConfigRootImpl root) {
|
||||
return resolve(root, ConfigResolveOptions.defaults());
|
||||
}
|
||||
|
||||
protected static ConfigRootImpl resolve(ConfigRootImpl root,
|
||||
ConfigResolveOptions options) {
|
||||
AbstractConfigValue resolved = SubstitutionResolver.resolve(
|
||||
(AbstractConfigValue) root, (AbstractConfigObject) root,
|
||||
options);
|
||||
return ((AbstractConfigObject) resolved).asRoot(root.rootPathObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* This looks up the key with no transformation or type conversion of any
|
||||
* kind, and returns null if the key is not present.
|
||||
@ -124,70 +100,17 @@ 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;
|
||||
}
|
||||
|
||||
protected abstract AbstractConfigObject newCopy(ResolveStatus status);
|
||||
|
||||
static private AbstractConfigValue resolve(AbstractConfigObject self,
|
||||
String pathExpression, ConfigValueType expected,
|
||||
String originalPath) {
|
||||
Path path = Path.newPath(pathExpression);
|
||||
return find(self, path, expected, originalPath);
|
||||
}
|
||||
|
||||
static private AbstractConfigValue findKey(AbstractConfigObject self,
|
||||
String key, ConfigValueType expected, String originalPath) {
|
||||
AbstractConfigValue v = self.peek(key);
|
||||
if (v == null)
|
||||
throw new ConfigException.Missing(originalPath);
|
||||
|
||||
if (expected != null)
|
||||
v = DefaultTransformer.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 find(AbstractConfigObject self,
|
||||
Path path, ConfigValueType expected,
|
||||
String originalPath) {
|
||||
String key = path.first();
|
||||
Path next = path.remainder();
|
||||
if (next == null) {
|
||||
return findKey(self, key, expected, originalPath);
|
||||
} else {
|
||||
AbstractConfigObject o = (AbstractConfigObject) findKey(self, key,
|
||||
ConfigValueType.OBJECT, originalPath);
|
||||
assert (o != null); // missing was supposed to throw
|
||||
return find(o, next, expected, originalPath);
|
||||
}
|
||||
}
|
||||
|
||||
AbstractConfigValue find(String pathExpression,
|
||||
ConfigValueType expected,
|
||||
String originalPath) {
|
||||
return resolve(this, pathExpression, expected,
|
||||
originalPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject withFallbacks(ConfigValue... others) {
|
||||
public AbstractConfigObject withFallbacks(ConfigMergeable... others) {
|
||||
return (AbstractConfigObject) super.withFallbacks(others);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject withFallback(ConfigValue other) {
|
||||
public AbstractConfigObject withFallback(ConfigMergeable mergeable) {
|
||||
ConfigValue other = mergeable.toValue();
|
||||
|
||||
if (other instanceof Unmergeable) {
|
||||
List<AbstractConfigValue> stack = new ArrayList<AbstractConfigValue>();
|
||||
stack.add(this);
|
||||
@ -361,234 +284,6 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue getValue(String path) {
|
||||
return find(path, null, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String path) {
|
||||
ConfigValue v = find(path, ConfigValueType.BOOLEAN, path);
|
||||
return (Boolean) v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number getNumber(String path) {
|
||||
ConfigValue v = find(path, ConfigValueType.NUMBER, path);
|
||||
return (Number) v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String path) {
|
||||
return getNumber(path).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String path) {
|
||||
return getNumber(path).longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(String path) {
|
||||
return getNumber(path).doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String path) {
|
||||
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
||||
return (String) v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigList getList(String path) {
|
||||
AbstractConfigValue v = find(path, ConfigValueType.LIST, path);
|
||||
return (ConfigList) v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject getObject(String path) {
|
||||
AbstractConfigObject obj = (AbstractConfigObject) find(path,
|
||||
ConfigValueType.OBJECT, path);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAnyRef(String path) {
|
||||
ConfigValue v = find(path, null, path);
|
||||
return v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getMemorySizeInBytes(String path) {
|
||||
Long size = null;
|
||||
try {
|
||||
size = getLong(path);
|
||||
} catch (ConfigException.WrongType e) {
|
||||
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
||||
size = Config.parseMemorySizeInBytes((String) v.unwrapped(), v.origin(),
|
||||
path);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getMilliseconds(String path) {
|
||||
long ns = getNanoseconds(path);
|
||||
long ms = TimeUnit.NANOSECONDS.toMillis(ns);
|
||||
return ms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNanoseconds(String path) {
|
||||
Long ns = null;
|
||||
try {
|
||||
ns = TimeUnit.MILLISECONDS.toNanos(getLong(path));
|
||||
} catch (ConfigException.WrongType e) {
|
||||
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
||||
ns = Config.parseDuration((String) v.unwrapped(), v.origin(), path);
|
||||
}
|
||||
return ns;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> List<T> getHomogeneousUnwrappedList(String path,
|
||||
ConfigValueType expected) {
|
||||
List<T> l = new ArrayList<T>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue cv : list) {
|
||||
// variance would be nice, but stupid cast will do
|
||||
AbstractConfigValue v = (AbstractConfigValue) cv;
|
||||
if (expected != null) {
|
||||
v = DefaultTransformer.transform(v, expected);
|
||||
}
|
||||
if (v.valueType() != expected)
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
"list of " + expected.name(), "list of "
|
||||
+ v.valueType().name());
|
||||
l.add((T) v.unwrapped());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Boolean> getBooleanList(String path) {
|
||||
return getHomogeneousUnwrappedList(path, ConfigValueType.BOOLEAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Number> getNumberList(String path) {
|
||||
return getHomogeneousUnwrappedList(path, ConfigValueType.NUMBER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getIntList(String path) {
|
||||
List<Integer> l = new ArrayList<Integer>();
|
||||
List<Number> numbers = getNumberList(path);
|
||||
for (Number n : numbers) {
|
||||
l.add(n.intValue());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getLongList(String path) {
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
List<Number> numbers = getNumberList(path);
|
||||
for (Number n : numbers) {
|
||||
l.add(n.longValue());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Double> getDoubleList(String path) {
|
||||
List<Double> l = new ArrayList<Double>();
|
||||
List<Number> numbers = getNumberList(path);
|
||||
for (Number n : numbers) {
|
||||
l.add(n.doubleValue());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getStringList(String path) {
|
||||
return getHomogeneousUnwrappedList(path, ConfigValueType.STRING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigObject> getObjectList(String path) {
|
||||
List<ConfigObject> l = new ArrayList<ConfigObject>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue v : list) {
|
||||
if (v.valueType() != ConfigValueType.OBJECT)
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
ConfigValueType.OBJECT.name(), v.valueType().name());
|
||||
l.add((ConfigObject) v);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Object> getAnyRefList(String path) {
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue v : list) {
|
||||
l.add(v.unwrapped());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getMemorySizeInBytesList(String path) {
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue v : list) {
|
||||
if (v.valueType() == ConfigValueType.NUMBER) {
|
||||
l.add(((Number) v.unwrapped()).longValue());
|
||||
} else if (v.valueType() == ConfigValueType.STRING) {
|
||||
String s = (String) v.unwrapped();
|
||||
Long n = Config.parseMemorySizeInBytes(s, v.origin(), path);
|
||||
l.add(n);
|
||||
} else {
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
"memory size string or number of bytes", v.valueType()
|
||||
.name());
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getMillisecondsList(String path) {
|
||||
List<Long> nanos = getNanosecondsList(path);
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
for (Long n : nanos) {
|
||||
l.add(TimeUnit.NANOSECONDS.toMillis(n));
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getNanosecondsList(String path) {
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue v : list) {
|
||||
if (v.valueType() == ConfigValueType.NUMBER) {
|
||||
l.add(TimeUnit.MILLISECONDS.toNanos(((Number) v.unwrapped())
|
||||
.longValue()));
|
||||
} else if (v.valueType() == ConfigValueType.STRING) {
|
||||
String s = (String) v.unwrapped();
|
||||
Long n = Config.parseDuration(s, v.origin(), path);
|
||||
l.add(n);
|
||||
} else {
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
"duration string or number of nanoseconds", v
|
||||
.valueType().name());
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
@ -67,15 +68,23 @@ abstract class AbstractConfigValue implements ConfigValue {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue withFallback(ConfigValue other) {
|
||||
public AbstractConfigValue toValue() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue withFallbacks(ConfigValue... fallbacks) {
|
||||
public AbstractConfigValue withFallback(ConfigMergeable other) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue withFallbacks(ConfigMergeable... fallbacks) {
|
||||
// note: this is a no-op unless the subclass overrides withFallback().
|
||||
// But we need to do this because subclass withFallback() may not
|
||||
// just "return this"
|
||||
AbstractConfigValue merged = this;
|
||||
for (ConfigValue f : fallbacks) {
|
||||
merged = merged.withFallback(f);
|
||||
for (ConfigMergeable f : fallbacks) {
|
||||
merged = merged.withFallback(f.toValue());
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
@ -100,7 +101,9 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue withFallback(ConfigValue other) {
|
||||
public AbstractConfigValue withFallback(ConfigMergeable mergeable) {
|
||||
ConfigValue other = mergeable.toValue();
|
||||
|
||||
if (other instanceof AbstractConfigObject
|
||||
|| other instanceof Unmergeable) {
|
||||
// if we turn out to be an object, and the fallback also does,
|
||||
|
@ -7,6 +7,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
@ -30,59 +31,6 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
|
||||
"created a delayed merge object not guaranteed to be an object");
|
||||
}
|
||||
|
||||
final private static class Root extends ConfigDelayedMergeObject implements
|
||||
ConfigRootImpl {
|
||||
final private Path rootPath;
|
||||
|
||||
Root(ConfigDelayedMergeObject original, Path rootPath) {
|
||||
super(original.origin(), original.stack);
|
||||
this.rootPath = rootPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Root asRoot(Path newRootPath) {
|
||||
if (newRootPath.equals(this.rootPath))
|
||||
return this;
|
||||
else
|
||||
return new Root(this, newRootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRootImpl resolve() {
|
||||
return resolve(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRootImpl resolve(ConfigResolveOptions options) {
|
||||
return resolve(this, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Root withFallback(ConfigValue value) {
|
||||
return super.withFallback(value).asRoot(rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Root withFallbacks(ConfigValue... values) {
|
||||
return super.withFallbacks(values).asRoot(rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rootPath() {
|
||||
return rootPath.render();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path rootPathObject() {
|
||||
return rootPath;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Root asRoot(Path rootPath) {
|
||||
return new Root(this, rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigDelayedMergeObject newCopy(ResolveStatus status) {
|
||||
if (status != resolveStatus())
|
||||
@ -120,7 +68,9 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigDelayedMergeObject withFallback(ConfigValue other) {
|
||||
public ConfigDelayedMergeObject withFallback(ConfigMergeable mergeable) {
|
||||
ConfigValue other = mergeable.toValue();
|
||||
|
||||
if (other instanceof AbstractConfigObject
|
||||
|| other instanceof Unmergeable) {
|
||||
// since we are an object, and the fallback could be,
|
||||
@ -142,7 +92,7 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigDelayedMergeObject withFallbacks(ConfigValue... others) {
|
||||
public ConfigDelayedMergeObject withFallbacks(ConfigMergeable... others) {
|
||||
return (ConfigDelayedMergeObject) super.withFallbacks(others);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigIncludeContext;
|
||||
import com.typesafe.config.ConfigIncluder;
|
||||
@ -119,17 +120,21 @@ public class ConfigImpl {
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static ConfigRoot emptyRoot(String rootPath, String originDescription) {
|
||||
String desc = originDescription != null ? originDescription : rootPath;
|
||||
return SimpleConfigObject.empty(new SimpleConfigOrigin(desc))
|
||||
.asRoot(
|
||||
Parser.parsePath(rootPath));
|
||||
return emptyObject(desc).toConfig().asRoot(
|
||||
Path.newPath(rootPath));
|
||||
}
|
||||
|
||||
public static ConfigObject empty(String originDescription) {
|
||||
static AbstractConfigObject emptyObject(String originDescription) {
|
||||
ConfigOrigin origin = originDescription != null ? new SimpleConfigOrigin(
|
||||
originDescription) : null;
|
||||
return emptyObject(origin);
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static Config emptyConfig(String originDescription) {
|
||||
return emptyObject(originDescription).toConfig();
|
||||
}
|
||||
|
||||
static AbstractConfigObject empty(ConfigOrigin origin) {
|
||||
return emptyObject(origin);
|
||||
}
|
||||
@ -267,9 +272,10 @@ public class ConfigImpl {
|
||||
public static ConfigRoot systemPropertiesRoot(String rootPath) {
|
||||
Path path = Parser.parsePath(rootPath);
|
||||
try {
|
||||
return systemPropertiesAsConfig().getObject(rootPath).asRoot(path);
|
||||
return systemPropertiesAsConfigObject().toConfig().getConfig(rootPath)
|
||||
.asRoot(path);
|
||||
} catch (ConfigException.Missing e) {
|
||||
return SimpleConfigObject.empty().asRoot(path);
|
||||
return emptyObject("system properties").toConfig().asRoot(path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,8 +335,7 @@ public class ConfigImpl {
|
||||
|
||||
private static AbstractConfigObject systemProperties = null;
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public synchronized static AbstractConfigObject systemPropertiesAsConfig() {
|
||||
synchronized static AbstractConfigObject systemPropertiesAsConfigObject() {
|
||||
if (systemProperties == null) {
|
||||
systemProperties = loadSystemProperties();
|
||||
}
|
||||
@ -344,6 +349,11 @@ public class ConfigImpl {
|
||||
"system properties")).parse();
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static Config systemPropertiesAsConfig() {
|
||||
return systemPropertiesAsConfigObject().toConfig();
|
||||
}
|
||||
|
||||
// this is a hack to let us set system props in the test suite
|
||||
synchronized static void dropSystemPropertiesConfig() {
|
||||
systemProperties = null;
|
||||
@ -351,8 +361,7 @@ public class ConfigImpl {
|
||||
|
||||
private static AbstractConfigObject envVariables = null;
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public synchronized static AbstractConfigObject envVariablesAsConfig() {
|
||||
synchronized static AbstractConfigObject envVariablesAsConfigObject() {
|
||||
if (envVariables == null) {
|
||||
envVariables = loadEnvVariables();
|
||||
}
|
||||
@ -370,4 +379,9 @@ public class ConfigImpl {
|
||||
return new SimpleConfigObject(new SimpleConfigOrigin("env variables"),
|
||||
m, ResolveStatus.RESOLVED);
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static Config envVariablesAsConfig() {
|
||||
return envVariablesAsConfigObject().toConfig();
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import com.typesafe.config.ConfigRoot;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
interface ConfigRootImpl extends ConfigRoot {
|
||||
Path rootPathObject();
|
||||
|
||||
@Override
|
||||
ConfigRootImpl withFallbacks(ConfigValue... fallbacks);
|
||||
}
|
@ -6,6 +6,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
@ -51,7 +52,9 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue withFallback(ConfigValue other) {
|
||||
public AbstractConfigValue withFallback(ConfigMergeable mergeable) {
|
||||
ConfigValue other = mergeable.toValue();
|
||||
|
||||
if (other instanceof AbstractConfigObject
|
||||
|| other instanceof Unmergeable) {
|
||||
// if we turn out to be an object, and the fallback also does,
|
||||
@ -113,12 +116,12 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||
Path unprefixed = subst.subPath(prefixLength);
|
||||
|
||||
if (result == null && options.getUseSystemProperties()) {
|
||||
result = findInObject(ConfigImpl.systemPropertiesAsConfig(), null,
|
||||
result = findInObject(ConfigImpl.systemPropertiesAsConfigObject(), null,
|
||||
unprefixed, depth, options);
|
||||
}
|
||||
|
||||
if (result == null && options.getUseSystemEnvironment()) {
|
||||
result = findInObject(ConfigImpl.envVariablesAsConfig(), null,
|
||||
result = findInObject(ConfigImpl.envVariablesAsConfigObject(), null,
|
||||
unprefixed, depth, options);
|
||||
}
|
||||
|
||||
|
@ -1,92 +0,0 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
abstract class DelegatingConfigObject extends AbstractConfigObject {
|
||||
final private AbstractConfigObject underlying;
|
||||
|
||||
DelegatingConfigObject(AbstractConfigObject underlying) {
|
||||
super(underlying.origin());
|
||||
this.underlying = underlying;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DelegatingConfigObject newCopy(ResolveStatus newStatus) {
|
||||
return newCopy(underlying, newStatus);
|
||||
}
|
||||
|
||||
abstract DelegatingConfigObject newCopy(AbstractConfigObject underlying,
|
||||
ResolveStatus newStatus);
|
||||
|
||||
@Override
|
||||
ResolveStatus resolveStatus() {
|
||||
return underlying.resolveStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
final protected ConfigRootImpl asRoot(Path rootPath) {
|
||||
return asRoot(underlying, rootPath);
|
||||
}
|
||||
|
||||
protected abstract ConfigRootImpl asRoot(AbstractConfigObject underlying,
|
||||
Path rootPath);
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return underlying.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return underlying.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> unwrapped() {
|
||||
return underlying.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue peek(String key) {
|
||||
return underlying.peek(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject withFallback(ConfigValue value) {
|
||||
return underlying.withFallback(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject withFallbacks(ConfigValue... values) {
|
||||
return underlying.withFallbacks(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return underlying.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<java.util.Map.Entry<String, ConfigValue>> entrySet() {
|
||||
return underlying.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return underlying.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return underlying.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigValue> values() {
|
||||
return underlying.values();
|
||||
}
|
||||
}
|
64
src/main/java/com/typesafe/config/impl/RootConfig.java
Normal file
64
src/main/java/com/typesafe/config/impl/RootConfig.java
Normal file
@ -0,0 +1,64 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
import com.typesafe.config.ConfigRoot;
|
||||
|
||||
final class RootConfig extends SimpleConfig implements ConfigRoot {
|
||||
|
||||
final private Path rootPath;
|
||||
|
||||
RootConfig(AbstractConfigObject underlying, Path rootPath) {
|
||||
super(underlying);
|
||||
this.rootPath = rootPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RootConfig asRoot(AbstractConfigObject underlying,
|
||||
Path newRootPath) {
|
||||
if (newRootPath.equals(this.rootPath))
|
||||
return this;
|
||||
else
|
||||
return new RootConfig(underlying, newRootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootConfig resolve() {
|
||||
return resolve(ConfigResolveOptions.defaults());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootConfig resolve(ConfigResolveOptions options) {
|
||||
// if the object is already resolved then we should end up returning
|
||||
// "this" here, since asRoot() should return this if the path
|
||||
// is unchanged.
|
||||
SimpleConfig resolved = resolvedObject(options).toConfig();
|
||||
return resolved.asRoot(rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootConfig withFallback(ConfigMergeable value) {
|
||||
// this can return "this" if the withFallback does nothing
|
||||
return super.withFallback(value).asRoot(rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootConfig withFallbacks(ConfigMergeable... values) {
|
||||
// this can return "this" if the withFallbacks does nothing
|
||||
return super.withFallbacks(values).asRoot(rootPath);
|
||||
}
|
||||
|
||||
Path rootPathObject() {
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rootPath() {
|
||||
return rootPath.render();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Root" + super.toString();
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
final class RootConfigObject extends DelegatingConfigObject implements
|
||||
ConfigRootImpl {
|
||||
|
||||
final private Path rootPath;
|
||||
|
||||
RootConfigObject(AbstractConfigObject underlying, Path rootPath) {
|
||||
super(underlying);
|
||||
this.rootPath = rootPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigRootImpl asRoot(AbstractConfigObject underlying,
|
||||
Path newRootPath) {
|
||||
if (newRootPath.equals(this.rootPath))
|
||||
return this;
|
||||
else
|
||||
return new RootConfigObject(underlying, newRootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootConfigObject newCopy(AbstractConfigObject underlying,
|
||||
ResolveStatus newStatus) {
|
||||
return new RootConfigObject(underlying.newCopy(newStatus), rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRootImpl resolve() {
|
||||
return resolve(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRootImpl resolve(ConfigResolveOptions options) {
|
||||
return resolve(this, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootConfigObject withFallback(ConfigValue value) {
|
||||
return new RootConfigObject(super.withFallback(value), rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootConfigObject withFallbacks(ConfigValue... values) {
|
||||
return new RootConfigObject(super.withFallbacks(values), rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path rootPathObject() {
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rootPath() {
|
||||
return rootPath.render();
|
||||
}
|
||||
}
|
589
src/main/java/com/typesafe/config/impl/SimpleConfig.java
Normal file
589
src/main/java/com/typesafe/config/impl/SimpleConfig.java
Normal file
@ -0,0 +1,589 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigList;
|
||||
import com.typesafe.config.ConfigMergeable;
|
||||
import com.typesafe.config.ConfigObject;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigResolveOptions;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
import com.typesafe.config.ConfigValueType;
|
||||
|
||||
/**
|
||||
* One thing to keep in mind in the future: if any Collection-like APIs are
|
||||
* added here, including iterators or size() or anything, then we'd have to
|
||||
* grapple with whether ConfigNull values are "in" the Config (probably not) and
|
||||
* we'd probably want to make the collection look flat - not like a tree. So the
|
||||
* key-value pairs would be all the tree's leaf values, in a big flat list with
|
||||
* their full paths.
|
||||
*/
|
||||
class SimpleConfig implements Config {
|
||||
|
||||
AbstractConfigObject object;
|
||||
|
||||
SimpleConfig(AbstractConfigObject object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject toObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigOrigin origin() {
|
||||
return object.origin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of this config that implements the ConfigRoot
|
||||
* interface.
|
||||
*
|
||||
* @return a config root
|
||||
*/
|
||||
RootConfig asRoot(Path rootPath) {
|
||||
return asRoot(object, rootPath);
|
||||
}
|
||||
|
||||
// RootConfig overrides this to avoid a new object on unchanged path.
|
||||
protected RootConfig asRoot(AbstractConfigObject underlying,
|
||||
Path newRootPath) {
|
||||
return new RootConfig(underlying, newRootPath);
|
||||
}
|
||||
|
||||
protected AbstractConfigObject resolvedObject(ConfigResolveOptions options) {
|
||||
AbstractConfigValue resolved = SubstitutionResolver.resolve(object,
|
||||
object, options);
|
||||
return (AbstractConfigObject) resolved;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPath(String pathExpression) {
|
||||
Path path = Path.newPath(pathExpression);
|
||||
ConfigValue peeked = object.peekPath(path, null, 0, null);
|
||||
return peeked != null && peeked.valueType() != ConfigValueType.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return object.isEmpty();
|
||||
}
|
||||
|
||||
static private AbstractConfigValue find(AbstractConfigObject self,
|
||||
String pathExpression, ConfigValueType expected, String originalPath) {
|
||||
Path path = Path.newPath(pathExpression);
|
||||
return find(self, path, expected, originalPath);
|
||||
}
|
||||
|
||||
static private AbstractConfigValue findKey(AbstractConfigObject self,
|
||||
String key, ConfigValueType expected, String originalPath) {
|
||||
AbstractConfigValue v = self.peek(key);
|
||||
if (v == null)
|
||||
throw new ConfigException.Missing(originalPath);
|
||||
|
||||
if (expected != null)
|
||||
v = DefaultTransformer.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 find(AbstractConfigObject self,
|
||||
Path path, ConfigValueType expected, String originalPath) {
|
||||
String key = path.first();
|
||||
Path next = path.remainder();
|
||||
if (next == null) {
|
||||
return findKey(self, key, expected, originalPath);
|
||||
} else {
|
||||
AbstractConfigObject o = (AbstractConfigObject) findKey(self, key,
|
||||
ConfigValueType.OBJECT, originalPath);
|
||||
assert (o != null); // missing was supposed to throw
|
||||
return find(o, next, expected, originalPath);
|
||||
}
|
||||
}
|
||||
|
||||
AbstractConfigValue find(String pathExpression, ConfigValueType expected,
|
||||
String originalPath) {
|
||||
return find(object, pathExpression, expected, originalPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue getValue(String path) {
|
||||
return find(path, null, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String path) {
|
||||
ConfigValue v = find(path, ConfigValueType.BOOLEAN, path);
|
||||
return (Boolean) v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number getNumber(String path) {
|
||||
ConfigValue v = find(path, ConfigValueType.NUMBER, path);
|
||||
return (Number) v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String path) {
|
||||
return getNumber(path).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String path) {
|
||||
return getNumber(path).longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(String path) {
|
||||
return getNumber(path).doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String path) {
|
||||
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
||||
return (String) v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigList getList(String path) {
|
||||
AbstractConfigValue v = find(path, ConfigValueType.LIST, path);
|
||||
return (ConfigList) v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject getObject(String path) {
|
||||
AbstractConfigObject obj = (AbstractConfigObject) find(path,
|
||||
ConfigValueType.OBJECT, path);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleConfig getConfig(String path) {
|
||||
return getObject(path).toConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAnyRef(String path) {
|
||||
ConfigValue v = find(path, null, path);
|
||||
return v.unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getMemorySizeInBytes(String path) {
|
||||
Long size = null;
|
||||
try {
|
||||
size = getLong(path);
|
||||
} catch (ConfigException.WrongType e) {
|
||||
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
||||
size = parseMemorySizeInBytes((String) v.unwrapped(),
|
||||
v.origin(), path);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getMilliseconds(String path) {
|
||||
long ns = getNanoseconds(path);
|
||||
long ms = TimeUnit.NANOSECONDS.toMillis(ns);
|
||||
return ms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNanoseconds(String path) {
|
||||
Long ns = null;
|
||||
try {
|
||||
ns = TimeUnit.MILLISECONDS.toNanos(getLong(path));
|
||||
} catch (ConfigException.WrongType e) {
|
||||
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
||||
ns = parseDuration((String) v.unwrapped(), v.origin(), path);
|
||||
}
|
||||
return ns;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> List<T> getHomogeneousUnwrappedList(String path,
|
||||
ConfigValueType expected) {
|
||||
List<T> l = new ArrayList<T>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue cv : list) {
|
||||
// variance would be nice, but stupid cast will do
|
||||
AbstractConfigValue v = (AbstractConfigValue) cv;
|
||||
if (expected != null) {
|
||||
v = DefaultTransformer.transform(v, expected);
|
||||
}
|
||||
if (v.valueType() != expected)
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
"list of " + expected.name(), "list of "
|
||||
+ v.valueType().name());
|
||||
l.add((T) v.unwrapped());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Boolean> getBooleanList(String path) {
|
||||
return getHomogeneousUnwrappedList(path, ConfigValueType.BOOLEAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Number> getNumberList(String path) {
|
||||
return getHomogeneousUnwrappedList(path, ConfigValueType.NUMBER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getIntList(String path) {
|
||||
List<Integer> l = new ArrayList<Integer>();
|
||||
List<Number> numbers = getNumberList(path);
|
||||
for (Number n : numbers) {
|
||||
l.add(n.intValue());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getLongList(String path) {
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
List<Number> numbers = getNumberList(path);
|
||||
for (Number n : numbers) {
|
||||
l.add(n.longValue());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Double> getDoubleList(String path) {
|
||||
List<Double> l = new ArrayList<Double>();
|
||||
List<Number> numbers = getNumberList(path);
|
||||
for (Number n : numbers) {
|
||||
l.add(n.doubleValue());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getStringList(String path) {
|
||||
return getHomogeneousUnwrappedList(path, ConfigValueType.STRING);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends ConfigValue> List<T> getHomogeneousWrappedList(
|
||||
String path, ConfigValueType expected) {
|
||||
List<T> l = new ArrayList<T>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue cv : list) {
|
||||
// variance would be nice, but stupid cast will do
|
||||
AbstractConfigValue v = (AbstractConfigValue) cv;
|
||||
if (expected != null) {
|
||||
v = DefaultTransformer.transform(v, expected);
|
||||
}
|
||||
if (v.valueType() != expected)
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
"list of " + expected.name(), "list of "
|
||||
+ v.valueType().name());
|
||||
l.add((T) v);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigObject> getObjectList(String path) {
|
||||
return getHomogeneousWrappedList(path, ConfigValueType.OBJECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Config> getConfigList(String path) {
|
||||
List<ConfigObject> objects = getObjectList(path);
|
||||
List<Config> l = new ArrayList<Config>();
|
||||
for (ConfigObject o : objects) {
|
||||
l.add(o.toConfig());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Object> getAnyRefList(String path) {
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue v : list) {
|
||||
l.add(v.unwrapped());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getMemorySizeInBytesList(String path) {
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue v : list) {
|
||||
if (v.valueType() == ConfigValueType.NUMBER) {
|
||||
l.add(((Number) v.unwrapped()).longValue());
|
||||
} else if (v.valueType() == ConfigValueType.STRING) {
|
||||
String s = (String) v.unwrapped();
|
||||
Long n = parseMemorySizeInBytes(s, v.origin(), path);
|
||||
l.add(n);
|
||||
} else {
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
"memory size string or number of bytes", v.valueType()
|
||||
.name());
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getMillisecondsList(String path) {
|
||||
List<Long> nanos = getNanosecondsList(path);
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
for (Long n : nanos) {
|
||||
l.add(TimeUnit.NANOSECONDS.toMillis(n));
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getNanosecondsList(String path) {
|
||||
List<Long> l = new ArrayList<Long>();
|
||||
List<? extends ConfigValue> list = getList(path);
|
||||
for (ConfigValue v : list) {
|
||||
if (v.valueType() == ConfigValueType.NUMBER) {
|
||||
l.add(TimeUnit.MILLISECONDS.toNanos(((Number) v.unwrapped())
|
||||
.longValue()));
|
||||
} else if (v.valueType() == ConfigValueType.STRING) {
|
||||
String s = (String) v.unwrapped();
|
||||
Long n = parseDuration(s, v.origin(), path);
|
||||
l.add(n);
|
||||
} else {
|
||||
throw new ConfigException.WrongType(v.origin(), path,
|
||||
"duration string or number of nanoseconds", v
|
||||
.valueType().name());
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigObject toValue() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleConfig withFallback(ConfigMergeable other) {
|
||||
// this can return "this" if the withFallback doesn't need a new
|
||||
// ConfigObject
|
||||
return object.withFallback(other).toConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleConfig withFallbacks(ConfigMergeable... others) {
|
||||
// this can return "this" if the withFallbacks doesn't need a new
|
||||
// ConfigObject
|
||||
return object.withFallbacks(others).toConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object other) {
|
||||
if (other instanceof SimpleConfig) {
|
||||
return object.equals(((SimpleConfig) other).object);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
// we do the "41*" just so our hash code won't match that of the
|
||||
// underlying object. there's no real reason it can't match, but
|
||||
// making it not match might catch some kinds of bug.
|
||||
return 41 * object.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Config(" + object.toString() + ")";
|
||||
}
|
||||
|
||||
private static String getUnits(String s) {
|
||||
int i = s.length() - 1;
|
||||
while (i >= 0) {
|
||||
char c = s.charAt(i);
|
||||
if (!Character.isLetter(c))
|
||||
break;
|
||||
i -= 1;
|
||||
}
|
||||
return s.substring(i + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a duration string. If no units are specified in the string, it is
|
||||
* assumed to be in milliseconds. The returned duration is in nanoseconds.
|
||||
* The purpose of this function is to implement the duration-related methods
|
||||
* in the ConfigObject interface.
|
||||
*
|
||||
* @param input
|
||||
* the string to parse
|
||||
* @param originForException
|
||||
* origin of the value being parsed
|
||||
* @param pathForException
|
||||
* path to include in exceptions
|
||||
* @return duration in nanoseconds
|
||||
* @throws ConfigException
|
||||
* if string is invalid
|
||||
*/
|
||||
public static long parseDuration(String input,
|
||||
ConfigOrigin originForException, String pathForException) {
|
||||
String s = ConfigUtil.unicodeTrim(input);
|
||||
String originalUnitString = getUnits(s);
|
||||
String unitString = originalUnitString;
|
||||
String numberString = ConfigUtil.unicodeTrim(s.substring(0, s.length()
|
||||
- unitString.length()));
|
||||
TimeUnit units = null;
|
||||
|
||||
// this would be caught later anyway, but the error message
|
||||
// is more helpful if we check it here.
|
||||
if (numberString.length() == 0)
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "No number in duration value '" + input
|
||||
+ "'");
|
||||
|
||||
if (unitString.length() > 2 && !unitString.endsWith("s"))
|
||||
unitString = unitString + "s";
|
||||
|
||||
// note that this is deliberately case-sensitive
|
||||
if (unitString.equals("") || unitString.equals("ms")
|
||||
|| unitString.equals("milliseconds")) {
|
||||
units = TimeUnit.MILLISECONDS;
|
||||
} else if (unitString.equals("us") || unitString.equals("microseconds")) {
|
||||
units = TimeUnit.MICROSECONDS;
|
||||
} else if (unitString.equals("ns") || unitString.equals("nanoseconds")) {
|
||||
units = TimeUnit.NANOSECONDS;
|
||||
} else if (unitString.equals("d") || unitString.equals("days")) {
|
||||
units = TimeUnit.DAYS;
|
||||
} else if (unitString.equals("h") || unitString.equals("hours")) {
|
||||
units = TimeUnit.HOURS;
|
||||
} else if (unitString.equals("s") || unitString.equals("seconds")) {
|
||||
units = TimeUnit.SECONDS;
|
||||
} else if (unitString.equals("m") || unitString.equals("minutes")) {
|
||||
units = TimeUnit.MINUTES;
|
||||
} else {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse time unit '"
|
||||
+ originalUnitString
|
||||
+ "' (try ns, us, ms, s, m, d)");
|
||||
}
|
||||
|
||||
try {
|
||||
// if the string is purely digits, parse as an integer to avoid
|
||||
// possible precision loss;
|
||||
// otherwise as a double.
|
||||
if (numberString.matches("[0-9]+")) {
|
||||
return units.toNanos(Long.parseLong(numberString));
|
||||
} else {
|
||||
long nanosInUnit = units.toNanos(1);
|
||||
return (long) (Double.parseDouble(numberString) * nanosInUnit);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse duration number '"
|
||||
+ numberString + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private static enum MemoryUnit {
|
||||
BYTES(1), KILOBYTES(1024), MEGABYTES(1024 * 1024), GIGABYTES(
|
||||
1024 * 1024 * 1024), TERABYTES(1024 * 1024 * 1024 * 1024);
|
||||
|
||||
int bytes;
|
||||
|
||||
MemoryUnit(int bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a memory-size string. If no units are specified in the string, it
|
||||
* is assumed to be in bytes. The returned value is in bytes. The purpose of
|
||||
* this function is to implement the memory-size-related methods in the
|
||||
* ConfigObject interface. The units parsed are interpreted as powers of
|
||||
* two, that is, the convention for memory rather than the convention for
|
||||
* disk space.
|
||||
*
|
||||
* @param input
|
||||
* the string to parse
|
||||
* @param originForException
|
||||
* origin of the value being parsed
|
||||
* @param pathForException
|
||||
* path to include in exceptions
|
||||
* @return size in bytes
|
||||
* @throws ConfigException
|
||||
* if string is invalid
|
||||
*/
|
||||
public static long parseMemorySizeInBytes(String input,
|
||||
ConfigOrigin originForException, String pathForException) {
|
||||
String s = ConfigUtil.unicodeTrim(input);
|
||||
String unitStringMaybePlural = getUnits(s);
|
||||
String unitString;
|
||||
if (unitStringMaybePlural.endsWith("s"))
|
||||
unitString = unitStringMaybePlural.substring(0,
|
||||
unitStringMaybePlural.length() - 1);
|
||||
else
|
||||
unitString = unitStringMaybePlural;
|
||||
String unitStringLower = unitString.toLowerCase();
|
||||
String numberString = ConfigUtil.unicodeTrim(s.substring(0, s.length()
|
||||
- unitStringMaybePlural.length()));
|
||||
|
||||
// this would be caught later anyway, but the error message
|
||||
// is more helpful if we check it here.
|
||||
if (numberString.length() == 0)
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "No number in size-in-bytes value '"
|
||||
+ input + "'");
|
||||
|
||||
MemoryUnit units = null;
|
||||
|
||||
// the short abbreviations are case-insensitive but you can't write the
|
||||
// long form words in all caps.
|
||||
if (unitString.equals("") || unitStringLower.equals("b")
|
||||
|| unitString.equals("byte")) {
|
||||
units = MemoryUnit.BYTES;
|
||||
} else if (unitStringLower.equals("k") || unitString.equals("kilobyte")) {
|
||||
units = MemoryUnit.KILOBYTES;
|
||||
} else if (unitStringLower.equals("m") || unitString.equals("megabyte")) {
|
||||
units = MemoryUnit.MEGABYTES;
|
||||
} else if (unitStringLower.equals("g") || unitString.equals("gigabyte")) {
|
||||
units = MemoryUnit.GIGABYTES;
|
||||
} else if (unitStringLower.equals("t") || unitString.equals("terabyte")) {
|
||||
units = MemoryUnit.TERABYTES;
|
||||
} else {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse size unit '"
|
||||
+ unitStringMaybePlural + "' (try b, k, m, g, t)");
|
||||
}
|
||||
|
||||
try {
|
||||
// if the string is purely digits, parse as an integer to avoid
|
||||
// possible precision loss;
|
||||
// otherwise as a double.
|
||||
if (numberString.matches("[0-9]+")) {
|
||||
return Long.parseLong(numberString) * units.bytes;
|
||||
} else {
|
||||
return (long) (Double.parseDouble(numberString) * units.bytes);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ConfigException.BadValue(originForException,
|
||||
pathForException, "Could not parse memory size number '"
|
||||
+ numberString + "'");
|
||||
}
|
||||
}
|
||||
}
|
@ -12,31 +12,45 @@ import scala.collection.mutable
|
||||
class ApiExamples {
|
||||
@Test
|
||||
def readSomeConfig() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
// you don't have to write the types explicitly of course,
|
||||
// just doing that to show what they are.
|
||||
val a: Int = conf.getInt("ints.fortyTwo")
|
||||
val obj: ConfigObject = conf.getObject("ints")
|
||||
val c: Int = obj.getInt("fortyTwo")
|
||||
val child: Config = conf.getConfig("ints")
|
||||
val b: Int = child.getInt("fortyTwo")
|
||||
val ms: Long = conf.getMilliseconds("durations.halfSecond")
|
||||
|
||||
// a Config has an associated tree of values, with a ConfigObject
|
||||
// at the root. The ConfigObject implements java.util.Map
|
||||
val obj: ConfigObject = conf.toObject
|
||||
|
||||
// this is how you do conf.getInt "manually" on the value tree, if you
|
||||
// were so inclined. (This is not a good approach vs. conf.getInt() above,
|
||||
// just showing how ConfigObject stores a tree of values.)
|
||||
val c: Int = obj.get("ints")
|
||||
.asInstanceOf[ConfigObject]
|
||||
.get("fortyTwo").unwrapped()
|
||||
.asInstanceOf[java.lang.Integer]
|
||||
|
||||
// this is unfortunate; asScala creates a mutable map but
|
||||
// the ConfigObject is in fact immutable.
|
||||
// a ConfigObject is in fact immutable.
|
||||
val objAsScalaMap: mutable.Map[String, ConfigValue] = obj.asScala
|
||||
|
||||
// this is sort of ugly...
|
||||
// this is sort of ugly, but you could improve it
|
||||
// with a Scala implicit wrapper, see below...
|
||||
val d: Int = conf.getAnyRef("ints.fortyTwo") match {
|
||||
case x: java.lang.Integer => x
|
||||
case x: java.lang.Long => x.intValue
|
||||
}
|
||||
|
||||
class EnhancedConfigObject(obj: ConfigObject) {
|
||||
def getAny(path: String): Any = obj.getAnyRef(path)
|
||||
// our implicit wrapper: do stuff like this to get a nicer Scala API
|
||||
class EnhancedConfig(c: Config) {
|
||||
def getAny(path: String): Any = c.getAnyRef(path)
|
||||
}
|
||||
implicit def obj2enhanced(obj: ConfigObject) = new EnhancedConfigObject(obj)
|
||||
implicit def config2enhanced(c: Config) = new EnhancedConfig(c)
|
||||
|
||||
// somewhat nicer
|
||||
// somewhat nicer now
|
||||
val e: Int = conf.getAny("ints.fortyTwo") match {
|
||||
case x: Int => x
|
||||
case x: Long => x.intValue
|
||||
|
@ -1,4 +1,5 @@
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
object Util {
|
||||
def time(body: () => Unit, iterations: Int): Long = {
|
||||
// warm up
|
||||
@ -26,7 +27,7 @@ object Util {
|
||||
|
||||
object FileLoad extends App {
|
||||
def task() {
|
||||
val conf = Config.load("test04")
|
||||
val conf = ConfigFactory.load("test04")
|
||||
if (!"2.0-SNAPSHOT".equals(conf.getString("akka.version"))) {
|
||||
throw new Exception("broken file load")
|
||||
}
|
||||
@ -39,7 +40,7 @@ object FileLoad extends App {
|
||||
}
|
||||
|
||||
object Resolve extends App {
|
||||
val conf = Config.load("test02")
|
||||
val conf = ConfigFactory.load("test02")
|
||||
|
||||
def task() {
|
||||
conf.resolve()
|
||||
|
@ -153,17 +153,17 @@ class ConfParserTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def duplicateKeyLastWins() {
|
||||
val obj = parseObject("""{ "a" : 10, "a" : 11 } """)
|
||||
val obj = parseConfig("""{ "a" : 10, "a" : 11 } """)
|
||||
|
||||
assertEquals(1, obj.size())
|
||||
assertEquals(1, obj.toObject.size())
|
||||
assertEquals(11, obj.getInt("a"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def duplicateKeyObjectsMerged() {
|
||||
val obj = parseObject("""{ "a" : { "x" : 1, "y" : 2 }, "a" : { "x" : 42, "z" : 100 } }""")
|
||||
val obj = parseConfig("""{ "a" : { "x" : 1, "y" : 2 }, "a" : { "x" : 42, "z" : 100 } }""")
|
||||
|
||||
assertEquals(1, obj.size())
|
||||
assertEquals(1, obj.toObject.size())
|
||||
assertEquals(3, obj.getObject("a").size())
|
||||
assertEquals(42, obj.getInt("a.x"))
|
||||
assertEquals(2, obj.getInt("a.y"))
|
||||
@ -172,9 +172,9 @@ class ConfParserTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def duplicateKeyObjectsMergedRecursively() {
|
||||
val obj = parseObject("""{ "a" : { "b" : { "x" : 1, "y" : 2 } }, "a" : { "b" : { "x" : 42, "z" : 100 } } }""")
|
||||
val obj = parseConfig("""{ "a" : { "b" : { "x" : 1, "y" : 2 } }, "a" : { "b" : { "x" : 42, "z" : 100 } } }""")
|
||||
|
||||
assertEquals(1, obj.size())
|
||||
assertEquals(1, obj.toObject.size())
|
||||
assertEquals(1, obj.getObject("a").size())
|
||||
assertEquals(3, obj.getObject("a.b").size())
|
||||
assertEquals(42, obj.getInt("a.b.x"))
|
||||
@ -184,9 +184,9 @@ class ConfParserTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def duplicateKeyObjectsMergedRecursivelyDeeper() {
|
||||
val obj = parseObject("""{ "a" : { "b" : { "c" : { "x" : 1, "y" : 2 } } }, "a" : { "b" : { "c" : { "x" : 42, "z" : 100 } } } }""")
|
||||
val obj = parseConfig("""{ "a" : { "b" : { "c" : { "x" : 1, "y" : 2 } } }, "a" : { "b" : { "c" : { "x" : 42, "z" : 100 } } } }""")
|
||||
|
||||
assertEquals(1, obj.size())
|
||||
assertEquals(1, obj.toObject.size())
|
||||
assertEquals(1, obj.getObject("a").size())
|
||||
assertEquals(1, obj.getObject("a.b").size())
|
||||
assertEquals(3, obj.getObject("a.b.c").size())
|
||||
@ -198,16 +198,16 @@ class ConfParserTest extends TestUtils {
|
||||
@Test
|
||||
def duplicateKeyObjectNullObject() {
|
||||
// null is supposed to "reset" the object at key "a"
|
||||
val obj = parseObject("""{ a : { b : 1 }, a : null, a : { c : 2 } }""")
|
||||
assertEquals(1, obj.size())
|
||||
val obj = parseConfig("""{ a : { b : 1 }, a : null, a : { c : 2 } }""")
|
||||
assertEquals(1, obj.toObject.size())
|
||||
assertEquals(1, obj.getObject("a").size())
|
||||
assertEquals(2, obj.getInt("a.c"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def duplicateKeyObjectNumberObject() {
|
||||
val obj = parseObject("""{ a : { b : 1 }, a : 42, a : { c : 2 } }""")
|
||||
assertEquals(1, obj.size())
|
||||
val obj = parseConfig("""{ a : { b : 1 }, a : 42, a : { c : 2 } }""")
|
||||
assertEquals(1, obj.toObject.size())
|
||||
assertEquals(1, obj.getObject("a").size())
|
||||
assertEquals(2, obj.getInt("a.c"))
|
||||
}
|
||||
@ -263,8 +263,8 @@ class ConfParserTest extends TestUtils {
|
||||
var tested = 0;
|
||||
for (v <- valids; change <- changes) {
|
||||
tested += 1;
|
||||
val obj = parseObject(change(v))
|
||||
assertEquals(3, obj.size())
|
||||
val obj = parseConfig(change(v))
|
||||
assertEquals(3, obj.toObject.size())
|
||||
assertEquals("y", obj.getString("a"))
|
||||
assertEquals("z", obj.getString("b"))
|
||||
assertEquals(Seq(1, 2, 3), obj.getIntList("c").asScala)
|
||||
@ -273,37 +273,37 @@ class ConfParserTest extends TestUtils {
|
||||
assertEquals(valids.length * changes.length, tested)
|
||||
|
||||
// with no newline or comma, we do value concatenation
|
||||
val noNewlineInArray = parseObject(" { c : [ 1 2 3 ] } ")
|
||||
val noNewlineInArray = parseConfig(" { c : [ 1 2 3 ] } ")
|
||||
assertEquals(Seq("1 2 3"), noNewlineInArray.getStringList("c").asScala)
|
||||
|
||||
val noNewlineInArrayWithQuoted = parseObject(""" { c : [ "4" "5" "6" ] } """)
|
||||
val noNewlineInArrayWithQuoted = parseConfig(""" { c : [ "4" "5" "6" ] } """)
|
||||
assertEquals(Seq("4 5 6"), noNewlineInArrayWithQuoted.getStringList("c").asScala)
|
||||
|
||||
val noNewlineInObject = parseObject(" { a : b c } ")
|
||||
val noNewlineInObject = parseConfig(" { a : b c } ")
|
||||
assertEquals("b c", noNewlineInObject.getString("a"))
|
||||
|
||||
val noNewlineAtEnd = parseObject("a : b")
|
||||
val noNewlineAtEnd = parseConfig("a : b")
|
||||
assertEquals("b", noNewlineAtEnd.getString("a"))
|
||||
|
||||
intercept[ConfigException] {
|
||||
parseObject("{ a : y b : z }")
|
||||
parseConfig("{ a : y b : z }")
|
||||
}
|
||||
|
||||
intercept[ConfigException] {
|
||||
parseObject("""{ "a" : "y" "b" : "z" }""")
|
||||
parseConfig("""{ "a" : "y" "b" : "z" }""")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def keysWithSlash() {
|
||||
val obj = parseObject("""/a/b/c=42, x/y/z : 32""")
|
||||
val obj = parseConfig("""/a/b/c=42, x/y/z : 32""")
|
||||
assertEquals(42, obj.getInt("/a/b/c"))
|
||||
assertEquals(32, obj.getInt("x/y/z"))
|
||||
}
|
||||
|
||||
private def lineNumberTest(num: Int, text: String) {
|
||||
val e = intercept[ConfigException] {
|
||||
parseObject(text)
|
||||
parseConfig(text)
|
||||
}
|
||||
if (!e.getMessage.contains(num + ":"))
|
||||
throw new Exception("error message did not contain line '" + num + "' '" + text.replace("\n", "\\n") + "'", e)
|
||||
|
@ -6,12 +6,13 @@ import com.typesafe.config.ConfigValue
|
||||
import com.typesafe.config.ConfigException
|
||||
import com.typesafe.config.ConfigResolveOptions
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
class ConfigSubstitutionTest extends TestUtils {
|
||||
|
||||
private def resolveWithoutFallbacks(v: AbstractConfigObject) = {
|
||||
val options = ConfigResolveOptions.noSystem()
|
||||
SubstitutionResolver.resolve(v, v, options).asInstanceOf[AbstractConfigObject]
|
||||
SubstitutionResolver.resolve(v, v, options).asInstanceOf[AbstractConfigObject].toConfig
|
||||
}
|
||||
private def resolveWithoutFallbacks(s: ConfigSubstitution, root: AbstractConfigObject) = {
|
||||
val options = ConfigResolveOptions.noSystem()
|
||||
@ -20,7 +21,7 @@ class ConfigSubstitutionTest extends TestUtils {
|
||||
|
||||
private def resolve(v: AbstractConfigObject) = {
|
||||
val options = ConfigResolveOptions.defaults()
|
||||
SubstitutionResolver.resolve(v, v, options).asInstanceOf[AbstractConfigObject]
|
||||
SubstitutionResolver.resolve(v, v, options).asInstanceOf[AbstractConfigObject].toConfig
|
||||
}
|
||||
private def resolve(s: ConfigSubstitution, root: AbstractConfigObject) = {
|
||||
val options = ConfigResolveOptions.defaults()
|
||||
@ -106,7 +107,7 @@ class ConfigSubstitutionTest extends TestUtils {
|
||||
assertEquals(stringValue("start<>end"), v)
|
||||
|
||||
// but when null is NOT a subst, it should not become empty, incidentally
|
||||
val o = parseObject("""{ "a" : null foo bar }""")
|
||||
val o = parseConfig("""{ "a" : null foo bar }""")
|
||||
assertEquals("null foo bar", o.getString("a"))
|
||||
}
|
||||
|
||||
@ -281,13 +282,13 @@ class ConfigSubstitutionTest extends TestUtils {
|
||||
val resolved = resolve(substEnvVarObject)
|
||||
|
||||
var existed = 0
|
||||
for (k <- resolved.keySet().asScala) {
|
||||
for (k <- resolved.toObject.keySet().asScala) {
|
||||
val e = System.getenv(k.toUpperCase());
|
||||
if (e != null) {
|
||||
existed += 1
|
||||
assertEquals(e, resolved.getString(k))
|
||||
} else {
|
||||
assertEquals(nullValue, resolved.get(k))
|
||||
assertEquals(nullValue, resolved.toObject.get(k))
|
||||
}
|
||||
}
|
||||
if (existed == 0) {
|
||||
@ -307,13 +308,13 @@ class ConfigSubstitutionTest extends TestUtils {
|
||||
for (k <- substEnvVarObject.keySet().asScala) {
|
||||
nullsMap.put(k.toUpperCase(), null);
|
||||
}
|
||||
val nulls = Config.fromMap(nullsMap, "nulls map")
|
||||
val nulls = ConfigFactory.parseMap(nullsMap, "nulls map")
|
||||
|
||||
val resolved = resolve(substEnvVarObject.withFallback(nulls))
|
||||
|
||||
for (k <- resolved.keySet().asScala) {
|
||||
assertNotNull(resolved.get(k))
|
||||
assertEquals(nullValue, resolved.get(k))
|
||||
for (k <- resolved.toObject.keySet().asScala) {
|
||||
assertNotNull(resolved.toObject.get(k))
|
||||
assertEquals(nullValue, resolved.toObject.get(k))
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +333,7 @@ class ConfigSubstitutionTest extends TestUtils {
|
||||
val e = System.getenv(k.toUpperCase());
|
||||
if (e != null) {
|
||||
existed += 1
|
||||
assertEquals(e, resolved.getObject("a").getString(k))
|
||||
assertEquals(e, resolved.getConfig("a").getString(k))
|
||||
} else {
|
||||
assertEquals(nullValue, resolved.getObject("a").get(k))
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import scala.collection.JavaConverters._
|
||||
import com.typesafe.config.ConfigResolveOptions
|
||||
import java.io.File
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
class ConfigTest extends TestUtils {
|
||||
|
||||
@ -23,7 +24,7 @@ class ConfigTest extends TestUtils {
|
||||
// a non-object value follows, and then an object after that;
|
||||
// in that case, if an association starts with the non-object
|
||||
// value, then the value after the non-object value gets lost.
|
||||
private def associativeMerge(allObjects: Seq[AbstractConfigObject])(assertions: AbstractConfigObject => Unit) {
|
||||
private def associativeMerge(allObjects: Seq[AbstractConfigObject])(assertions: SimpleConfig => Unit) {
|
||||
def m(toMerge: AbstractConfigObject*) = merge(toMerge: _*)
|
||||
|
||||
def makeTrees(objects: Seq[AbstractConfigObject]): Iterator[AbstractConfigObject] = {
|
||||
@ -63,7 +64,7 @@ class ConfigTest extends TestUtils {
|
||||
}
|
||||
|
||||
for (tree <- trees) {
|
||||
assertions(tree)
|
||||
assertions(tree.toConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,42 +72,42 @@ class ConfigTest extends TestUtils {
|
||||
def mergeTrivial() {
|
||||
val obj1 = parseObject("""{ "a" : 1 }""")
|
||||
val obj2 = parseObject("""{ "b" : 2 }""")
|
||||
val merged = merge(obj1, obj2)
|
||||
val merged = merge(obj1, obj2).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(2, merged.getInt("b"))
|
||||
assertEquals(2, merged.keySet().size)
|
||||
assertEquals(2, merged.toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
def mergeEmpty() {
|
||||
val merged = merge()
|
||||
val merged = merge().toConfig
|
||||
|
||||
assertEquals(0, merged.keySet().size)
|
||||
assertEquals(0, merged.toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
def mergeOne() {
|
||||
val obj1 = parseObject("""{ "a" : 1 }""")
|
||||
val merged = merge(obj1)
|
||||
val merged = merge(obj1).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(1, merged.keySet().size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
def mergeOverride() {
|
||||
val obj1 = parseObject("""{ "a" : 1 }""")
|
||||
val obj2 = parseObject("""{ "a" : 2 }""")
|
||||
val merged = merge(obj1, obj2)
|
||||
val merged = merge(obj1, obj2).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(1, merged.keySet().size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
|
||||
val merged2 = merge(obj2, obj1)
|
||||
val merged2 = merge(obj2, obj1).toConfig
|
||||
|
||||
assertEquals(2, merged2.getInt("a"))
|
||||
assertEquals(1, merged2.keySet().size)
|
||||
assertEquals(1, merged2.toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -115,13 +116,13 @@ class ConfigTest extends TestUtils {
|
||||
val obj2 = parseObject("""{ "b" : 2 }""")
|
||||
val obj3 = parseObject("""{ "c" : 3 }""")
|
||||
val obj4 = parseObject("""{ "d" : 4 }""")
|
||||
val merged = merge(obj1, obj2, obj3, obj4)
|
||||
val merged = merge(obj1, obj2, obj3, obj4).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(2, merged.getInt("b"))
|
||||
assertEquals(3, merged.getInt("c"))
|
||||
assertEquals(4, merged.getInt("d"))
|
||||
assertEquals(4, merged.keySet().size)
|
||||
assertEquals(4, merged.toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -130,60 +131,60 @@ class ConfigTest extends TestUtils {
|
||||
val obj2 = parseObject("""{ "a" : 2 }""")
|
||||
val obj3 = parseObject("""{ "a" : 3 }""")
|
||||
val obj4 = parseObject("""{ "a" : 4 }""")
|
||||
val merged = merge(obj1, obj2, obj3, obj4)
|
||||
val merged = merge(obj1, obj2, obj3, obj4).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(1, merged.keySet().size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
|
||||
val merged2 = merge(obj4, obj3, obj2, obj1)
|
||||
val merged2 = merge(obj4, obj3, obj2, obj1).toConfig
|
||||
|
||||
assertEquals(4, merged2.getInt("a"))
|
||||
assertEquals(1, merged2.keySet().size)
|
||||
assertEquals(1, merged2.toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
def mergeNested() {
|
||||
val obj1 = parseObject("""{ "root" : { "a" : 1, "z" : 101 } }""")
|
||||
val obj2 = parseObject("""{ "root" : { "b" : 2, "z" : 102 } }""")
|
||||
val merged = merge(obj1, obj2)
|
||||
val merged = merge(obj1, obj2).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("root.a"))
|
||||
assertEquals(2, merged.getInt("root.b"))
|
||||
assertEquals(101, merged.getInt("root.z"))
|
||||
assertEquals(1, merged.keySet().size)
|
||||
assertEquals(3, merged.getObject("root").keySet().size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
assertEquals(3, merged.getConfig("root").toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
def mergeWithEmpty() {
|
||||
val obj1 = parseObject("""{ "a" : 1 }""")
|
||||
val obj2 = parseObject("""{ }""")
|
||||
val merged = merge(obj1, obj2)
|
||||
val merged = merge(obj1, obj2).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(1, merged.keySet().size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
|
||||
val merged2 = merge(obj2, obj1)
|
||||
val merged2 = merge(obj2, obj1).toConfig
|
||||
|
||||
assertEquals(1, merged2.getInt("a"))
|
||||
assertEquals(1, merged2.keySet().size)
|
||||
assertEquals(1, merged2.toObject.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
def mergeOverrideObjectAndPrimitive() {
|
||||
val obj1 = parseObject("""{ "a" : 1 }""")
|
||||
val obj2 = parseObject("""{ "a" : { "b" : 42 } }""")
|
||||
val merged = merge(obj1, obj2)
|
||||
val merged = merge(obj1, obj2).toConfig
|
||||
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(1, merged.keySet().size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
|
||||
val merged2 = merge(obj2, obj1)
|
||||
val merged2 = merge(obj2, obj1).toConfig
|
||||
|
||||
assertEquals(42, merged2.getObject("a").getInt("b"))
|
||||
assertEquals(42, merged2.getConfig("a").getInt("b"))
|
||||
assertEquals(42, merged2.getInt("a.b"))
|
||||
assertEquals(1, merged2.keySet().size)
|
||||
assertEquals(1, merged2.getObject("a").keySet().size)
|
||||
assertEquals(1, merged2.toObject.size)
|
||||
assertEquals(1, merged2.getObject("a").size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -195,15 +196,17 @@ class ConfigTest extends TestUtils {
|
||||
val obj2 = parseObject("""{ "a" : 2 }""")
|
||||
val obj3 = parseObject("""{ "a" : { "b" : 43, "c" : 44 } }""")
|
||||
|
||||
val merged = merge(obj1, obj2, obj3)
|
||||
val merged = merge(obj1, obj2, obj3).toConfig
|
||||
|
||||
assertEquals(42, merged.getInt("a.b"))
|
||||
assertEquals(1, merged.size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
assertEquals(1, merged.getObject("a").size())
|
||||
|
||||
val merged2 = merge(obj3, obj2, obj1)
|
||||
val merged2 = merge(obj3, obj2, obj1).toConfig
|
||||
|
||||
assertEquals(43, merged2.getInt("a.b"))
|
||||
assertEquals(44, merged2.getInt("a.c"))
|
||||
assertEquals(1, merged2.size)
|
||||
assertEquals(1, merged2.toObject.size)
|
||||
assertEquals(2, merged2.getObject("a").size())
|
||||
}
|
||||
|
||||
@ -216,7 +219,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
associativeMerge(Seq(obj1, obj2, obj3)) { merged =>
|
||||
assertEquals(1, merged.getInt("a"))
|
||||
assertEquals(1, merged.keySet().size)
|
||||
assertEquals(1, merged.toObject.size)
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,15 +227,20 @@ class ConfigTest extends TestUtils {
|
||||
SubstitutionResolver.resolve(v, root, ConfigResolveOptions.noSystem())
|
||||
}
|
||||
|
||||
private def resolveNoSystem(v: SimpleConfig, root: SimpleConfig) = {
|
||||
SubstitutionResolver.resolve(v.toObject, root.toObject,
|
||||
ConfigResolveOptions.noSystem()).asInstanceOf[AbstractConfigObject].toConfig
|
||||
}
|
||||
|
||||
@Test
|
||||
def mergeSubstitutedValues() {
|
||||
val obj1 = parseObject("""{ "a" : { "x" : 1, "z" : 4 }, "c" : ${a} }""")
|
||||
val obj2 = parseObject("""{ "b" : { "y" : 2, "z" : 5 }, "c" : ${b} }""")
|
||||
|
||||
val merged = merge(obj1, obj2)
|
||||
val resolved = resolveNoSystem(merged, merged) match {
|
||||
val resolved = (resolveNoSystem(merged, merged) match {
|
||||
case x: ConfigObject => x
|
||||
}
|
||||
}).toConfig
|
||||
|
||||
assertEquals(3, resolved.getObject("c").size())
|
||||
assertEquals(1, resolved.getInt("c.x"))
|
||||
@ -246,18 +254,18 @@ class ConfigTest extends TestUtils {
|
||||
val obj2 = parseObject("""{ "b" : { "y" : 2, "z" : 5 }, "c" : ${b} }""")
|
||||
|
||||
val merged = merge(obj1, obj2)
|
||||
val resolved = resolveNoSystem(merged, merged) match {
|
||||
val resolved = (resolveNoSystem(merged, merged) match {
|
||||
case x: ConfigObject => x
|
||||
}
|
||||
}).toConfig
|
||||
|
||||
assertEquals(2, resolved.getObject("c").size())
|
||||
assertEquals(2, resolved.getInt("c.y"))
|
||||
assertEquals(42, resolved.getInt("c.z"))
|
||||
|
||||
val merged2 = merge(obj2, obj1)
|
||||
val resolved2 = resolveNoSystem(merged2, merged2) match {
|
||||
val resolved2 = (resolveNoSystem(merged2, merged2) match {
|
||||
case x: ConfigObject => x
|
||||
}
|
||||
}).toConfig
|
||||
|
||||
assertEquals(2, resolved2.getObject("c").size())
|
||||
assertEquals(2, resolved2.getInt("c.y"))
|
||||
@ -315,9 +323,7 @@ class ConfigTest extends TestUtils {
|
||||
val obj3 = parseObject("""{ "c" : { "z" : 3, "q" : 6 }, "j" : ${c} }""")
|
||||
|
||||
associativeMerge(Seq(obj1, obj2, obj3)) { merged =>
|
||||
val resolved = resolveNoSystem(merged, merged) match {
|
||||
case x: ConfigObject => x
|
||||
}
|
||||
val resolved = resolveNoSystem(merged, merged)
|
||||
|
||||
assertEquals(4, resolved.getObject("j").size())
|
||||
assertEquals(1, resolved.getInt("j.x"))
|
||||
@ -334,11 +340,9 @@ class ConfigTest extends TestUtils {
|
||||
val obj3 = parseObject("""{ "c" : { "z" : 3, "q" : 6 }, "j" : ${c} }""")
|
||||
|
||||
associativeMerge(Seq(obj1, obj2, obj3)) { merged =>
|
||||
val resolved = resolveNoSystem(merged, merged) match {
|
||||
case x: ConfigObject => x
|
||||
}
|
||||
val resolved = resolveNoSystem(merged, merged)
|
||||
|
||||
assertEquals(3, resolved.size())
|
||||
assertEquals(3, resolved.toObject.size())
|
||||
assertEquals(42, resolved.getInt("j"));
|
||||
assertEquals(2, resolved.getInt("b.y"))
|
||||
assertEquals(3, resolved.getInt("c.z"))
|
||||
@ -352,9 +356,7 @@ class ConfigTest extends TestUtils {
|
||||
val obj3 = parseObject("""{ "c" : { "z" : 3, "q" : 6 }, "j" : ${c} }""")
|
||||
|
||||
associativeMerge(Seq(obj1, obj2, obj3)) { merged =>
|
||||
val resolved = resolveNoSystem(merged, merged) match {
|
||||
case x: ConfigObject => x
|
||||
}
|
||||
val resolved = resolveNoSystem(merged, merged)
|
||||
|
||||
assertEquals(4, resolved.getObject("j").size())
|
||||
assertEquals(1, resolved.getInt("j.x"))
|
||||
@ -372,9 +374,7 @@ class ConfigTest extends TestUtils {
|
||||
val obj4 = parseObject("""{ "c" : { "z" : 4, "q" : 8 }, "j" : ${c} }""")
|
||||
|
||||
associativeMerge(Seq(obj1, obj2, obj3, obj4)) { merged =>
|
||||
val resolved = resolveNoSystem(merged, merged) match {
|
||||
case x: ConfigObject => x
|
||||
}
|
||||
val resolved = resolveNoSystem(merged, merged)
|
||||
|
||||
assertEquals(5, resolved.getObject("j").size())
|
||||
assertEquals(1, resolved.getInt("j.w"))
|
||||
@ -387,7 +387,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test01Getting() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
// get all the primitive types
|
||||
assertEquals(42, conf.getInt("ints.fortyTwo"))
|
||||
@ -404,7 +404,7 @@ class ConfigTest extends TestUtils {
|
||||
// to get null we have to use the get() method from Map,
|
||||
// which takes a key and not a path
|
||||
assertEquals(nullValue(), conf.getObject("nulls").get("null"))
|
||||
assertNull(conf.get("notinthefile"))
|
||||
assertNull(conf.toObject.get("notinthefile"))
|
||||
|
||||
// get stuff with getValue
|
||||
assertEquals(intValue(42), conf.getValue("ints.fortyTwo"))
|
||||
@ -447,7 +447,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test01Exceptions() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
// should throw Missing if key doesn't exist
|
||||
intercept[ConfigException.Missing] {
|
||||
@ -534,7 +534,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test01Conversions() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
// should convert numbers to string
|
||||
assertEquals("42", conf.getString("ints.fortyTwo"))
|
||||
@ -597,7 +597,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test01MergingOtherFormats() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
// should have loaded stuff from .json
|
||||
assertEquals(1, conf.getInt("fromJson1"))
|
||||
@ -611,7 +611,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test01ToString() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
// toString() on conf objects doesn't throw (toString is just a debug string so not testing its result)
|
||||
conf.toString()
|
||||
@ -619,7 +619,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test01SystemFallbacks() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
val jv = System.getProperty("java.version")
|
||||
assertNotNull(jv)
|
||||
assertEquals(jv, conf.getString("system.javaversion"))
|
||||
@ -627,13 +627,13 @@ class ConfigTest extends TestUtils {
|
||||
if (home != null) {
|
||||
assertEquals(home, conf.getString("system.home"))
|
||||
} else {
|
||||
assertEquals(nullValue, conf.get("system.home"))
|
||||
assertEquals(nullValue, conf.getObject("system").get("home"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def test02SubstitutionsWithWeirdPaths() {
|
||||
val conf = Config.load("test02")
|
||||
val conf = ConfigFactory.load("test02")
|
||||
|
||||
assertEquals(42, conf.getInt("42_a"))
|
||||
assertEquals(42, conf.getInt("42_b"))
|
||||
@ -645,7 +645,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test02UseWeirdPathsWithConfigObject() {
|
||||
val conf = Config.load("test02")
|
||||
val conf = ConfigFactory.load("test02")
|
||||
|
||||
// we're checking that the getters in ConfigObject support
|
||||
// these weird path expressions
|
||||
@ -657,7 +657,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test03Includes() {
|
||||
val conf = Config.load("test03")
|
||||
val conf = ConfigFactory.load("test03")
|
||||
|
||||
// include should have overridden the "ints" value in test03
|
||||
assertEquals(42, conf.getInt("test01.ints.fortyTwo"))
|
||||
@ -685,7 +685,7 @@ class ConfigTest extends TestUtils {
|
||||
if (home != null) {
|
||||
assertEquals(home, conf.getString("test01.system.home"))
|
||||
} else {
|
||||
assertEquals(nullValue, conf.get("test01.system.home"))
|
||||
assertEquals(nullValue, conf.getObject("test01.system").get("home"))
|
||||
}
|
||||
val concatenated = conf.getString("test01.system.concatenated")
|
||||
assertTrue(concatenated.contains("Your Java version"))
|
||||
@ -695,7 +695,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test04LoadAkkaReference() {
|
||||
val conf = Config.load("test04")
|
||||
val conf = ConfigFactory.load("test04")
|
||||
|
||||
// Note, test04 is an unmodified old-style akka.conf,
|
||||
// which means it has an outer akka{} namespace.
|
||||
@ -710,7 +710,7 @@ class ConfigTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def test05LoadPlayApplicationConf() {
|
||||
val conf = Config.load("test05")
|
||||
val conf = ConfigFactory.load("test05")
|
||||
|
||||
assertEquals("prod", conf.getString("%prod.application.mode"))
|
||||
assertEquals("Yet another blog", conf.getString("blog.title"))
|
||||
|
@ -87,10 +87,23 @@ class ConfigValueTest extends TestUtils {
|
||||
checkNotEqualObjects(a, c)
|
||||
checkNotEqualObjects(b, c)
|
||||
|
||||
val root = a.asRoot(path("foo"))
|
||||
checkEqualObjects(a, root)
|
||||
checkNotEqualObjects(root, b)
|
||||
checkNotEqualObjects(root, c)
|
||||
// the config for an equal object is also equal
|
||||
val config = a.toConfig()
|
||||
checkEqualObjects(config, config)
|
||||
checkEqualObjects(config, sameAsA.toConfig())
|
||||
checkEqualObjects(a.toConfig(), config)
|
||||
checkNotEqualObjects(config, b.toConfig())
|
||||
checkNotEqualObjects(config, c.toConfig())
|
||||
|
||||
// configs are not equal to objects
|
||||
checkNotEqualObjects(a, a.toConfig())
|
||||
checkNotEqualObjects(b, b.toConfig())
|
||||
|
||||
// configs are equal to the same config as a root
|
||||
val root = config.asRoot(path("foo"))
|
||||
checkEqualObjects(config, root)
|
||||
checkNotEqualObjects(b.toConfig(), root)
|
||||
checkNotEqualObjects(c.toConfig(), root)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -321,7 +334,7 @@ class ConfigValueTest extends TestUtils {
|
||||
unresolved { dmo.keySet() }
|
||||
unresolved { dmo.size() }
|
||||
unresolved { dmo.values() }
|
||||
unresolved { dmo.getInt("foo") }
|
||||
unresolved { dmo.toConfig.getInt("foo") }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -335,12 +348,12 @@ class ConfigValueTest extends TestUtils {
|
||||
// formats as 1E100 (capital E)
|
||||
val d = "1e100"
|
||||
|
||||
val obj = parseObject("{ a : " + a + ", b : " + b + ", c : " + c + ", d : " + d + "}")
|
||||
val obj = parseConfig("{ a : " + a + ", b : " + b + ", c : " + c + ", d : " + d + "}")
|
||||
assertEquals(Seq(a, b, c, d),
|
||||
Seq("a", "b", "c", "d") map { obj.getString(_) })
|
||||
|
||||
// make sure it still works if we're doing concatenation
|
||||
val obj2 = parseObject("{ a : xx " + a + " yy, b : xx " + b + " yy, c : xx " + c + " yy, d : xx " + d + " yy}")
|
||||
val obj2 = parseConfig("{ a : xx " + a + " yy, b : xx " + b + " yy, c : xx " + c + " yy, d : xx " + d + " yy}")
|
||||
assertEquals(Seq(a, b, c, d) map { "xx " + _ + " yy" },
|
||||
Seq("a", "b", "c", "d") map { obj2.getString(_) })
|
||||
}
|
||||
@ -374,11 +387,11 @@ class ConfigValueTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def hasPathWorks() {
|
||||
val empty = parseObject("{}")
|
||||
val empty = parseConfig("{}")
|
||||
|
||||
assertFalse(empty.hasPath("foo"))
|
||||
|
||||
val obj = parseObject("a=null, b.c.d=11, foo=bar")
|
||||
val obj = parseConfig("a=null, b.c.d=11, foo=bar")
|
||||
|
||||
// returns true for the non-null values
|
||||
assertTrue(obj.hasPath("foo"))
|
||||
@ -387,12 +400,12 @@ class ConfigValueTest extends TestUtils {
|
||||
assertTrue(obj.hasPath("b"))
|
||||
|
||||
// hasPath() is false for null values but containsKey is true
|
||||
assertEquals(nullValue(), obj.get("a"))
|
||||
assertTrue(obj.containsKey("a"))
|
||||
assertEquals(nullValue(), obj.toObject.get("a"))
|
||||
assertTrue(obj.toObject.containsKey("a"))
|
||||
assertFalse(obj.hasPath("a"))
|
||||
|
||||
// false for totally absent values
|
||||
assertFalse(obj.containsKey("notinhere"))
|
||||
assertFalse(obj.toObject.containsKey("notinhere"))
|
||||
assertFalse(obj.hasPath("notinhere"))
|
||||
|
||||
// throws proper exceptions
|
||||
@ -425,12 +438,12 @@ class ConfigValueTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def automaticBooleanConversions() {
|
||||
val trues = parseObject("{ a=true, b=yes, c=on }")
|
||||
val trues = parseObject("{ a=true, b=yes, c=on }").toConfig
|
||||
assertEquals(true, trues.getBoolean("a"))
|
||||
assertEquals(true, trues.getBoolean("b"))
|
||||
assertEquals(true, trues.getBoolean("c"))
|
||||
|
||||
val falses = parseObject("{ a=false, b=no, c=off }")
|
||||
val falses = parseObject("{ a=false, b=no, c=off }").toConfig
|
||||
assertEquals(false, falses.getBoolean("a"))
|
||||
assertEquals(false, falses.getBoolean("b"))
|
||||
assertEquals(false, falses.getBoolean("c"))
|
||||
|
@ -42,12 +42,12 @@ class EquivalentsTest extends TestUtils {
|
||||
|
||||
private def parse(flavor: ConfigSyntax, f: File) = {
|
||||
val options = ConfigParseOptions.defaults().setSyntax(flavor)
|
||||
postParse(Config.parse(f, options))
|
||||
postParse(ConfigFactory.parse(f, options).toObject)
|
||||
}
|
||||
|
||||
private def parse(f: File) = {
|
||||
val options = ConfigParseOptions.defaults()
|
||||
postParse(Config.parse(f, options))
|
||||
postParse(ConfigFactory.parse(f, options).toObject)
|
||||
}
|
||||
|
||||
// would like each "equivNN" directory to be a suite and each file in the dir
|
||||
|
@ -5,6 +5,7 @@ import org.junit._
|
||||
import java.util.Properties
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
class PropertiesTest extends TestUtils {
|
||||
@Test
|
||||
@ -50,7 +51,7 @@ class PropertiesTest extends TestUtils {
|
||||
|
||||
props.setProperty(propsPath, propsPath)
|
||||
|
||||
val conf = Config.parse(props, ConfigParseOptions.defaults())
|
||||
val conf = ConfigFactory.parse(props, ConfigParseOptions.defaults())
|
||||
|
||||
assertEquals(propsPath, conf.getString(confPath))
|
||||
}
|
||||
@ -82,9 +83,9 @@ class PropertiesTest extends TestUtils {
|
||||
props.setProperty("x.y", "bar")
|
||||
props.setProperty("x.y.z", "foo")
|
||||
|
||||
val conf = Config.parse(props, ConfigParseOptions.defaults())
|
||||
val conf = ConfigFactory.parse(props, ConfigParseOptions.defaults())
|
||||
|
||||
assertEquals(2, conf.size())
|
||||
assertEquals(2, conf.toObject.size())
|
||||
assertEquals("foo", conf.getString("a.b"))
|
||||
assertEquals("foo", conf.getString("x.y.z"))
|
||||
}
|
||||
|
@ -12,11 +12,11 @@ import scala.collection.mutable
|
||||
class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def basicLoadAndGet() {
|
||||
val conf = Config.load("test01")
|
||||
val conf = ConfigFactory.load("test01")
|
||||
|
||||
val a = conf.getInt("ints.fortyTwo")
|
||||
val obj = conf.getObject("ints")
|
||||
val c = obj.getInt("fortyTwo")
|
||||
val child = conf.getConfig("ints")
|
||||
val c = child.getInt("fortyTwo")
|
||||
val ms = conf.getMilliseconds("durations.halfSecond")
|
||||
|
||||
// should have used system variables
|
||||
@ -29,7 +29,7 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def noSystemVariables() {
|
||||
// should not have used system variables
|
||||
val conf = Config.load("test01", ConfigParseOptions.defaults(),
|
||||
val conf = ConfigFactory.load("test01", ConfigParseOptions.defaults(),
|
||||
ConfigResolveOptions.noSystem())
|
||||
|
||||
intercept[ConfigException.Null] {
|
||||
@ -43,7 +43,7 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def canLimitLoadToJson {
|
||||
val options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON);
|
||||
val conf = Config.load("test01", options, ConfigResolveOptions.defaults())
|
||||
val conf = ConfigFactory.load("test01", options, ConfigResolveOptions.defaults())
|
||||
|
||||
assertEquals(1, conf.getInt("fromJson1"))
|
||||
intercept[ConfigException.Missing] {
|
||||
@ -54,7 +54,7 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def canLimitLoadToProperties {
|
||||
val options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES);
|
||||
val conf = Config.load("test01", options, ConfigResolveOptions.defaults())
|
||||
val conf = ConfigFactory.load("test01", options, ConfigResolveOptions.defaults())
|
||||
|
||||
assertEquals(1, conf.getInt("fromProps.one"))
|
||||
intercept[ConfigException.Missing] {
|
||||
@ -65,7 +65,7 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def canLimitLoadToConf {
|
||||
val options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF);
|
||||
val conf = Config.load("test01", options, ConfigResolveOptions.defaults())
|
||||
val conf = ConfigFactory.load("test01", options, ConfigResolveOptions.defaults())
|
||||
|
||||
assertEquals(42, conf.getInt("ints.fortyTwo"))
|
||||
intercept[ConfigException.Missing] {
|
||||
@ -77,22 +77,22 @@ class PublicApiTest extends TestUtils {
|
||||
}
|
||||
|
||||
@Test
|
||||
def emptyObjects() {
|
||||
assertEquals(0, Config.empty().size())
|
||||
assertEquals("empty config", Config.empty().origin().description())
|
||||
assertEquals(0, Config.empty("foo").size())
|
||||
assertEquals("foo", Config.empty("foo").origin().description())
|
||||
assertEquals(0, Config.emptyRoot("foo.bar").size())
|
||||
assertEquals("foo.bar", Config.emptyRoot("foo.bar").origin().description())
|
||||
def emptyConfigs() {
|
||||
assertTrue(ConfigFactory.empty().isEmpty())
|
||||
assertEquals("empty config", ConfigFactory.empty().origin().description())
|
||||
assertTrue(ConfigFactory.empty("foo").isEmpty())
|
||||
assertEquals("foo", ConfigFactory.empty("foo").origin().description())
|
||||
assertTrue(ConfigFactory.emptyRoot("foo.bar").isEmpty())
|
||||
assertEquals("foo.bar", ConfigFactory.emptyRoot("foo.bar").origin().description())
|
||||
}
|
||||
|
||||
private val defaultValueDesc = "hardcoded value";
|
||||
|
||||
private def testFromValue(expectedValue: ConfigValue, createFrom: AnyRef) {
|
||||
assertEquals(expectedValue, Config.fromAnyRef(createFrom))
|
||||
assertEquals(defaultValueDesc, Config.fromAnyRef(createFrom).origin().description())
|
||||
assertEquals(expectedValue, Config.fromAnyRef(createFrom, "foo"))
|
||||
assertEquals("foo", Config.fromAnyRef(createFrom, "foo").origin().description())
|
||||
assertEquals(expectedValue, ConfigValueFactory.fromAnyRef(createFrom))
|
||||
assertEquals(defaultValueDesc, ConfigValueFactory.fromAnyRef(createFrom).origin().description())
|
||||
assertEquals(expectedValue, ConfigValueFactory.fromAnyRef(createFrom, "foo"))
|
||||
assertEquals("foo", ConfigValueFactory.fromAnyRef(createFrom, "foo").origin().description())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -135,8 +135,8 @@ class PublicApiTest extends TestUtils {
|
||||
testFromValue(new SimpleConfigObject(fakeOrigin(), emptyMapValue), Collections.emptyMap[String, Int])
|
||||
testFromValue(new SimpleConfigObject(fakeOrigin(), aMapValue), Map("a" -> 1, "b" -> 2, "c" -> 3).asJava)
|
||||
|
||||
assertEquals("hardcoded value", Config.fromMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava).origin().description())
|
||||
assertEquals("foo", Config.fromMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava, "foo").origin().description())
|
||||
assertEquals("hardcoded value", ConfigValueFactory.fromMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava).origin().description())
|
||||
assertEquals("foo", ConfigValueFactory.fromMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava, "foo").origin().description())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -158,34 +158,34 @@ class PublicApiTest extends TestUtils {
|
||||
|
||||
// testFromValue doesn't test the fromIterable public wrapper around fromAnyRef,
|
||||
// do so here.
|
||||
assertEquals(new SimpleConfigList(fakeOrigin(), aListValue), Config.fromIterable(List(1, 2, 3).asJava))
|
||||
assertEquals(new SimpleConfigList(fakeOrigin(), aListValue), Config.fromIterable(treeSet))
|
||||
assertEquals(new SimpleConfigList(fakeOrigin(), aListValue), ConfigValueFactory.fromIterable(List(1, 2, 3).asJava))
|
||||
assertEquals(new SimpleConfigList(fakeOrigin(), aListValue), ConfigValueFactory.fromIterable(treeSet))
|
||||
|
||||
assertEquals("hardcoded value", Config.fromIterable(List(1, 2, 3).asJava).origin().description())
|
||||
assertEquals("foo", Config.fromIterable(treeSet, "foo").origin().description())
|
||||
assertEquals("hardcoded value", ConfigValueFactory.fromIterable(List(1, 2, 3).asJava).origin().description())
|
||||
assertEquals("foo", ConfigValueFactory.fromIterable(treeSet, "foo").origin().description())
|
||||
}
|
||||
|
||||
@Test
|
||||
def roundTripUnwrap() {
|
||||
val conf = Config.load("test01")
|
||||
assertTrue(conf.size() > 4) // "has a lot of stuff in it"
|
||||
val unwrapped = conf.unwrapped()
|
||||
val rewrapped = Config.fromMap(unwrapped, conf.origin().description())
|
||||
val conf = ConfigFactory.load("test01")
|
||||
assertTrue(conf.toObject.size > 4) // "has a lot of stuff in it"
|
||||
val unwrapped = conf.toObject.unwrapped()
|
||||
val rewrapped = ConfigValueFactory.fromMap(unwrapped, conf.origin().description())
|
||||
val reunwrapped = rewrapped.unwrapped()
|
||||
assertEquals(conf, rewrapped)
|
||||
assertEquals(conf.toObject, rewrapped)
|
||||
assertEquals(reunwrapped, unwrapped)
|
||||
}
|
||||
|
||||
private def testFromPathMap(expectedValue: ConfigObject, createFrom: java.util.Map[String, Object]) {
|
||||
assertEquals(expectedValue, Config.fromPathMap(createFrom))
|
||||
assertEquals(defaultValueDesc, Config.fromPathMap(createFrom).origin().description())
|
||||
assertEquals(expectedValue, Config.fromPathMap(createFrom, "foo"))
|
||||
assertEquals("foo", Config.fromPathMap(createFrom, "foo").origin().description())
|
||||
assertEquals(expectedValue, ConfigFactory.parseMap(createFrom).toObject)
|
||||
assertEquals(defaultValueDesc, ConfigFactory.parseMap(createFrom).origin().description())
|
||||
assertEquals(expectedValue, ConfigFactory.parseMap(createFrom, "foo").toObject)
|
||||
assertEquals("foo", ConfigFactory.parseMap(createFrom, "foo").origin().description())
|
||||
}
|
||||
|
||||
@Test
|
||||
def fromJavaPathMap() {
|
||||
// first the same tests as with fromMap
|
||||
// first the same tests as with fromMap, but use parseMap
|
||||
val emptyMapValue = Collections.emptyMap[String, AbstractConfigValue]
|
||||
val aMapValue = Map("a" -> 1, "b" -> 2, "c" -> 3).mapValues(intValue(_): AbstractConfigValue).asJava
|
||||
testFromPathMap(new SimpleConfigObject(fakeOrigin(), emptyMapValue),
|
||||
@ -193,19 +193,19 @@ class PublicApiTest extends TestUtils {
|
||||
testFromPathMap(new SimpleConfigObject(fakeOrigin(), aMapValue),
|
||||
Map("a" -> 1, "b" -> 2, "c" -> 3).asInstanceOf[Map[String, AnyRef]].asJava)
|
||||
|
||||
assertEquals("hardcoded value", Config.fromPathMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava).origin().description())
|
||||
assertEquals("foo", Config.fromPathMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava, "foo").origin().description())
|
||||
assertEquals("hardcoded value", ConfigFactory.parseMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava).origin().description())
|
||||
assertEquals("foo", ConfigFactory.parseMap(Map("a" -> 1, "b" -> 2, "c" -> 3).asJava, "foo").origin().description())
|
||||
|
||||
// now some tests with paths; be sure to test nested path maps
|
||||
val simplePathMapValue = Map("x.y" -> 4, "z" -> 5).asInstanceOf[Map[String, AnyRef]].asJava
|
||||
val pathMapValue = Map("a.c" -> 1, "b" -> simplePathMapValue).asInstanceOf[Map[String, AnyRef]].asJava
|
||||
|
||||
val obj = Config.fromPathMap(pathMapValue)
|
||||
val conf = ConfigFactory.parseMap(pathMapValue)
|
||||
|
||||
assertEquals(2, obj.size)
|
||||
assertEquals(4, obj.getInt("b.x.y"))
|
||||
assertEquals(5, obj.getInt("b.z"))
|
||||
assertEquals(1, obj.getInt("a.c"))
|
||||
assertEquals(2, conf.toObject.size)
|
||||
assertEquals(4, conf.getInt("b.x.y"))
|
||||
assertEquals(5, conf.getInt("b.z"))
|
||||
assertEquals(1, conf.getInt("a.c"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -213,7 +213,7 @@ class PublicApiTest extends TestUtils {
|
||||
// "a" is both number 1 and an object
|
||||
val pathMapValue = Map("a" -> 1, "a.b" -> 2).asInstanceOf[Map[String, AnyRef]].asJava
|
||||
intercept[ConfigException.BugOrBroken] {
|
||||
Config.fromPathMap(pathMapValue)
|
||||
ConfigFactory.parseMap(pathMapValue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,11 +236,11 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def allowMissing() {
|
||||
val e = intercept[ConfigException.IO] {
|
||||
Config.parse(resource("nonexistent.conf"), ConfigParseOptions.defaults().setAllowMissing(false))
|
||||
ConfigFactory.parse(resource("nonexistent.conf"), ConfigParseOptions.defaults().setAllowMissing(false))
|
||||
}
|
||||
assertTrue(e.getMessage.contains("No such"))
|
||||
|
||||
val conf = Config.parse(resource("nonexistent.conf"), ConfigParseOptions.defaults().setAllowMissing(true))
|
||||
val conf = ConfigFactory.parse(resource("nonexistent.conf"), ConfigParseOptions.defaults().setAllowMissing(true))
|
||||
assertTrue(conf.isEmpty())
|
||||
}
|
||||
|
||||
@ -251,10 +251,10 @@ class PublicApiTest extends TestUtils {
|
||||
// change that the includes are allowed to be missing.
|
||||
// This can break because some options might "propagate" through
|
||||
// to includes, but we don't want them all to do so.
|
||||
val conf = Config.parse(resource("test03.conf"), ConfigParseOptions.defaults().setAllowMissing(false))
|
||||
val conf = ConfigFactory.parse(resource("test03.conf"), ConfigParseOptions.defaults().setAllowMissing(false))
|
||||
assertEquals(42, conf.getInt("test01.booleans"))
|
||||
|
||||
val conf2 = Config.parse(resource("test03.conf"), ConfigParseOptions.defaults().setAllowMissing(true))
|
||||
val conf2 = ConfigFactory.parse(resource("test03.conf"), ConfigParseOptions.defaults().setAllowMissing(true))
|
||||
assertEquals(conf, conf2)
|
||||
}
|
||||
|
||||
@ -277,7 +277,7 @@ class PublicApiTest extends TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private def whatWasIncluded(parser: ConfigParseOptions => ConfigObject): List[Included] = {
|
||||
private def whatWasIncluded(parser: ConfigParseOptions => Config): List[Included] = {
|
||||
val included = mutable.ListBuffer[Included]()
|
||||
val includer = new RecordingIncluder(null, included)
|
||||
|
||||
@ -288,7 +288,7 @@ class PublicApiTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def includersAreUsedWithFiles() {
|
||||
val included = whatWasIncluded(Config.parse(resource("test03.conf"), _))
|
||||
val included = whatWasIncluded(ConfigFactory.parse(resource("test03.conf"), _))
|
||||
|
||||
assertEquals(List("test01", "test02.conf", "equiv01/original.json",
|
||||
"nothere", "nothere.conf", "nothere.json", "nothere.properties"),
|
||||
@ -298,7 +298,7 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def includersAreUsedRecursivelyWithFiles() {
|
||||
// includes.conf has recursive includes in it
|
||||
val included = whatWasIncluded(Config.parse(resource("equiv03/includes.conf"), _))
|
||||
val included = whatWasIncluded(ConfigFactory.parse(resource("equiv03/includes.conf"), _))
|
||||
|
||||
assertEquals(List("letters/a.conf", "numbers/1.conf", "numbers/2", "letters/b.json", "letters/c"),
|
||||
included.map(_.name))
|
||||
@ -306,7 +306,7 @@ class PublicApiTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def includersAreUsedWithClasspath() {
|
||||
val included = whatWasIncluded(Config.parse(classOf[PublicApiTest], "/test03.conf", _))
|
||||
val included = whatWasIncluded(ConfigFactory.parse(classOf[PublicApiTest], "/test03.conf", _))
|
||||
|
||||
assertEquals(List("test01", "test02.conf", "equiv01/original.json",
|
||||
"nothere", "nothere.conf", "nothere.json", "nothere.properties"),
|
||||
@ -316,7 +316,7 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def includersAreUsedRecursivelyWithClasspath() {
|
||||
// includes.conf has recursive includes in it
|
||||
val included = whatWasIncluded(Config.parse(classOf[PublicApiTest], "/equiv03/includes.conf", _))
|
||||
val included = whatWasIncluded(ConfigFactory.parse(classOf[PublicApiTest], "/equiv03/includes.conf", _))
|
||||
|
||||
assertEquals(List("letters/a.conf", "numbers/1.conf", "numbers/2", "letters/b.json", "letters/c"),
|
||||
included.map(_.name))
|
||||
|
@ -8,6 +8,7 @@ import java.io.StringReader
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigSyntax
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
abstract trait TestUtils {
|
||||
protected def intercept[E <: Throwable: Manifest](block: => Unit): E = {
|
||||
@ -332,10 +333,14 @@ abstract trait TestUtils {
|
||||
protected def doubleValue(d: Double) = new ConfigDouble(fakeOrigin(), d, null)
|
||||
|
||||
protected def parseObject(s: String) = {
|
||||
parseConfig(s).toObject
|
||||
}
|
||||
|
||||
protected def parseConfig(s: String) = {
|
||||
val options = ConfigParseOptions.defaults().
|
||||
setOriginDescription("test string").
|
||||
setSyntax(ConfigSyntax.CONF);
|
||||
Config.parse(new StringReader(s), options).asInstanceOf[AbstractConfigObject]
|
||||
ConfigFactory.parse(new StringReader(s), options).asInstanceOf[SimpleConfig]
|
||||
}
|
||||
|
||||
protected def subst(ref: String) = {
|
||||
|
@ -20,19 +20,19 @@ class UnitParserTest extends TestUtils {
|
||||
"1.1574074074074073e-05d", "1.1574074074074073e-05 days", "1.1574074074074073e-05day")
|
||||
val oneSecInNanos = TimeUnit.SECONDS.toNanos(1)
|
||||
for (s <- oneSecs) {
|
||||
val result = Config.parseDuration(s, fakeOrigin(), "test")
|
||||
val result = SimpleConfig.parseDuration(s, fakeOrigin(), "test")
|
||||
assertEquals(oneSecInNanos, result)
|
||||
}
|
||||
|
||||
// bad units
|
||||
val e = intercept[ConfigException.BadValue] {
|
||||
Config.parseDuration("100 dollars", fakeOrigin(), "test")
|
||||
SimpleConfig.parseDuration("100 dollars", fakeOrigin(), "test")
|
||||
}
|
||||
assertTrue(e.getMessage().contains("time unit"))
|
||||
|
||||
// bad number
|
||||
val e2 = intercept[ConfigException.BadValue] {
|
||||
Config.parseDuration("1 00 seconds", fakeOrigin(), "test")
|
||||
SimpleConfig.parseDuration("1 00 seconds", fakeOrigin(), "test")
|
||||
}
|
||||
assertTrue(e2.getMessage().contains("duration number"))
|
||||
}
|
||||
@ -47,7 +47,7 @@ class UnitParserTest extends TestUtils {
|
||||
"1m", "1M", "1 M", "1 megabytes", "1 megabyte",
|
||||
"0.0009765625g", "0.0009765625G", "0.0009765625 gigabytes", "0.0009765625 gigabyte")
|
||||
|
||||
def parseMem(s: String) = Config.parseMemorySizeInBytes(s, fakeOrigin(), "test")
|
||||
def parseMem(s: String) = SimpleConfig.parseMemorySizeInBytes(s, fakeOrigin(), "test")
|
||||
|
||||
for (s <- oneMegs) {
|
||||
val result = parseMem(s)
|
||||
@ -61,13 +61,13 @@ class UnitParserTest extends TestUtils {
|
||||
|
||||
// bad units
|
||||
val e = intercept[ConfigException.BadValue] {
|
||||
Config.parseMemorySizeInBytes("100 dollars", fakeOrigin(), "test")
|
||||
SimpleConfig.parseMemorySizeInBytes("100 dollars", fakeOrigin(), "test")
|
||||
}
|
||||
assertTrue(e.getMessage().contains("size unit"))
|
||||
|
||||
// bad number
|
||||
val e2 = intercept[ConfigException.BadValue] {
|
||||
Config.parseMemorySizeInBytes("1 00 bytes", fakeOrigin(), "test")
|
||||
SimpleConfig.parseMemorySizeInBytes("1 00 bytes", fakeOrigin(), "test")
|
||||
}
|
||||
assertTrue(e2.getMessage().contains("size number"))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user