+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+import java.time.Duration;
+import java.time.Period;
+import java.time.temporal.TemporalAmount;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An immutable map from config paths to config values. Paths are dot-separated
+ * expressions such as foo.bar.baz. Values are as in JSON
+ * (booleans, strings, numbers, lists, or objects), represented by
+ * {@link ConfigValue} instances. Values accessed through the
+ * Config interface are never null.
+ *
+ *
+ * {@code Config} is an immutable object and thus safe to use from multiple
+ * threads. There's never a need for "defensive copies."
+ *
+ *
+ * Fundamental operations on a {@code Config} include getting configuration
+ * values, resolving substitutions with {@link Config#resolve()}, and
+ * merging configs using {@link Config#withFallback(ConfigMergeable)}.
+ *
+ *
+ * All operations return a new immutable {@code Config} rather than modifying
+ * the original instance.
+ *
+ *
+ * Examples
+ *
+ *
+ * You can find an example app and library on
+ * GitHub. Also be sure to read the package overview which
+ * describes the big picture as shown in those examples.
+ *
+ *
+ * Paths, keys, and Config vs. ConfigObject
+ *
+ *
+ * Config is a view onto a tree of {@link ConfigObject}; the
+ * corresponding object tree can be found through {@link Config#root()}.
+ * ConfigObject is a map from config keys, rather than
+ * paths, to config values. Think of ConfigObject as a JSON object
+ * and Config as a configuration API.
+ *
+ *
+ * The API tries to consistently use the terms "key" and "path." 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 keys. Path
+ * expressions are described in the spec for
+ * Human-Optimized Config Object Notation. In brief, a 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 {@code Config} is in terms of path expressions, while the API
+ * for a {@code ConfigObject} is in terms of keys. Conceptually, {@code Config}
+ * is a one-level map from paths to values, while a
+ * {@code ConfigObject} is a tree of nested maps from keys to values.
+ *
+ *
+ * Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
+ * between path expressions and individual path elements (keys).
+ *
+ *
+ * Another difference between {@code Config} and {@code ConfigObject} is that
+ * conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType()
+ * valueType()} of {@link ConfigValueType#NULL NULL} exist in a
+ * {@code ConfigObject}, while a {@code Config} treats null values as if they
+ * were missing. (With the exception of two methods: {@link Config#hasPathOrNull}
+ * and {@link Config#getIsNull} let you detect null values.)
+ *
+ *
+ * Getting configuration values
+ *
+ *
+ * The "getters" on a {@code Config} all work in the same way. They never return
+ * null, nor do they return a {@code ConfigValue} with
+ * {@link ConfigValue#valueType() valueType()} of {@link ConfigValueType#NULL
+ * NULL}. Instead, they throw {@link ConfigException.Missing} if the value is
+ * completely absent or set to null. If the value is set to null, a subtype of
+ * {@code ConfigException.Missing} called {@link ConfigException.Null} will be
+ * thrown. {@link 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.
+ *
+ *
+ * Iteration
+ *
+ *
+ * If you want to iterate over the contents of a {@code Config}, you can get its
+ * {@code ConfigObject} with {@link #root()}, and then iterate over the
+ * {@code ConfigObject} (which implements java.util.Map). Or, you
+ * can use {@link #entrySet()} which recurses the object tree for you and builds
+ * up a Set of all path-value pairs where the value is not null.
+ *
+ *
+ * Resolving substitutions
+ *
+ *
+ * Substitutions are the ${foo.bar} syntax in config
+ * files, described in the specification. Resolving substitutions replaces these references with real
+ * values.
+ *
+ *
+ * Before using a {@code Config} it's necessary to call {@link Config#resolve()}
+ * to handle substitutions (though {@link ConfigFactory#load()} and similar
+ * methods will do the resolve for you already).
+ *
+ *
+ * Merging
+ *
+ *
+ * The full Config for your application can be constructed using
+ * the associative operation {@link Config#withFallback(ConfigMergeable)}. If
+ * you use {@link ConfigFactory#load()} (recommended), it merges system
+ * properties over the top of application.conf over the top of
+ * reference.conf, using withFallback. You can add in
+ * additional sources of configuration in the same way (usually, custom layers
+ * should go either just above or just below application.conf,
+ * keeping reference.conf at the bottom and system properties at
+ * the top).
+ *
+ *
+ * Serialization
+ *
+ *
+ * Convert a Config to a JSON or HOCON string by calling
+ * {@link ConfigObject#render()} on the root object,
+ * myConfig.root().render(). There's also a variant
+ * {@link ConfigObject#render(ConfigRenderOptions)} which allows you to control
+ * the format of the rendered string. (See {@link ConfigRenderOptions}.) Note
+ * that Config does not remember the formatting of the original
+ * file, so if you load, modify, and re-save a config file, it will be
+ * substantially reformatted.
+ *
+ *
+ * As an alternative to {@link ConfigObject#render()}, the
+ * toString() method produces a debug-output-oriented
+ * representation (which is not valid JSON).
+ *
+ *
+ * Java serialization is supported as well for Config and all
+ * subtypes of ConfigValue.
+ *
+ *
+ * This is an interface but don't implement it yourself
+ *
+ *
+ * Do not implement {@code Config}; it should only be implemented by
+ * the config library. Arbitrary implementations will not work because the
+ * library internals assume a specific concrete implementation. Also, this
+ * interface is likely to grow new methods over time, so third-party
+ * implementations will break.
+ */
+public interface Config extends ConfigMergeable {
+ /**
+ * Gets the {@code Config} as a tree of {@link ConfigObject}. This is a
+ * constant-time operation (it is not proportional to the number of values
+ * in the {@code Config}).
+ *
+ * @return the root object in the configuration
+ */
+ ConfigObject root();
+
+ /**
+ * Gets the origin of the {@code Config}, which may be a file, or a file
+ * with a line number, or just a descriptive phrase.
+ *
+ * @return the origin of the {@code Config} for use in error messages
+ */
+ ConfigOrigin origin();
+
+ @Override
+ Config withFallback(ConfigMergeable other);
+
+ /**
+ * Returns a replacement config with all substitutions (the
+ * ${foo.bar} syntax, see the
+ * spec) resolved. Substitutions are looked up using this
+ * Config as the root object, that is, a substitution
+ * ${foo.bar} will be replaced with the result of
+ * getValue("foo.bar").
+ *
+ *
+ * This method uses {@link ConfigResolveOptions#defaults()}, there is
+ * another variant {@link Config#resolve(ConfigResolveOptions)} which lets
+ * you specify non-default options.
+ *
+ *
+ * A given {@link Config} must be resolved before using it to retrieve
+ * config values, but ideally should be resolved one time for your entire
+ * stack of fallbacks (see {@link Config#withFallback}). Otherwise, some
+ * substitutions that could have resolved with all fallbacks available may
+ * not resolve, which will be potentially confusing for your application's
+ * users.
+ *
+ *
+ * resolve() should be invoked on root config objects, rather
+ * than on a subtree (a subtree is the result of something like
+ * config.getConfig("foo")). The problem with
+ * resolve() on a subtree is that substitutions are relative to
+ * the root of the config and the subtree will have no way to get values
+ * from the root. For example, if you did
+ * config.getConfig("foo").resolve() on the below config file,
+ * it would not work:
+ *
+ *
+ * common-value = 10
+ * foo {
+ * whatever = ${common-value}
+ * }
+ *
+ *
+ *
+ * Many methods on {@link ConfigFactory} such as
+ * {@link ConfigFactory#load()} automatically resolve the loaded
+ * Config on the loaded stack of config files.
+ *
+ *
+ * Resolving an already-resolved config is a harmless no-op, but again, it
+ * is best to resolve an entire stack of fallbacks (such as all your config
+ * files combined) rather than resolving each one individually.
+ *
+ * @return an immutable object with substitutions resolved
+ * @throws ConfigException.UnresolvedSubstitution
+ * if any substitutions refer to nonexistent paths
+ * @throws ConfigException
+ * some other config exception if there are other problems
+ */
+ Config resolve();
+
+ /**
+ * Like {@link Config#resolve()} but allows you to specify non-default
+ * options.
+ *
+ * @param options
+ * resolve options
+ * @return the resolved Config (may be only partially resolved if options are set to allow unresolved)
+ */
+ Config resolve(ConfigResolveOptions options);
+
+ /**
+ * Checks whether the config is completely resolved. After a successful call
+ * to {@link Config#resolve()} it will be completely resolved, but after
+ * calling {@link Config#resolve(ConfigResolveOptions)} with
+ * allowUnresolved set in the options, it may or may not be
+ * completely resolved. A newly-loaded config may or may not be completely
+ * resolved depending on whether there were substitutions present in the
+ * file.
+ *
+ * @return true if there are no unresolved substitutions remaining in this
+ * configuration.
+ * @since 1.2.0
+ */
+ boolean isResolved();
+
+ /**
+ * Like {@link Config#resolve()} except that substitution values are looked
+ * up in the given source, rather than in this instance. This is a
+ * special-purpose method which doesn't make sense to use in most cases;
+ * it's only needed if you're constructing some sort of app-specific custom
+ * approach to configuration. The more usual approach if you have a source
+ * of substitution values would be to merge that source into your config
+ * stack using {@link Config#withFallback} and then resolve.
+ *
+ * Note that this method does NOT look in this instance for substitution
+ * values. If you want to do that, you could either merge this instance into
+ * your value source using {@link Config#withFallback}, or you could resolve
+ * multiple times with multiple sources (using
+ * {@link ConfigResolveOptions#setAllowUnresolved(boolean)} so the partial
+ * resolves don't fail).
+ *
+ * @param source
+ * configuration to pull values from
+ * @return an immutable object with substitutions resolved
+ * @throws ConfigException.UnresolvedSubstitution
+ * if any substitutions refer to paths which are not in the
+ * source
+ * @throws ConfigException
+ * some other config exception if there are other problems
+ * @since 1.2.0
+ */
+ Config resolveWith(Config source);
+
+ /**
+ * Like {@link Config#resolveWith(Config)} but allows you to specify
+ * non-default options.
+ *
+ * @param source
+ * source configuration to pull values from
+ * @param options
+ * resolve options
+ * @return the resolved Config (may be only partially resolved
+ * if options are set to allow unresolved)
+ * @since 1.2.0
+ */
+ Config resolveWith(Config source, ConfigResolveOptions options);
+
+ /**
+ * Validates this config against a reference config, throwing an exception
+ * if it is invalid. The purpose of this method is to "fail early" with a
+ * comprehensive list of problems; in general, anything this method can find
+ * would be detected later when trying to use the config, but it's often
+ * more user-friendly to fail right away when loading the config.
+ *
+ *
+ * Using this method is always optional, since you can "fail late" instead.
+ *
+ *
+ * You must restrict validation to paths you "own" (those whose meaning are
+ * defined by your code module). If you validate globally, you may trigger
+ * errors about paths that happen to be in the config but have nothing to do
+ * with your module. It's best to allow the modules owning those paths to
+ * validate them. Also, if every module validates only its own stuff, there
+ * isn't as much redundant work being done.
+ *
+ *
+ * If no paths are specified in checkValid()'s parameter list,
+ * validation is for the entire config.
+ *
+ *
+ * If you specify paths that are not in the reference config, those paths
+ * are ignored. (There's nothing to validate.)
+ *
+ *
+ * Here's what validation involves:
+ *
+ *
+ * - All paths found in the reference config must be present in this
+ * config or an exception will be thrown.
+ *
-
+ * Some changes in type from the reference config to this config will cause
+ * an exception to be thrown. Not all potential type problems are detected,
+ * in particular it's assumed that strings are compatible with everything
+ * except objects and lists. This is because string types are often "really"
+ * some other type (system properties always start out as strings, or a
+ * string like "5ms" could be used with {@link #getMilliseconds}). Also,
+ * it's allowed to set any type to null or override null with any type.
+ *
-
+ * Any unresolved substitutions in this config will cause a validation
+ * failure; both the reference config and this config should be resolved
+ * before validation. If the reference config is unresolved, it's a bug in
+ * the caller of this method.
+ *
+ *
+ *
+ * If you want to allow a certain setting to have a flexible type (or
+ * otherwise want validation to be looser for some settings), you could
+ * either remove the problematic setting from the reference config provided
+ * to this method, or you could intercept the validation exception and
+ * screen out certain problems. Of course, this will only work if all other
+ * callers of this method are careful to restrict validation to their own
+ * paths, as they should be.
+ *
+ *
+ * If validation fails, the thrown exception contains a list of all problems
+ * found. See {@link ConfigException.ValidationFailed#problems}. The
+ * exception's getMessage() will have all the problems
+ * concatenated into one huge string, as well.
+ *
+ *
+ * Again, checkValid() can't guess every domain-specific way a
+ * setting can be invalid, so some problems may arise later when attempting
+ * to use the config. checkValid() is limited to reporting
+ * generic, but common, problems such as missing settings and blatant type
+ * incompatibilities.
+ *
+ * @param reference
+ * a reference configuration
+ * @param restrictToPaths
+ * only validate values underneath these paths that your code
+ * module owns and understands
+ * @throws ConfigException.ValidationFailed
+ * if there are any validation issues
+ * @throws ConfigException.NotResolved
+ * if this config is not resolved
+ * @throws ConfigException.BugOrBroken
+ * if the reference config is unresolved or caller otherwise
+ * misuses the API
+ */
+ void checkValid(Config reference, String... restrictToPaths);
+
+ /**
+ * Checks whether a value is present and non-null at the given path. This
+ * differs in two ways from {@code Map.containsKey()} as implemented by
+ * {@link ConfigObject}: it looks for a path expression, not a key; and it
+ * returns false for null values, while {@code containsKey()} returns true
+ * indicating that the object contains a null value for the key.
+ *
+ *
+ * If a path exists according to {@link #hasPath(String)}, then
+ * {@link #getValue(String)} will never throw an exception. However, the
+ * typed getters, such as {@link #getInt(String)}, will still throw if the
+ * value is not convertible to the requested type.
+ *
+ *
+ * Note that path expressions have a syntax and sometimes require quoting
+ * (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @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);
+
+ /**
+ * Checks whether a value is present at the given path, even
+ * if the value is null. Most of the getters on
+ * Config will throw if you try to get a null
+ * value, so if you plan to call {@link #getValue(String)},
+ * {@link #getInt(String)}, or another getter you may want to
+ * use plain {@link #hasPath(String)} rather than this method.
+ *
+ *
+ * To handle all three cases (unset, null, and a non-null value)
+ * the code might look like:
+ *
+ * if (config.hasPathOrNull(path)) {
+ * if (config.getIsNull(path)) {
+ * // handle null setting
+ * } else {
+ * // get and use non-null setting
+ * }
+ * } else {
+ * // handle entirely unset path
+ * }
+ *
+ *
+ * However, the usual thing is to allow entirely unset
+ * paths to be a bug that throws an exception (because you set
+ * a default in your reference.conf), so in that
+ * case it's OK to call {@link #getIsNull(String)} without
+ * checking hasPathOrNull first.
+ *
+ *
+ * Note that path expressions have a syntax and sometimes require quoting
+ * (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @param path
+ * the path expression
+ * @return true if a value is present at the path, even if the value is null
+ * @throws ConfigException.BadPath
+ * if the path expression is invalid
+ */
+ boolean hasPathOrNull(String path);
+
+ /**
+ * Returns true if the {@code Config}'s root object contains no key-value
+ * pairs.
+ *
+ * @return true if the configuration is empty
+ */
+ boolean isEmpty();
+
+ /**
+ * Returns the set of path-value pairs, excluding any null values, found by
+ * recursing {@link #root() the root object}. Note that this is very
+ * different from root().entrySet() which returns the set of
+ * immediate-child keys in the root object and includes null values.
+ *
+ * Entries contain path expressions meaning there may be quoting
+ * and escaping involved. Parse path expressions with
+ * {@link ConfigUtil#splitPath}.
+ *
+ * Because a Config is conceptually a single-level map from
+ * paths to values, there will not be any {@link ConfigObject} values in the
+ * entries (that is, all entries represent leaf nodes). Use
+ * {@link ConfigObject} rather than Config if you want a tree.
+ * (OK, this is a slight lie: Config entries may contain
+ * {@link ConfigList} and the lists may contain objects. But no objects are
+ * directly included as entry values.)
+ *
+ * @return set of paths with non-null values, built up by recursing the
+ * entire tree of {@link ConfigObject} and creating an entry for
+ * each leaf value.
+ */
+ Set> entrySet();
+
+ /**
+ * Checks whether a value is set to null at the given path,
+ * but throws an exception if the value is entirely
+ * unset. This method will not throw if {@link
+ * #hasPathOrNull(String)} returned true for the same path, so
+ * to avoid any possible exception check
+ * hasPathOrNull() first. However, an exception
+ * for unset paths will usually be the right thing (because a
+ * reference.conf should exist that has the path
+ * set, the path should never be unset unless something is
+ * broken).
+ *
+ *
+ * Note that path expressions have a syntax and sometimes require quoting
+ * (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @param path
+ * the path expression
+ * @return true if the value exists and is null, false if it
+ * exists and is not null
+ * @throws ConfigException.BadPath
+ * if the path expression is invalid
+ * @throws ConfigException.Missing
+ * if value is not set at all
+ */
+ boolean getIsNull(String path);
+
+ /**
+ *
+ * @param path
+ * path expression
+ * @return the boolean value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to boolean
+ */
+ boolean getBoolean(String path);
+
+ /**
+ * @param path
+ * path expression
+ * @return the numeric value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a number
+ */
+ Number getNumber(String path);
+
+ /**
+ * Gets the integer at the given path. If the value at the
+ * path has a fractional (floating point) component, it
+ * will be discarded and only the integer part will be
+ * returned (it works like a "narrowing primitive conversion"
+ * in the Java language specification).
+ *
+ * @param path
+ * path expression
+ * @return the 32-bit integer value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to an int (for example it is out
+ * of range, or it's a boolean value)
+ */
+ int getInt(String path);
+
+ /**
+ * Gets the long integer at the given path. If the value at
+ * the path has a fractional (floating point) component, it
+ * will be discarded and only the integer part will be
+ * returned (it works like a "narrowing primitive conversion"
+ * in the Java language specification).
+ *
+ * @param path
+ * path expression
+ * @return the 64-bit long value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a long
+ */
+ long getLong(String path);
+
+ /**
+ * @param path
+ * path expression
+ * @return the floating-point value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a double
+ */
+ double getDouble(String path);
+
+ /**
+ * @param path
+ * path expression
+ * @return the string value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a string
+ */
+ String getString(String path);
+
+ /**
+ * @param enumClass
+ * an enum class
+ * @param
+ * a generic denoting a specific type of enum
+ * @param path
+ * path expression
+ * @return the {@code Enum} value at the requested path
+ * of the requested enum class
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to an Enum
+ */
+ public > T getEnum(Class enumClass, String path);
+
+ /**
+ * @param path
+ * path expression
+ * @return the {@link ConfigObject} value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to an object
+ */
+ ConfigObject getObject(String path);
+
+ /**
+ * @param path
+ * path expression
+ * @return the nested {@code Config} value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a Config
+ */
+ Config getConfig(String path);
+
+ /**
+ * Gets the value at the path as an unwrapped Java boxed value (
+ * {@link java.lang.Boolean Boolean}, {@link java.lang.Integer Integer}, and
+ * so on - see {@link ConfigValue#unwrapped()}).
+ *
+ * @param path
+ * path expression
+ * @return the unwrapped value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ */
+ 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 {@code get()} on the {@link
+ * Config#root()} object (or other object in the tree) if you
+ * want an unprocessed value.
+ *
+ * @param path
+ * path expression
+ * @return the value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ */
+ ConfigValue getValue(String path);
+
+ /**
+ * Gets a value as a size in bytes (parses special strings like "128M"). If
+ * the value is already a number, then it's left alone; if it's a string,
+ * it's parsed understanding unit suffixes such as "128K", as documented in
+ * the the
+ * spec.
+ *
+ * @param path
+ * path expression
+ * @return the value at the requested path, in bytes
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a size in bytes
+ */
+ Long getBytes(String path);
+
+ /**
+ * Gets a value as an amount of memory (parses special strings like "128M"). If
+ * the value is already a number, then it's left alone; if it's a string,
+ * it's parsed understanding unit suffixes such as "128K", as documented in
+ * the the
+ * spec.
+ *
+ * @since 1.3.0
+ *
+ * @param path
+ * path expression
+ * @return the value at the requested path, in bytes
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a size in bytes
+ */
+ ConfigMemorySize getMemorySize(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" as documented in the the
+ * spec.
+ *
+ * @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)}
+ *
+ * @param path
+ * path expression
+ * @return the duration value at the requested path, in milliseconds
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a number of milliseconds
+ */
+ @Deprecated 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, as for
+ * {@link #getDuration(String, TimeUnit)}.
+ *
+ * @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)}
+ *
+ * @param path
+ * path expression
+ * @return the duration value at the requested path, in nanoseconds
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a number of nanoseconds
+ */
+ @Deprecated Long getNanoseconds(String path);
+
+ /**
+ * Gets a value as a duration in a specified
+ * {@link java.util.concurrent.TimeUnit TimeUnit}. If the value is already a
+ * number, then it's taken as milliseconds and then converted to the
+ * requested TimeUnit; if it's a string, it's parsed understanding units
+ * suffixes like "10m" or "5ns" as documented in the the
+ * spec.
+ *
+ * @since 1.2.0
+ *
+ * @param path
+ * path expression
+ * @param unit
+ * convert the return value to this time unit
+ * @return the duration value at the requested path, in the given TimeUnit
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a number of the given TimeUnit
+ */
+ long getDuration(String path, TimeUnit unit);
+
+ /**
+ * Gets a value as a java.time.Duration. If the value is
+ * already a number, then it's taken as milliseconds; if it's
+ * a string, it's parsed understanding units suffixes like
+ * "10m" or "5ns" as documented in the the
+ * spec. This method never returns null.
+ *
+ * @since 1.3.0
+ *
+ * @param path
+ * path expression
+ * @return the duration value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a number of the given TimeUnit
+ */
+ Duration getDuration(String path);
+
+ /**
+ * Gets a value as a java.time.Period. If the value is
+ * already a number, then it's taken as days; if it's
+ * a string, it's parsed understanding units suffixes like
+ * "10d" or "5w" as documented in the the
+ * spec. This method never returns null.
+ *
+ * @since 1.3.0
+ *
+ * @param path
+ * path expression
+ * @return the period value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a number of the given TimeUnit
+ */
+ Period getPeriod(String path);
+
+ /**
+ * Gets a value as a java.time.temporal.TemporalAmount.
+ * This method will first try get get the value as a java.time.Duration, and if unsuccessful,
+ * then as a java.time.Period.
+ * This means that values like "5m" will be parsed as 5 minutes rather than 5 months
+ * @param path path expression
+ * @return the temporal value at the requested path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to Long or String
+ * @throws ConfigException.BadValue
+ * if value cannot be parsed as a TemporalAmount
+ */
+ TemporalAmount getTemporal(String path);
+
+ /**
+ * Gets a list value (with any element type) as a {@link ConfigList}, which
+ * implements {@code java.util.List}. Throws if the path is
+ * unset or null.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the {@link ConfigList} at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a ConfigList
+ */
+ ConfigList getList(String path);
+
+ /**
+ * Gets a list value with boolean elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to boolean.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of booleans
+ */
+ List getBooleanList(String path);
+
+ /**
+ * Gets a list value with number elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to number.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of numbers
+ */
+ List getNumberList(String path);
+
+ /**
+ * Gets a list value with int elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to int.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of ints
+ */
+ List getIntList(String path);
+
+ /**
+ * Gets a list value with long elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to long.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of longs
+ */
+ List getLongList(String path);
+
+ /**
+ * Gets a list value with double elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to double.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of doubles
+ */
+ List getDoubleList(String path);
+
+ /**
+ * Gets a list value with string elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to string.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of strings
+ */
+ List getStringList(String path);
+
+ /**
+ * Gets a list value with {@code Enum} elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to {@code Enum}.
+ *
+ * @param enumClass
+ * the enum class
+ * @param
+ * a generic denoting a specific type of enum
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of {@code Enum}
+ */
+ > List getEnumList(Class enumClass, String path);
+
+ /**
+ * Gets a list value with object elements. Throws if the
+ * path is unset or null or not a list or contains values not
+ * convertible to ConfigObject.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of objects
+ */
+ List extends ConfigObject> getObjectList(String path);
+
+ /**
+ * Gets a list value with Config elements.
+ * Throws if the path is unset or null or not a list or
+ * contains values not convertible to Config.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of configs
+ */
+ List extends Config> getConfigList(String path);
+
+ /**
+ * Gets a list value with any kind of elements. Throws if the
+ * path is unset or null or not a list. Each element is
+ * "unwrapped" (see {@link ConfigValue#unwrapped()}).
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list
+ */
+ List extends Object> getAnyRefList(String path);
+
+ /**
+ * Gets a list value with elements representing a size in
+ * bytes. Throws if the path is unset or null or not a list
+ * or contains values not convertible to memory sizes.
+ *
+ * @param path
+ * the path to the list value.
+ * @return the list at the path
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of memory sizes
+ */
+ List getBytesList(String path);
+
+ /**
+ * Gets a list, converting each value in the list to a memory size, using the
+ * same rules as {@link #getMemorySize(String)}.
+ *
+ * @since 1.3.0
+ * @param path
+ * a path expression
+ * @return list of memory sizes
+ * @throws ConfigException.Missing
+ * if value is absent or null
+ * @throws ConfigException.WrongType
+ * if value is not convertible to a list of memory sizes
+ */
+ List getMemorySizeList(String path);
+
+ /**
+ * @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)}
+ * @param path the path
+ * @return list of millisecond values
+ */
+ @Deprecated List getMillisecondsList(String path);
+
+ /**
+ * @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)}
+ * @param path the path
+ * @return list of nanosecond values
+ */
+ @Deprecated List getNanosecondsList(String path);
+
+ /**
+ * Gets a list, converting each value in the list to a duration, using the
+ * same rules as {@link #getDuration(String, TimeUnit)}.
+ *
+ * @since 1.2.0
+ * @param path
+ * a path expression
+ * @param unit
+ * time units of the returned values
+ * @return list of durations, in the requested units
+ */
+ List getDurationList(String path, TimeUnit unit);
+
+ /**
+ * Gets a list, converting each value in the list to a duration, using the
+ * same rules as {@link #getDuration(String)}.
+ *
+ * @since 1.3.0
+ * @param path
+ * a path expression
+ * @return list of durations
+ */
+ List getDurationList(String path);
+
+ /**
+ * Clone the config with only the given path (and its children) retained;
+ * all sibling paths are removed.
+ *
+ * Note that path expressions have a syntax and sometimes require quoting
+ * (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @param path
+ * path to keep
+ * @return a copy of the config minus all paths except the one specified
+ */
+ Config withOnlyPath(String path);
+
+ /**
+ * Clone the config with the given path removed.
+ *
+ * Note that path expressions have a syntax and sometimes require quoting
+ * (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @param path
+ * path expression to remove
+ * @return a copy of the config minus the specified path
+ */
+ Config withoutPath(String path);
+
+ /**
+ * Places the config inside another {@code Config} at the given path.
+ *
+ * Note that path expressions have a syntax and sometimes require quoting
+ * (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @param path
+ * path expression to store this config at.
+ * @return a {@code Config} instance containing this config at the given
+ * path.
+ */
+ Config atPath(String path);
+
+ /**
+ * Places the config inside a {@code Config} at the given key. See also
+ * atPath(). Note that a key is NOT a path expression (see
+ * {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @param key
+ * key to store this config at.
+ * @return a {@code Config} instance containing this config at the given
+ * key.
+ */
+ Config atKey(String key);
+
+ /**
+ * Returns a {@code Config} based on this one, but with the given path set
+ * to the given value. Does not modify this instance (since it's immutable).
+ * If the path already has a value, that value is replaced. To remove a
+ * value, use withoutPath().
+ *
+ * Note that path expressions have a syntax and sometimes require quoting
+ * (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
+ *
+ * @param path
+ * path expression for the value's new location
+ * @param value
+ * value at the new path
+ * @return the new instance with the new map entry
+ */
+ Config withValue(String path, ConfigValue value);
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigBeanFactory.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigBeanFactory.java
new file mode 100644
index 0000000..d246dc2
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigBeanFactory.java
@@ -0,0 +1,49 @@
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigBeanImpl;
+
+/**
+ * Factory for automatically creating a Java class from a {@link Config}.
+ * See {@link #create(Config,Class)}.
+ *
+ * @since 1.3.0
+ */
+public class ConfigBeanFactory {
+
+ /**
+ * Creates an instance of a class, initializing its fields from a {@link Config}.
+ *
+ * Example usage:
+ *
+ *
+ * Config configSource = ConfigFactory.load().getConfig("foo");
+ * FooConfig config = ConfigBeanFactory.create(configSource, FooConfig.class);
+ *
+ *
+ * The Java class should follow JavaBean conventions. Field types
+ * can be any of the types you can normally get from a {@link
+ * Config}, including java.time.Duration or {@link
+ * ConfigMemorySize}. Fields may also be another JavaBean-style
+ * class.
+ *
+ * Fields are mapped to config by converting the config key to
+ * camel case. So the key foo-bar becomes JavaBean
+ * setter setFooBar.
+ *
+ * @since 1.3.0
+ *
+ * @param config source of config information
+ * @param clazz class to be instantiated
+ * @param the type of the class to be instantiated
+ * @return an instance of the class populated with data from the config
+ * @throws ConfigException.BadBean
+ * If something is wrong with the JavaBean
+ * @throws ConfigException.ValidationFailed
+ * If the config doesn't conform to the bean's implied schema
+ * @throws ConfigException
+ * Can throw the same exceptions as the getters on Config
+ */
+ public static T create(Config config, Class clazz) {
+ return ConfigBeanImpl.createInternal(config, clazz);
+ }
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigException.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigException.java
new file mode 100644
index 0000000..d5feb6c
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigException.java
@@ -0,0 +1,447 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+
+import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigImplUtil;
+
+/**
+ * All exceptions thrown by the library are subclasses of
+ * ConfigException.
+ */
+public abstract class ConfigException extends RuntimeException implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ final private transient ConfigOrigin origin;
+
+ protected ConfigException(ConfigOrigin origin, String message,
+ Throwable cause) {
+ super(origin.description() + ": " + message, cause);
+ this.origin = origin;
+ }
+
+ protected ConfigException(ConfigOrigin origin, String message) {
+ this(origin.description() + ": " + message, null);
+ }
+
+ protected ConfigException(String message, Throwable cause) {
+ super(message, cause);
+ this.origin = null;
+ }
+
+ protected ConfigException(String message) {
+ this(message, null);
+ }
+
+ /**
+ * Returns an "origin" (such as a filename and line number) for the
+ * exception, or null if none is available. If there's no sensible origin
+ * for a given exception, or the kind of exception doesn't meaningfully
+ * relate to a particular origin file, this returns null. Never assume this
+ * will return non-null, it can always return null.
+ *
+ * @return origin of the problem, or null if unknown/inapplicable
+ */
+ public ConfigOrigin origin() {
+ return origin;
+ }
+
+ // we customize serialization because ConfigOrigin isn't
+ // serializable and we don't want it to be (don't want to
+ // support it)
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ ConfigImplUtil.writeOrigin(out, origin);
+ }
+
+ // For deserialization - uses reflection to set the final origin field on the object
+ private static void setOriginField(T hasOriginField, Class clazz,
+ ConfigOrigin origin) throws IOException {
+ // circumvent "final"
+ Field f;
+ try {
+ f = clazz.getDeclaredField("origin");
+ } catch (NoSuchFieldException e) {
+ throw new IOException(clazz.getSimpleName() + " has no origin field?", e);
+ } catch (SecurityException e) {
+ throw new IOException("unable to fill out origin field in " +
+ clazz.getSimpleName(), e);
+ }
+ f.setAccessible(true);
+ try {
+ f.set(hasOriginField, origin);
+ } catch (IllegalArgumentException e) {
+ throw new IOException("unable to set origin field", e);
+ } catch (IllegalAccessException e) {
+ throw new IOException("unable to set origin field", e);
+ }
+ }
+
+ private void readObject(java.io.ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
+ setOriginField(this, ConfigException.class, origin);
+ }
+
+ /**
+ * Exception indicating that the type of a value does not match the type you
+ * requested.
+ *
+ */
+ public static class WrongType extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public WrongType(ConfigOrigin origin, String path, String expected, String actual,
+ Throwable cause) {
+ super(origin, path + " has type " + actual + " rather than " + expected, cause);
+ }
+
+ public WrongType(ConfigOrigin origin, String path, String expected, String actual) {
+ this(origin, path, expected, actual, null);
+ }
+
+ public WrongType(ConfigOrigin origin, String message, Throwable cause) {
+ super(origin, message, cause);
+ }
+
+ public WrongType(ConfigOrigin origin, String message) {
+ super(origin, message, null);
+ }
+ }
+
+ /**
+ * Exception indicates that the setting was never set to anything, not even
+ * null.
+ */
+ public static class Missing extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public Missing(String path, Throwable cause) {
+ super("No configuration setting found for key '" + path + "'",
+ cause);
+ }
+
+ public Missing(String path) {
+ this(path, null);
+ }
+
+ protected Missing(ConfigOrigin origin, String message, Throwable cause) {
+ super(origin, message, cause);
+ }
+
+ protected Missing(ConfigOrigin origin, String message) {
+ this(origin, message, null);
+ }
+ }
+
+ /**
+ * Exception indicates that the setting was treated as missing because it
+ * was set to null.
+ */
+ public static class Null extends Missing {
+ private static final long serialVersionUID = 1L;
+
+ private static String makeMessage(String path, String expected) {
+ if (expected != null) {
+ return "Configuration key '" + path
+ + "' is set to null but expected " + expected;
+ } else {
+ return "Configuration key '" + path + "' is null";
+ }
+ }
+
+ public Null(ConfigOrigin origin, String path, String expected,
+ Throwable cause) {
+ super(origin, makeMessage(path, expected), cause);
+ }
+
+ public Null(ConfigOrigin origin, String path, String expected) {
+ this(origin, path, expected, null);
+ }
+ }
+
+ /**
+ * Exception indicating that a value was messed up, for example you may have
+ * asked for a duration and the value can't be sensibly parsed as a
+ * duration.
+ *
+ */
+ public static class BadValue extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public BadValue(ConfigOrigin origin, String path, String message,
+ Throwable cause) {
+ super(origin, "Invalid value at '" + path + "': " + message, cause);
+ }
+
+ public BadValue(ConfigOrigin origin, String path, String message) {
+ this(origin, path, message, null);
+ }
+
+ public BadValue(String path, String message, Throwable cause) {
+ super("Invalid value at '" + path + "': " + message, cause);
+ }
+
+ public BadValue(String path, String message) {
+ this(path, message, null);
+ }
+ }
+
+ /**
+ * Exception indicating that a path expression was invalid. Try putting
+ * double quotes around path elements that contain "special" characters.
+ *
+ */
+ public static class BadPath extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public BadPath(ConfigOrigin origin, String path, String message,
+ Throwable cause) {
+ super(origin,
+ path != null ? ("Invalid path '" + path + "': " + message)
+ : message, cause);
+ }
+
+ public BadPath(ConfigOrigin origin, String path, String message) {
+ this(origin, path, message, null);
+ }
+
+ public BadPath(String path, String message, Throwable cause) {
+ super(path != null ? ("Invalid path '" + path + "': " + message)
+ : message, cause);
+ }
+
+ public BadPath(String path, String message) {
+ this(path, message, null);
+ }
+
+ public BadPath(ConfigOrigin origin, String message) {
+ this(origin, null, message);
+ }
+ }
+
+ /**
+ * Exception indicating that there's a bug in something (possibly the
+ * library itself) or the runtime environment is broken. This exception
+ * should never be handled; instead, something should be fixed to keep the
+ * exception from occurring. This exception can be thrown by any method in
+ * the library.
+ */
+ public static class BugOrBroken extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public BugOrBroken(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public BugOrBroken(String message) {
+ this(message, null);
+ }
+ }
+
+ /**
+ * Exception indicating that there was an IO error.
+ *
+ */
+ public static class IO extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public IO(ConfigOrigin origin, String message, Throwable cause) {
+ super(origin, message, cause);
+ }
+
+ public IO(ConfigOrigin origin, String message) {
+ this(origin, message, null);
+ }
+ }
+
+ /**
+ * Exception indicating that there was a parse error.
+ *
+ */
+ public static class Parse extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public Parse(ConfigOrigin origin, String message, Throwable cause) {
+ super(origin, message, cause);
+ }
+
+ public Parse(ConfigOrigin origin, String message) {
+ this(origin, message, null);
+ }
+ }
+
+ /**
+ * Exception indicating that a substitution did not resolve to anything.
+ * Thrown by {@link Config#resolve}.
+ */
+ public static class UnresolvedSubstitution extends Parse {
+ private static final long serialVersionUID = 1L;
+
+ public UnresolvedSubstitution(ConfigOrigin origin, String detail, Throwable cause) {
+ super(origin, "Could not resolve substitution to a value: " + detail, cause);
+ }
+
+ public UnresolvedSubstitution(ConfigOrigin origin, String detail) {
+ this(origin, detail, null);
+ }
+ }
+
+ /**
+ * Exception indicating that you tried to use a function that requires
+ * substitutions to be resolved, but substitutions have not been resolved
+ * (that is, {@link Config#resolve} was not called). This is always a bug in
+ * either application code or the library; it's wrong to write a handler for
+ * this exception because you should be able to fix the code to avoid it by
+ * adding calls to {@link Config#resolve}.
+ */
+ public static class NotResolved extends BugOrBroken {
+ private static final long serialVersionUID = 1L;
+
+ public NotResolved(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public NotResolved(String message) {
+ this(message, null);
+ }
+ }
+
+ /**
+ * Information about a problem that occurred in {@link Config#checkValid}. A
+ * {@link ConfigException.ValidationFailed} exception thrown from
+ * checkValid() includes a list of problems encountered.
+ */
+ public static class ValidationProblem implements Serializable {
+
+ final private String path;
+ final private transient ConfigOrigin origin;
+ final private String problem;
+
+ public ValidationProblem(String path, ConfigOrigin origin, String problem) {
+ this.path = path;
+ this.origin = origin;
+ this.problem = problem;
+ }
+
+ /**
+ * Returns the config setting causing the problem.
+ * @return the path of the problem setting
+ */
+ public String path() {
+ return path;
+ }
+
+ /**
+ * Returns where the problem occurred (origin may include info on the
+ * file, line number, etc.).
+ * @return the origin of the problem setting
+ */
+ public ConfigOrigin origin() {
+ return origin;
+ }
+
+ /**
+ * Returns a description of the problem.
+ * @return description of the problem
+ */
+ public String problem() {
+ return problem;
+ }
+
+ // We customize serialization because ConfigOrigin isn't
+ // serializable and we don't want it to be
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ ConfigImplUtil.writeOrigin(out, origin);
+ }
+
+ private void readObject(java.io.ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
+ setOriginField(this, ValidationProblem.class, origin);
+ }
+
+ @Override
+ public String toString() {
+ return "ValidationProblem(" + path + "," + origin + "," + problem + ")";
+ }
+ }
+
+ /**
+ * Exception indicating that {@link Config#checkValid} found validity
+ * problems. The problems are available via the {@link #problems()} method.
+ * The getMessage() of this exception is a potentially very
+ * long string listing all the problems found.
+ */
+ public static class ValidationFailed extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ final private Iterable problems;
+
+ public ValidationFailed(Iterable problems) {
+ super(makeMessage(problems), null);
+ this.problems = problems;
+ }
+
+ public Iterable problems() {
+ return problems;
+ }
+
+ private static String makeMessage(Iterable problems) {
+ StringBuilder sb = new StringBuilder();
+ for (ValidationProblem p : problems) {
+ sb.append(p.origin().description());
+ sb.append(": ");
+ sb.append(p.path());
+ sb.append(": ");
+ sb.append(p.problem());
+ sb.append(", ");
+ }
+ if (sb.length() == 0)
+ throw new ConfigException.BugOrBroken(
+ "ValidationFailed must have a non-empty list of problems");
+ sb.setLength(sb.length() - 2); // chop comma and space
+
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Some problem with a JavaBean we are trying to initialize.
+ * @since 1.3.0
+ */
+ public static class BadBean extends BugOrBroken {
+ private static final long serialVersionUID = 1L;
+
+ public BadBean(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public BadBean(String message) {
+ this(message, null);
+ }
+ }
+
+ /**
+ * Exception that doesn't fall into any other category.
+ */
+ public static class Generic extends ConfigException {
+ private static final long serialVersionUID = 1L;
+
+ public Generic(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public Generic(String message) {
+ this(message, null);
+ }
+ }
+
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigFactory.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigFactory.java
new file mode 100644
index 0000000..eecb9e1
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigFactory.java
@@ -0,0 +1,1066 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+import com.drtshock.playervaults.lib.com.typesafe.config.impl.ConfigImpl;
+import com.drtshock.playervaults.lib.com.typesafe.config.impl.Parseable;
+
+import java.io.File;
+import java.io.Reader;
+import java.net.URL;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+
+/**
+ * Contains static methods for creating {@link Config} instances.
+ *
+ *
+ * See also {@link ConfigValueFactory} which contains static methods for
+ * converting Java values into a {@link ConfigObject}. You can then convert a
+ * {@code ConfigObject} into a {@code Config} with {@link ConfigObject#toConfig}.
+ *
+ *
+ * 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 {@link ConfigValue}
+ * from a resource and nothing else.
+ *
+ *
You can find an example app and library on
+ * GitHub. Also be sure to read the package
+ * overview which describes the big picture as shown in those
+ * examples.
+ */
+public final class ConfigFactory {
+ private static final String STRATEGY_PROPERTY_NAME = "config.strategy";
+
+ private ConfigFactory() {
+ }
+
+ /**
+ * Loads an application's configuration from the given classpath resource or
+ * classpath resource basename, sandwiches it between default reference
+ * config and default overrides, and then resolves it. The classpath
+ * resource is "raw" (it should have no "/" prefix, and is not made relative
+ * to any package, so it's like {@link ClassLoader#getResource} not
+ * {@link Class#getResource}).
+ *
+ *
+ * Resources are loaded from the current thread's
+ * {@link Thread#getContextClassLoader()}. In general, a library needs its
+ * configuration to come from the class loader used to load that library, so
+ * the proper "reference.conf" are present.
+ *
+ *
+ * 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
+ * {@link #load(Config)}.
+ *
+ *
+ * To load a standalone resource (without the default reference and default
+ * overrides), use {@link #parseResourcesAnySyntax(String)} rather than this
+ * method. To load only the reference config use {@link #defaultReference()}
+ * and to load only the overrides use {@link #defaultOverrides()}.
+ *
+ * @param resourceBasename
+ * name (optionally without extension) of a resource on classpath
+ * @return configuration for an application relative to context class loader
+ */
+ public static Config load(String resourceBasename) {
+ return load(resourceBasename, ConfigParseOptions.defaults(),
+ ConfigResolveOptions.defaults());
+ }
+
+ /**
+ * Like {@link #load(String)} but uses the supplied class loader instead of
+ * the current thread's context class loader.
+ *
+ *
+ * To load a standalone resource (without the default reference and default
+ * overrides), use {@link #parseResourcesAnySyntax(ClassLoader, String)}
+ * rather than this method. To load only the reference config use
+ * {@link #defaultReference(ClassLoader)} and to load only the overrides use
+ * {@link #defaultOverrides(ClassLoader)}.
+ *
+ * @param loader class loader to look for resources in
+ * @param resourceBasename basename (no .conf/.json/.properties suffix)
+ * @return configuration for an application relative to given class loader
+ */
+ public static Config load(ClassLoader loader, String resourceBasename) {
+ return load(resourceBasename, ConfigParseOptions.defaults().setClassLoader(loader),
+ ConfigResolveOptions.defaults());
+ }
+
+ /**
+ * Like {@link #load(String)} but allows you to specify parse and resolve
+ * options.
+ *
+ * @param resourceBasename
+ * the classpath resource name with optional extension
+ * @param parseOptions
+ * options to use when parsing the resource
+ * @param resolveOptions
+ * options to use when resolving the stack
+ * @return configuration for an application
+ */
+ public static Config load(String resourceBasename, ConfigParseOptions parseOptions,
+ ConfigResolveOptions resolveOptions) {
+ ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load");
+ Config appConfig = ConfigFactory.parseResourcesAnySyntax(resourceBasename, withLoader);
+ return load(withLoader.getClassLoader(), appConfig, resolveOptions);
+ }
+
+ /**
+ * Like {@link #load(String,ConfigParseOptions,ConfigResolveOptions)} but
+ * has a class loader parameter that overrides any from the
+ * {@code ConfigParseOptions}.
+ *
+ * @param loader
+ * class loader in which to find resources (overrides loader in
+ * parse options)
+ * @param resourceBasename
+ * the classpath resource name with optional extension
+ * @param parseOptions
+ * options to use when parsing the resource (class loader
+ * overridden)
+ * @param resolveOptions
+ * options to use when resolving the stack
+ * @return configuration for an application
+ */
+ public static Config load(ClassLoader loader, String resourceBasename,
+ ConfigParseOptions parseOptions, ConfigResolveOptions resolveOptions) {
+ return load(resourceBasename, parseOptions.setClassLoader(loader), resolveOptions);
+ }
+
+ private static ClassLoader checkedContextClassLoader(String methodName) {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if (loader == null)
+ throw new ConfigException.BugOrBroken("Context class loader is not set for the current thread; "
+ + "if Thread.currentThread().getContextClassLoader() returns null, you must pass a ClassLoader "
+ + "explicitly to ConfigFactory." + methodName);
+ else
+ return loader;
+ }
+
+ private static ConfigParseOptions ensureClassLoader(ConfigParseOptions options, String methodName) {
+ if (options.getClassLoader() == null)
+ return options.setClassLoader(checkedContextClassLoader(methodName));
+ else
+ return options;
+ }
+
+ /**
+ * Assembles a standard configuration using a custom Config
+ * object rather than loading "application.conf". The Config
+ * object will be sandwiched between the default reference config and
+ * default overrides and then resolved.
+ *
+ * @param config
+ * the application's portion of the configuration
+ * @return resolved configuration with overrides and fallbacks added
+ */
+ public static Config load(Config config) {
+ return load(checkedContextClassLoader("load"), config);
+ }
+
+ /**
+ * Like {@link #load(Config)} but allows you to specify
+ * the class loader for looking up resources.
+ *
+ * @param loader
+ * the class loader to use to find resources
+ * @param config
+ * the application's portion of the configuration
+ * @return resolved configuration with overrides and fallbacks added
+ */
+ public static Config load(ClassLoader loader, Config config) {
+ return load(loader, config, ConfigResolveOptions.defaults());
+ }
+
+ /**
+ * Like {@link #load(Config)} but allows you to specify
+ * {@link ConfigResolveOptions}.
+ *
+ * @param config
+ * the application's portion of the configuration
+ * @param resolveOptions
+ * options for resolving the assembled config stack
+ * @return resolved configuration with overrides and fallbacks added
+ */
+ public static Config load(Config config, ConfigResolveOptions resolveOptions) {
+ return load(checkedContextClassLoader("load"), config, resolveOptions);
+ }
+
+ /**
+ * Like {@link #load(Config,ConfigResolveOptions)} but allows you to specify
+ * a class loader other than the context class loader.
+ *
+ * @param loader
+ * class loader to use when looking up override and reference
+ * configs
+ * @param config
+ * the application's portion of the configuration
+ * @param resolveOptions
+ * options for resolving the assembled config stack
+ * @return resolved configuration with overrides and fallbacks added
+ */
+ public static Config load(ClassLoader loader, Config config, ConfigResolveOptions resolveOptions) {
+ return defaultOverrides(loader).withFallback(config).withFallback(defaultReference(loader))
+ .resolve(resolveOptions);
+ }
+
+
+
+ /**
+ * Loads a default configuration, equivalent to {@link #load(Config)
+ * load(defaultApplication())} in most cases. This configuration should be used by
+ * libraries and frameworks unless an application provides a different one.
+ *
+ * This method may return a cached singleton so will not see changes to
+ * system properties or config files. (Use {@link #invalidateCaches()} to
+ * force it to reload.)
+ *
+ * @return configuration for an application
+ */
+ public static Config load() {
+ ClassLoader loader = checkedContextClassLoader("load");
+ return load(loader);
+ }
+
+ /**
+ * Like {@link #load()} but allows specifying parse options.
+ *
+ * @param parseOptions
+ * Options for parsing resources
+ * @return configuration for an application
+ */
+ public static Config load(ConfigParseOptions parseOptions) {
+ return load(parseOptions, ConfigResolveOptions.defaults());
+ }
+
+ /**
+ * Like {@link #load()} but allows specifying a class loader other than the
+ * thread's current context class loader.
+ *
+ * @param loader
+ * class loader for finding resources
+ * @return configuration for an application
+ */
+ public static Config load(final ClassLoader loader) {
+ final ConfigParseOptions withLoader = ConfigParseOptions.defaults().setClassLoader(loader);
+ return ConfigImpl.computeCachedConfig(loader, "load", new Callable() {
+ @Override
+ public Config call() {
+ return load(loader, defaultApplication(withLoader));
+ }
+ });
+ }
+
+ /**
+ * Like {@link #load()} but allows specifying a class loader other than the
+ * thread's current context class loader and also specify parse options.
+ *
+ * @param loader
+ * class loader for finding resources (overrides any loader in parseOptions)
+ * @param parseOptions
+ * Options for parsing resources
+ * @return configuration for an application
+ */
+ public static Config load(ClassLoader loader, ConfigParseOptions parseOptions) {
+ return load(parseOptions.setClassLoader(loader));
+ }
+
+ /**
+ * Like {@link #load()} but allows specifying a class loader other than the
+ * thread's current context class loader and also specify resolve options.
+ *
+ * @param loader
+ * class loader for finding resources
+ * @param resolveOptions
+ * options for resolving the assembled config stack
+ * @return configuration for an application
+ */
+ public static Config load(ClassLoader loader, ConfigResolveOptions resolveOptions) {
+ return load(loader, ConfigParseOptions.defaults(), resolveOptions);
+ }
+
+
+ /**
+ * Like {@link #load()} but allows specifying a class loader other than the
+ * thread's current context class loader, parse options, and resolve options.
+ *
+ * @param loader
+ * class loader for finding resources (overrides any loader in parseOptions)
+ * @param parseOptions
+ * Options for parsing resources
+ * @param resolveOptions
+ * options for resolving the assembled config stack
+ * @return configuration for an application
+ */
+ public static Config load(ClassLoader loader, ConfigParseOptions parseOptions, ConfigResolveOptions resolveOptions) {
+ final ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load");
+ return load(loader, defaultApplication(withLoader), resolveOptions);
+ }
+
+ /**
+ * Like {@link #load()} but allows specifying parse options and resolve
+ * options.
+ *
+ * @param parseOptions
+ * Options for parsing resources
+ * @param resolveOptions
+ * options for resolving the assembled config stack
+ * @return configuration for an application
+ *
+ * @since 1.3.0
+ */
+ public static Config load(ConfigParseOptions parseOptions, final ConfigResolveOptions resolveOptions) {
+ final ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load");
+ return load(defaultApplication(withLoader), resolveOptions);
+ }
+
+ /**
+ * Obtains the default reference configuration, which is currently created
+ * by merging all resources "reference.conf" found on the classpath and
+ * overriding the result with system properties. The returned reference
+ * configuration will already have substitutions resolved.
+ *
+ *
+ * Libraries and frameworks should ship with a "reference.conf" in their
+ * jar.
+ *
+ *
+ * The reference config must be looked up in the class loader that contains
+ * the libraries that you want to use with this config, so the
+ * "reference.conf" for each library can be found. Use
+ * {@link #defaultReference(ClassLoader)} if the context class loader is not
+ * suitable.
+ *
+ *
+ * The {@link #load()} methods merge this configuration for you
+ * automatically.
+ *
+ *
+ * Future versions may look for reference configuration in more places. It
+ * is not guaranteed that this method only looks at
+ * "reference.conf".
+ *
+ * @return the default reference config for context class loader
+ */
+ public static Config defaultReference() {
+ return defaultReference(checkedContextClassLoader("defaultReference"));
+ }
+
+ /**
+ * Like {@link #defaultReference()} but allows you to specify a class loader
+ * to use rather than the current context class loader.
+ *
+ * @param loader class loader to look for resources in
+ * @return the default reference config for this class loader
+ */
+ public static Config defaultReference(ClassLoader loader) {
+ return ConfigImpl.defaultReference(loader);
+ }
+
+ /**
+ * Obtains the default override configuration, which currently consists of
+ * system properties. The returned override configuration will already have
+ * substitutions resolved.
+ *
+ *
+ * The {@link #load()} methods merge this configuration for you
+ * automatically.
+ *
+ *
+ * Future versions may get overrides in more places. It is not guaranteed
+ * that this method only uses system properties.
+ *
+ * @return the default override configuration
+ */
+ public static Config defaultOverrides() {
+ return systemProperties();
+ }
+
+ /**
+ * Like {@link #defaultOverrides()} but allows you to specify a class loader
+ * to use rather than the current context class loader.
+ *
+ * @param loader class loader to look for resources in
+ * @return the default override configuration
+ */
+ public static Config defaultOverrides(ClassLoader loader) {
+ return systemProperties();
+ }
+
+ /**
+ * Obtains the default application-specific configuration,
+ * which defaults to parsing application.conf,
+ * application.json, and
+ * application.properties on the classpath, but
+ * can also be rerouted using the config.file,
+ * config.resource, and config.url
+ * system properties.
+ *
+ *
The no-arguments {@link #load()} method automatically
+ * stacks the {@link #defaultReference()}, {@link
+ * #defaultApplication()}, and {@link #defaultOverrides()}
+ * configs. You would use defaultApplication()
+ * directly only if you're somehow customizing behavior by
+ * reimplementing load().
+ *
+ *
The configuration returned by
+ * defaultApplication() will not be resolved
+ * already, in contrast to defaultReference() and
+ * defaultOverrides(). This is because
+ * application.conf would normally be resolved after
+ * merging with the reference and override configs.
+ *
+ *
+ * If the system properties config.resource,
+ * config.file, or config.url are set, then the
+ * classpath resource, file, or URL specified in those properties will be
+ * used rather than the default
+ * application.{conf,json,properties} classpath resources.
+ * These system properties should not be set in code (after all, you can
+ * just parse whatever you want manually and then use {@link #load(Config)}
+ * if you don't want to use application.conf). The properties
+ * are intended for use by the person or script launching the application.
+ * For example someone might have a production.conf that
+ * include application.conf but then change a couple of values.
+ * When launching the app they could specify
+ * -Dconfig.resource=production.conf to get production mode.
+ *
+ *
+ * If no system properties are set to change the location of the default
+ * configuration, defaultApplication() is equivalent to
+ * ConfigFactory.parseResources("application").
+ *
+ * @since 1.3.0
+ *
+ * @return the default application.conf or system-property-configured configuration
+ */
+ public static Config defaultApplication() {
+ return defaultApplication(ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Like {@link #defaultApplication()} but allows you to specify a class loader
+ * to use rather than the current context class loader.
+ *
+ * @since 1.3.0
+ *
+ * @param loader class loader to look for resources in
+ * @return the default application configuration
+ */
+ public static Config defaultApplication(ClassLoader loader) {
+ return defaultApplication(ConfigParseOptions.defaults().setClassLoader(loader));
+ }
+
+ /**
+ * Like {@link #defaultApplication()} but allows you to specify parse options.
+ *
+ * @since 1.3.0
+ *
+ * @param options the options
+ * @return the default application configuration
+ */
+ public static Config defaultApplication(ConfigParseOptions options) {
+ return getConfigLoadingStrategy().parseApplicationConfig(ensureClassLoader(options, "defaultApplication"));
+ }
+
+ /**
+ * Reloads any cached configs, picking up changes to system properties for
+ * example. Because a {@link Config} is immutable, anyone with a reference
+ * to the old configs will still have the same outdated objects. However,
+ * new calls to {@link #load()} or {@link #defaultOverrides()} or
+ * {@link #defaultReference} may return a new object.
+ *
+ * This method is primarily intended for use in unit tests, for example,
+ * that may want to update a system property then confirm that it's used
+ * correctly. In many cases, use of this method may indicate there's a
+ * better way to set up your code.
+ *
+ * Caches may be reloaded immediately or lazily; once you call this method,
+ * the reload can occur at any time, even during the invalidation process.
+ * So FIRST make the changes you'd like the caches to notice, then SECOND
+ * call this method to invalidate caches. Don't expect that invalidating,
+ * making changes, then calling {@link #load()}, will work. Make changes
+ * before you invalidate.
+ */
+ public static void invalidateCaches() {
+ // We rely on this having the side effect that it drops
+ // all caches
+ ConfigImpl.reloadSystemPropertiesConfig();
+ ConfigImpl.reloadEnvVariablesConfig();
+ }
+
+ /**
+ * Gets an empty configuration. See also {@link #empty(String)} to create an
+ * empty configuration with a description, which may improve user-visible
+ * error messages.
+ *
+ * @return an empty configuration
+ */
+ public static Config empty() {
+ return empty(null);
+ }
+
+ /**
+ * Gets an empty configuration with a description to be used to create a
+ * {@link ConfigOrigin} for this Config. The description should
+ * be very short and say what the configuration is, like "default settings"
+ * or "foo settings" or something. (Presumably you will merge some actual
+ * settings into this empty config using {@link Config#withFallback}, making
+ * the description more useful.)
+ *
+ * @param originDescription
+ * description of the config
+ * @return an empty configuration
+ */
+ public static Config empty(String originDescription) {
+ return ConfigImpl.emptyConfig(originDescription);
+ }
+
+ /**
+ * Gets a Config containing the system properties from
+ * {@link java.lang.System#getProperties()}, parsed and converted as with
+ * {@link #parseProperties}.
+ *
+ * This method can return a global immutable singleton, so it's preferred
+ * over parsing system properties yourself.
+ *
+ * {@link #load} will include the system properties as overrides already, as
+ * will {@link #defaultReference} and {@link #defaultOverrides}.
+ *
+ *
+ * Because this returns a singleton, it will not notice changes to system
+ * properties made after the first time this method is called. Use
+ * {@link #invalidateCaches()} to force the singleton to reload if you
+ * modify system properties.
+ *
+ * @return system properties parsed into a Config
+ */
+ public static Config systemProperties() {
+ return ConfigImpl.systemPropertiesAsConfig();
+ }
+
+ /**
+ * Gets a Config containing the system's environment variables.
+ * This method can return a global immutable singleton.
+ *
+ *
+ * Environment variables are used as fallbacks when resolving substitutions
+ * whether or not this object is included in the config being resolved, so
+ * you probably don't need to use this method for most purposes. It can be a
+ * nicer API for accessing environment variables than raw
+ * {@link java.lang.System#getenv(String)} though, since you can use methods
+ * such as {@link Config#getInt}.
+ *
+ * @return system environment variables parsed into a Config
+ */
+ public static Config systemEnvironment() {
+ return ConfigImpl.envVariablesAsConfig();
+ }
+
+ /**
+ * Converts a Java {@link java.util.Properties} object to a
+ * {@link ConfigObject} using the rules documented in the HOCON
+ * spec. 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 have System.getProperties() as a
+ * ConfigObject, it's better to use the {@link #systemProperties()} method
+ * which returns a cached global singleton.
+ *
+ * @param properties
+ * a Java Properties object
+ * @param options
+ * the parse options
+ * @return the parsed configuration
+ */
+ public static Config parseProperties(Properties properties,
+ ConfigParseOptions options) {
+ return Parseable.newProperties(properties, options).parse().toConfig();
+ }
+
+ /**
+ * Like {@link #parseProperties(Properties, ConfigParseOptions)} but uses default
+ * parse options.
+ * @param properties
+ * a Java Properties object
+ * @return the parsed configuration
+ */
+ public static Config parseProperties(Properties properties) {
+ return parseProperties(properties, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses a Reader into a Config instance. Does not call
+ * {@link Config#resolve} or merge the parsed stream with any
+ * other configuration; this method parses a single stream and
+ * does nothing else. It does process "include" statements in
+ * the parsed stream, and may end up doing other IO due to those
+ * statements.
+ *
+ * @param reader
+ * the reader to parse
+ * @param options
+ * parse options to control how the reader is interpreted
+ * @return the parsed configuration
+ * @throws ConfigException on IO or parse errors
+ */
+ public static Config parseReader(Reader reader, ConfigParseOptions options) {
+ return Parseable.newReader(reader, options).parse().toConfig();
+ }
+
+ /**
+ * Parses a reader into a Config instance as with
+ * {@link #parseReader(Reader,ConfigParseOptions)} but always uses the
+ * default parse options.
+ *
+ * @param reader
+ * the reader to parse
+ * @return the parsed configuration
+ * @throws ConfigException on IO or parse errors
+ */
+ public static Config parseReader(Reader reader) {
+ return parseReader(reader, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses a URL into a Config instance. Does not call
+ * {@link Config#resolve} or merge the parsed stream with any
+ * other configuration; this method parses a single stream and
+ * does nothing else. It does process "include" statements in
+ * the parsed stream, and may end up doing other IO due to those
+ * statements.
+ *
+ * @param url
+ * the url to parse
+ * @param options
+ * parse options to control how the url is interpreted
+ * @return the parsed configuration
+ * @throws ConfigException on IO or parse errors
+ */
+ public static Config parseURL(URL url, ConfigParseOptions options) {
+ return Parseable.newURL(url, options).parse().toConfig();
+ }
+
+ /**
+ * Parses a url into a Config instance as with
+ * {@link #parseURL(URL,ConfigParseOptions)} but always uses the
+ * default parse options.
+ *
+ * @param url
+ * the url to parse
+ * @return the parsed configuration
+ * @throws ConfigException on IO or parse errors
+ */
+ public static Config parseURL(URL url) {
+ return parseURL(url, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses a file into a Config instance. Does not call
+ * {@link Config#resolve} or merge the file with any other
+ * configuration; this method parses a single file and does
+ * nothing else. It does process "include" statements in the
+ * parsed file, and may end up doing other IO due to those
+ * statements.
+ *
+ * @param file
+ * the file to parse
+ * @param options
+ * parse options to control how the file is interpreted
+ * @return the parsed configuration
+ * @throws ConfigException on IO or parse errors
+ */
+ public static Config parseFile(File file, ConfigParseOptions options) {
+ return Parseable.newFile(file, options).parse().toConfig();
+ }
+
+ /**
+ * Parses a file into a Config instance as with
+ * {@link #parseFile(File,ConfigParseOptions)} but always uses the
+ * default parse options.
+ *
+ * @param file
+ * the file to parse
+ * @return the parsed configuration
+ * @throws ConfigException on IO or parse errors
+ */
+ public static Config parseFile(File file) {
+ return parseFile(file, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses a file with a flexible extension. If the fileBasename
+ * already ends in a known extension, this method parses it according to
+ * that extension (the file's syntax must match its extension). If the
+ * fileBasename does not end in an extension, it parses files
+ * with all known extensions and merges whatever is found.
+ *
+ *
+ * In the current implementation, the extension ".conf" forces
+ * {@link ConfigSyntax#CONF}, ".json" forces {@link ConfigSyntax#JSON}, and
+ * ".properties" forces {@link ConfigSyntax#PROPERTIES}. When merging files,
+ * ".conf" falls back to ".json" falls back to ".properties".
+ *
+ *
+ * Future versions of the implementation may add additional syntaxes or
+ * additional extensions. However, the ordering (fallback priority) of the
+ * three current extensions will remain the same.
+ *
+ *
+ * If options forces a specific syntax, this method only parses
+ * files with an extension matching that syntax.
+ *
+ *
+ * If {@link ConfigParseOptions#getAllowMissing options.getAllowMissing()}
+ * is true, then no files have to exist; if false, then at least one file
+ * has to exist.
+ *
+ * @param fileBasename
+ * a filename with or without extension
+ * @param options
+ * parse options
+ * @return the parsed configuration
+ */
+ public static Config parseFileAnySyntax(File fileBasename,
+ ConfigParseOptions options) {
+ return ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig();
+ }
+
+ /**
+ * Like {@link #parseFileAnySyntax(File,ConfigParseOptions)} but always uses
+ * default parse options.
+ *
+ * @param fileBasename
+ * a filename with or without extension
+ * @return the parsed configuration
+ */
+ public static Config parseFileAnySyntax(File fileBasename) {
+ return parseFileAnySyntax(fileBasename, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses all resources on the classpath with the given name and merges them
+ * into a single Config.
+ *
+ *
+ * If the resource name does not begin with a "/", it will have the supplied
+ * class's package added to it, in the same way as
+ * {@link java.lang.Class#getResource}.
+ *
+ *
+ * Duplicate resources with the same name are merged such that ones returned
+ * earlier from {@link ClassLoader#getResources} fall back to (have higher
+ * priority than) the ones returned later. This implies that resources
+ * earlier in the classpath override those later in the classpath when they
+ * configure the same setting. However, in practice real applications may
+ * not be consistent about classpath ordering, so be careful. It may be best
+ * to avoid assuming too much.
+ *
+ * @param klass
+ * klass.getClassLoader() will be used to load
+ * resources, and non-absolute resource names will have this
+ * class's package added
+ * @param resource
+ * resource to look up, relative to klass's package
+ * or absolute starting with a "/"
+ * @param options
+ * parse options
+ * @return the parsed configuration
+ */
+ public static Config parseResources(Class> klass, String resource,
+ ConfigParseOptions options) {
+ return Parseable.newResources(klass, resource, options).parse()
+ .toConfig();
+ }
+
+ /**
+ * Like {@link #parseResources(Class,String,ConfigParseOptions)} but always uses
+ * default parse options.
+ *
+ * @param klass
+ * klass.getClassLoader() will be used to load
+ * resources, and non-absolute resource names will have this
+ * class's package added
+ * @param resource
+ * resource to look up, relative to klass's package
+ * or absolute starting with a "/"
+ * @return the parsed configuration
+ */
+ public static Config parseResources(Class> klass, String resource) {
+ return parseResources(klass, resource, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses classpath resources with a flexible extension. In general, this
+ * method has the same behavior as
+ * {@link #parseFileAnySyntax(File,ConfigParseOptions)} but for classpath
+ * resources instead, as in {@link #parseResources}.
+ *
+ *
+ * There is a thorny problem with this method, which is that
+ * {@link java.lang.ClassLoader#getResources} must be called separately for
+ * each possible extension. The implementation ends up with separate lists
+ * of resources called "basename.conf" and "basename.json" for example. As a
+ * result, the ideal ordering between two files with different extensions is
+ * unknown; there is no way to figure out how to merge the two lists in
+ * classpath order. To keep it simple, the lists are simply concatenated,
+ * with the same syntax priorities as
+ * {@link #parseFileAnySyntax(File,ConfigParseOptions) parseFileAnySyntax()}
+ * - all ".conf" resources are ahead of all ".json" resources which are
+ * ahead of all ".properties" resources.
+ *
+ * @param klass
+ * class which determines the ClassLoader and the
+ * package for relative resource names
+ * @param resourceBasename
+ * a resource name as in {@link java.lang.Class#getResource},
+ * with or without extension
+ * @param options
+ * parse options (class loader is ignored in favor of the one
+ * from klass)
+ * @return the parsed configuration
+ */
+ public static Config parseResourcesAnySyntax(Class> klass, String resourceBasename,
+ ConfigParseOptions options) {
+ return ConfigImpl.parseResourcesAnySyntax(klass, resourceBasename,
+ options).toConfig();
+ }
+
+ /**
+ * Like {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)}
+ * but always uses default parse options.
+ *
+ * @param klass
+ * klass.getClassLoader() will be used to load
+ * resources, and non-absolute resource names will have this
+ * class's package added
+ * @param resourceBasename
+ * a resource name as in {@link java.lang.Class#getResource},
+ * with or without extension
+ * @return the parsed configuration
+ */
+ public static Config parseResourcesAnySyntax(Class> klass, String resourceBasename) {
+ return parseResourcesAnySyntax(klass, resourceBasename, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses all resources on the classpath with the given name and merges them
+ * into a single Config.
+ *
+ *
+ * This works like {@link java.lang.ClassLoader#getResource}, not like
+ * {@link java.lang.Class#getResource}, so the name never begins with a
+ * slash.
+ *
+ *
+ * See {@link #parseResources(Class,String,ConfigParseOptions)} for full
+ * details.
+ *
+ * @param loader
+ * will be used to load resources by setting this loader on the
+ * provided options
+ * @param resource
+ * resource to look up
+ * @param options
+ * parse options (class loader is ignored)
+ * @return the parsed configuration
+ */
+ public static Config parseResources(ClassLoader loader, String resource,
+ ConfigParseOptions options) {
+ return parseResources(resource, options.setClassLoader(loader));
+ }
+
+ /**
+ * Like {@link #parseResources(ClassLoader,String,ConfigParseOptions)} but always uses
+ * default parse options.
+ *
+ * @param loader
+ * will be used to load resources
+ * @param resource
+ * resource to look up in the loader
+ * @return the parsed configuration
+ */
+ public static Config parseResources(ClassLoader loader, String resource) {
+ return parseResources(loader, resource, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses classpath resources with a flexible extension. In general, this
+ * method has the same behavior as
+ * {@link #parseFileAnySyntax(File,ConfigParseOptions)} but for classpath
+ * resources instead, as in
+ * {@link #parseResources(ClassLoader,String,ConfigParseOptions)}.
+ *
+ *
+ * {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)} differs
+ * in the syntax for the resource name, but otherwise see
+ * {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)} for
+ * some details and caveats on this method.
+ *
+ * @param loader
+ * class loader to look up resources in, will be set on options
+ * @param resourceBasename
+ * a resource name as in
+ * {@link java.lang.ClassLoader#getResource}, with or without
+ * extension
+ * @param options
+ * parse options (class loader ignored)
+ * @return the parsed configuration
+ */
+ public static Config parseResourcesAnySyntax(ClassLoader loader, String resourceBasename,
+ ConfigParseOptions options) {
+ return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options.setClassLoader(loader))
+ .toConfig();
+ }
+
+ /**
+ * Like {@link #parseResourcesAnySyntax(ClassLoader,String,ConfigParseOptions)} but always uses
+ * default parse options.
+ *
+ * @param loader
+ * will be used to load resources
+ * @param resourceBasename
+ * a resource name as in
+ * {@link java.lang.ClassLoader#getResource}, with or without
+ * extension
+ * @return the parsed configuration
+ */
+ public static Config parseResourcesAnySyntax(ClassLoader loader, String resourceBasename) {
+ return parseResourcesAnySyntax(loader, resourceBasename, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Like {@link #parseResources(ClassLoader,String,ConfigParseOptions)} but
+ * uses thread's current context class loader if none is set in the
+ * ConfigParseOptions.
+ * @param resource the resource name
+ * @param options parse options
+ * @return the parsed configuration
+ */
+ public static Config parseResources(String resource, ConfigParseOptions options) {
+ ConfigParseOptions withLoader = ensureClassLoader(options, "parseResources");
+ return Parseable.newResources(resource, withLoader).parse().toConfig();
+ }
+
+ /**
+ * Like {@link #parseResources(ClassLoader,String)} but uses thread's
+ * current context class loader.
+ * @param resource the resource name
+ * @return the parsed configuration
+ */
+ public static Config parseResources(String resource) {
+ return parseResources(resource, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Like
+ * {@link #parseResourcesAnySyntax(ClassLoader,String,ConfigParseOptions)}
+ * but uses thread's current context class loader.
+ * @param resourceBasename the resource basename (no file type suffix)
+ * @param options parse options
+ * @return the parsed configuration
+ */
+ public static Config parseResourcesAnySyntax(String resourceBasename, ConfigParseOptions options) {
+ return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options).toConfig();
+ }
+
+ /**
+ * Like {@link #parseResourcesAnySyntax(ClassLoader,String)} but uses
+ * thread's current context class loader.
+ * @param resourceBasename the resource basename (no file type suffix)
+ * @return the parsed configuration
+ */
+ public static Config parseResourcesAnySyntax(String resourceBasename) {
+ return parseResourcesAnySyntax(resourceBasename, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Parses a string (which should be valid HOCON or JSON by default, or
+ * the syntax specified in the options otherwise).
+ *
+ * @param s string to parse
+ * @param options parse options
+ * @return the parsed configuration
+ */
+ public static Config parseString(String s, ConfigParseOptions options) {
+ return Parseable.newString(s, options).parse().toConfig();
+ }
+
+ /**
+ * Parses a string (which should be valid HOCON or JSON).
+ *
+ * @param s string to parse
+ * @return the parsed configuration
+ */
+ public static Config parseString(String s) {
+ return parseString(s, ConfigParseOptions.defaults());
+ }
+
+ /**
+ * Creates a {@code Config} based on a {@link java.util.Map} from paths to
+ * plain Java values. Similar to
+ * {@link ConfigValueFactory#fromMap(Map,String)}, except the keys in the
+ * map are path expressions, rather than keys; and correspondingly it
+ * returns a {@code Config} instead of a {@code 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 map from paths to plain Java objects
+ * @param originDescription
+ * description of what this map represents, like a filename, or
+ * "default settings" (origin description is used in error
+ * messages)
+ * @return the map converted to a {@code Config}
+ */
+ public static Config parseMap(Map values,
+ String originDescription) {
+ return ConfigImpl.fromPathMap(values, originDescription).toConfig();
+ }
+
+ /**
+ * See the other overload of {@link #parseMap(Map, String)} for details,
+ * this one just uses a default origin description.
+ *
+ * @param values map from paths to plain Java values
+ * @return the map converted to a {@code Config}
+ */
+ public static Config parseMap(Map values) {
+ return parseMap(values, null);
+ }
+
+ private static ConfigLoadingStrategy getConfigLoadingStrategy() {
+ String className = System.getProperties().getProperty(STRATEGY_PROPERTY_NAME);
+
+ if (className != null) {
+ try {
+ return ConfigLoadingStrategy.class.cast(Class.forName(className).newInstance());
+ } catch (Throwable e) {
+ throw new ConfigException.BugOrBroken("Failed to load strategy: " + className, e);
+ }
+ } else {
+ return new DefaultConfigLoadingStrategy();
+ }
+ }
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncludeContext.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncludeContext.java
new file mode 100644
index 0000000..42a5b87
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncludeContext.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+
+/**
+ * Context provided to a {@link ConfigIncluder}; this interface is only useful
+ * inside a {@code ConfigIncluder} implementation, and is not intended for apps
+ * to implement.
+ *
+ *
+ * Do not implement this interface; it should only be implemented by
+ * the config library. Arbitrary implementations will not work because the
+ * library internals assume a specific concrete implementation. Also, this
+ * interface is likely to grow new methods over time, so third-party
+ * implementations will break.
+ */
+public interface ConfigIncludeContext {
+ /**
+ * Tries to find a name relative to whatever is doing the including, for
+ * example in the same directory as the file doing the including. Returns
+ * null if it can't meaningfully create a relative name. The returned
+ * parseable may not exist; this function is not required to do any IO, just
+ * compute what the name would be.
+ *
+ * The passed-in filename has to be a complete name (with extension), not
+ * just a basename. (Include statements in config files are allowed to give
+ * just a basename.)
+ *
+ * @param filename
+ * the name to make relative to the resource doing the including
+ * @return parseable item relative to the resource doing the including, or
+ * null
+ */
+ ConfigParseable relativeTo(String filename);
+
+ /**
+ * Parse options to use (if you use another method to get a
+ * {@link ConfigParseable} then use {@link ConfigParseable#options()}
+ * instead though).
+ *
+ * @return the parse options
+ */
+ ConfigParseOptions parseOptions();
+
+
+ /**
+ * Copy this {@link ConfigIncludeContext} giving it a new value for its parseOptions.
+ *
+ * @param options new parse options to use
+ *
+ * @return the updated copy of this context
+ */
+ ConfigIncludeContext setParseOptions(ConfigParseOptions options);
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluder.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluder.java
new file mode 100644
index 0000000..21db0dc
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluder.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+/**
+ * Implement this interface and provide an instance to
+ * {@link ConfigParseOptions#setIncluder ConfigParseOptions.setIncluder()} to
+ * customize handling of {@code include} statements in config files. You may
+ * also want to implement {@link ConfigIncluderClasspath},
+ * {@link ConfigIncluderFile}, and {@link ConfigIncluderURL}, or not.
+ */
+public interface ConfigIncluder {
+ /**
+ * Returns a new includer that falls back to the given includer. This is how
+ * you can obtain the default includer; it will be provided as a fallback.
+ * It's up to your includer to chain to it if you want to. You might want to
+ * merge any files found by the fallback includer with any objects you load
+ * yourself.
+ *
+ * It's important to handle the case where you already have the fallback
+ * with a "return this", i.e. this method should not create a new object if
+ * the fallback is the same one you already have. The same fallback may be
+ * added repeatedly.
+ *
+ * @param fallback the previous includer for chaining
+ * @return a new includer
+ */
+ ConfigIncluder withFallback(ConfigIncluder fallback);
+
+ /**
+ * Parses another item to be included. The returned object typically would
+ * not have substitutions resolved. You can throw a ConfigException here to
+ * abort parsing, or return an empty object, but may not return null.
+ *
+ * This method is used for a "heuristic" include statement that does not
+ * specify file, URL, or classpath resource. If the include statement does
+ * specify, then the same class implementing {@link ConfigIncluder} must
+ * also implement {@link ConfigIncluderClasspath},
+ * {@link ConfigIncluderFile}, or {@link ConfigIncluderURL} as needed, or a
+ * default includer will be used.
+ *
+ * @param context
+ * some info about the include context
+ * @param what
+ * the include statement's argument
+ * @return a non-null ConfigObject
+ */
+ ConfigObject include(ConfigIncludeContext context, String what);
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderClasspath.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderClasspath.java
new file mode 100644
index 0000000..0903073
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderClasspath.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+/**
+ * Implement this in addition to {@link ConfigIncluder} if you want to
+ * support inclusion of files with the {@code include classpath("resource")}
+ * syntax. If you do not implement this but do implement {@link ConfigIncluder},
+ * attempts to load classpath resources will use the default includer.
+ */
+public interface ConfigIncluderClasspath {
+ /**
+ * Parses another item to be included. The returned object typically would
+ * not have substitutions resolved. You can throw a ConfigException here to
+ * abort parsing, or return an empty object, but may not return null.
+ *
+ * @param context
+ * some info about the include context
+ * @param what
+ * the include statement's argument
+ * @return a non-null ConfigObject
+ */
+ ConfigObject includeResources(ConfigIncludeContext context, String what);
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderFile.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderFile.java
new file mode 100644
index 0000000..57f85f6
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderFile.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+import java.io.File;
+
+/**
+ * Implement this in addition to {@link ConfigIncluder} if you want to
+ * support inclusion of files with the {@code include file("filename")} syntax.
+ * If you do not implement this but do implement {@link ConfigIncluder},
+ * attempts to load files will use the default includer.
+ */
+public interface ConfigIncluderFile {
+ /**
+ * Parses another item to be included. The returned object typically would
+ * not have substitutions resolved. You can throw a ConfigException here to
+ * abort parsing, or return an empty object, but may not return null.
+ *
+ * @param context
+ * some info about the include context
+ * @param what
+ * the include statement's argument
+ * @return a non-null ConfigObject
+ */
+ ConfigObject includeFile(ConfigIncludeContext context, File what);
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderURL.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderURL.java
new file mode 100644
index 0000000..c5c40e4
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigIncluderURL.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+import java.net.URL;
+
+/**
+ * Implement this in addition to {@link ConfigIncluder} if you want to
+ * support inclusion of files with the {@code include url("http://example.com")}
+ * syntax. If you do not implement this but do implement {@link ConfigIncluder},
+ * attempts to load URLs will use the default includer.
+ */
+public interface ConfigIncluderURL {
+ /**
+ * Parses another item to be included. The returned object typically would
+ * not have substitutions resolved. You can throw a ConfigException here to
+ * abort parsing, or return an empty object, but may not return null.
+ *
+ * @param context
+ * some info about the include context
+ * @param what
+ * the include statement's argument
+ * @return a non-null ConfigObject
+ */
+ ConfigObject includeURL(ConfigIncludeContext context, URL what);
+}
diff --git a/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigList.java b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigList.java
new file mode 100644
index 0000000..dace555
--- /dev/null
+++ b/src/main/java/com/drtshock/playervaults/lib/com/typesafe/config/ConfigList.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2011-2012 Typesafe Inc.
+ */
+package com.drtshock.playervaults.lib.com.typesafe.config;
+
+import java.util.List;
+
+/**
+ * Subtype of {@link ConfigValue} representing a list value, as in JSON's
+ * {@code [1,2,3]} syntax.
+ *
+ *
+ * {@code ConfigList} implements {@code java.util.List} so you can
+ * use it like a regular Java list. Or call {@link #unwrapped()} to unwrap the
+ * list elements into plain Java values.
+ *
+ *
+ * Like all {@link ConfigValue} subtypes, {@code ConfigList} is immutable. This
+ * makes it threadsafe and you never have to create "defensive copies." The
+ * mutator methods from {@link java.util.List} all throw
+ * {@link java.lang.UnsupportedOperationException}.
+ *
+ *
+ * The {@link ConfigValue#valueType} method on a list returns
+ * {@link ConfigValueType#LIST}.
+ *
+ *
+ * Do not implement {@code ConfigList}; it should only be implemented
+ * by the config library. Arbitrary implementations will not work because the
+ * library internals assume a specific concrete implementation. Also, this
+ * interface is likely to grow new methods over time, so third-party
+ * implementations will break.
+ *
+ */
+public interface ConfigList extends List, ConfigValue {
+
+ /**
+ * Recursively unwraps the list, returning a list of plain Java values such
+ * as Integer or String or whatever is in the list.
+ *
+ * @return a {@link java.util.List} containing plain Java objects
+ */
+ @Override
+ List