mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-18 03:36:47 -04:00
updated typesafe config
This commit is contained in:
parent
47d9b91201
commit
3567b28e47
@ -196,8 +196,6 @@ object Settings {
|
|||||||
val renderSettings = ConfigRenderOptions.defaults.setJson(false).setOriginComments(false)
|
val renderSettings = ConfigRenderOptions.defaults.setJson(false).setOriginComments(false)
|
||||||
val out = new PrintWriter(file)
|
val out = new PrintWriter(file)
|
||||||
out.write(config.root.render(renderSettings).lines.
|
out.write(config.root.render(renderSettings).lines.
|
||||||
// Strip extra spaces in front.
|
|
||||||
map(_.stripPrefix(" ")).
|
|
||||||
// Indent two spaces instead of four.
|
// Indent two spaces instead of four.
|
||||||
map(line => """^(\s*)""".r.replaceAllIn(line, m => m.group(1).replace(" ", " "))).
|
map(line => """^(\s*)""".r.replaceAllIn(line, m => m.group(1).replace(" ", " "))).
|
||||||
// Finalize the string.
|
// Finalize the string.
|
||||||
|
@ -9,46 +9,76 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An immutable map from config paths to config values.
|
* An immutable map from config paths to config values. Paths are dot-separated
|
||||||
*
|
* expressions such as <code>foo.bar.baz</code>. Values are as in JSON
|
||||||
|
* (booleans, strings, numbers, lists, or objects), represented by
|
||||||
|
* {@link ConfigValue} instances. Values accessed through the
|
||||||
|
* <code>Config</code> interface are never null.
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Contrast with {@link ConfigObject} which is a map from config <em>keys</em>,
|
* {@code Config} is an immutable object and thus safe to use from multiple
|
||||||
* rather than paths, to config values. A {@code Config} contains a tree of
|
* threads. There's never a need for "defensive copies."
|
||||||
* {@code ConfigObject}, and {@link Config#root()} returns the tree's root
|
*
|
||||||
* object.
|
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Throughout the API, there is a distinction between "keys" and "paths". A key
|
* Fundamental operations on a {@code Config} include getting configuration
|
||||||
* is a key in a JSON object; it's just a string that's the key in a map. A
|
* values, <em>resolving</em> substitutions with {@link Config#resolve()}, and
|
||||||
* "path" is a parseable expression with a syntax and it refers to a series of
|
* merging configs using {@link Config#withFallback(ConfigMergeable)}.
|
||||||
* keys. Path expressions are described in the <a
|
*
|
||||||
|
* <p>
|
||||||
|
* All operations return a new immutable {@code Config} rather than modifying
|
||||||
|
* the original instance.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Examples</strong>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* You can find an example app and library <a
|
||||||
|
* href="https://github.com/typesafehub/config/tree/master/examples">on
|
||||||
|
* GitHub</a>. Also be sure to read the <a
|
||||||
|
* href="package-summary.html#package_description">package overview</a> which
|
||||||
|
* describes the big picture as shown in those examples.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Paths, keys, and Config vs. ConfigObject</strong>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <code>Config</code> is a view onto a tree of {@link ConfigObject}; the
|
||||||
|
* corresponding object tree can be found through {@link Config#root()}.
|
||||||
|
* <code>ConfigObject</code> is a map from config <em>keys</em>, rather than
|
||||||
|
* paths, to config values. Think of <code>ConfigObject</code> as a JSON object
|
||||||
|
* and <code>Config</code> as a configuration API.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 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 <a
|
||||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec for
|
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec for
|
||||||
* Human-Optimized Config Object Notation</a>. In brief, a path is
|
* Human-Optimized Config Object Notation</a>. In brief, a path is
|
||||||
* period-separated so "a.b.c" looks for key c in object b in object a in the
|
* 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
|
* root object. Sometimes double quotes are needed around special characters in
|
||||||
* path expressions.
|
* path expressions.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The API for a {@code Config} is in terms of path expressions, while the API
|
* 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}
|
* for a {@code ConfigObject} is in terms of keys. Conceptually, {@code Config}
|
||||||
* is a one-level map from <em>paths</em> to values, while a
|
* is a one-level map from <em>paths</em> to values, while a
|
||||||
* {@code ConfigObject} is a tree of nested maps from <em>keys</em> to values.
|
* {@code ConfigObject} is a tree of nested maps from <em>keys</em> to values.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
||||||
* between path expressions and individual path elements (keys).
|
* between path expressions and individual path elements (keys).
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Another difference between {@code Config} and {@code ConfigObject} is that
|
* Another difference between {@code Config} and {@code ConfigObject} is that
|
||||||
* conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType()
|
* conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType()
|
||||||
* valueType()} of {@link ConfigValueType#NULL NULL} exist in a
|
* valueType()} of {@link ConfigValueType#NULL NULL} exist in a
|
||||||
* {@code ConfigObject}, while a {@code Config} treats null values as if they
|
* {@code ConfigObject}, while a {@code Config} treats null values as if they
|
||||||
* were missing.
|
* were missing.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* {@code Config} is an immutable object and thus safe to use from multiple
|
* <strong>Getting configuration values</strong>
|
||||||
* threads. There's never a need for "defensive copies."
|
*
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The "getters" on a {@code Config} all work in the same way. They never return
|
* The "getters" on a {@code Config} all work in the same way. They never return
|
||||||
* null, nor do they return a {@code ConfigValue} with
|
* null, nor do they return a {@code ConfigValue} with
|
||||||
@ -59,25 +89,71 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* thrown. {@link ConfigException.WrongType} will be thrown anytime you ask for
|
* thrown. {@link ConfigException.WrongType} will be thrown anytime you ask for
|
||||||
* a type and the value has an incompatible type. Reasonable type conversions
|
* a type and the value has an incompatible type. Reasonable type conversions
|
||||||
* are performed for you though.
|
* are performed for you though.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Iteration</strong>
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If you want to iterate over the contents of a {@code Config}, you can get its
|
* 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} with {@link #root()}, and then iterate over the
|
||||||
* {@code ConfigObject} (which implements <code>java.util.Map</code>). Or, you
|
* {@code ConfigObject} (which implements <code>java.util.Map</code>). Or, you
|
||||||
* can use {@link #entrySet()} which recurses the object tree for you and builds
|
* can use {@link #entrySet()} which recurses the object tree for you and builds
|
||||||
* up a <code>Set</code> of all path-value pairs where the value is not null.
|
* up a <code>Set</code> of all path-value pairs where the value is not null.
|
||||||
*
|
*
|
||||||
* <p>Before using a {@code Config} it's necessary to call {@link Config#resolve()}
|
* <p>
|
||||||
* to handle substitutions (though {@link ConfigFactory#load()} and similar methods
|
* <strong>Resolving substitutions</strong>
|
||||||
* will do the resolve for you already).
|
*
|
||||||
*
|
* <p>
|
||||||
* <p> You can find an example app and library <a
|
* <em>Substitutions</em> are the <code>${foo.bar}</code> syntax in config
|
||||||
* href="https://github.com/typesafehub/config/tree/master/examples">on
|
* files, described in the <a href=
|
||||||
* GitHub</a>. Also be sure to read the <a
|
* "https://github.com/typesafehub/config/blob/master/HOCON.md#substitutions"
|
||||||
* href="package-summary.html#package_description">package
|
* >specification</a>. Resolving substitutions replaces these references with real
|
||||||
* overview</a> which describes the big picture as shown in those
|
* values.
|
||||||
* examples.
|
*
|
||||||
*
|
* <p>
|
||||||
|
* 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).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Merging</strong>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The full <code>Config</code> 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 <code>application.conf</code> over the top of
|
||||||
|
* <code>reference.conf</code>, using <code>withFallback</code>. You can add in
|
||||||
|
* additional sources of configuration in the same way (usually, custom layers
|
||||||
|
* should go either just above or just below <code>application.conf</code>,
|
||||||
|
* keeping <code>reference.conf</code> at the bottom and system properties at
|
||||||
|
* the top).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Serialization</strong>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Convert a <code>Config</code> to a JSON or HOCON string by calling
|
||||||
|
* {@link ConfigObject#render()} on the root object,
|
||||||
|
* <code>myConfig.root().render()</code>. 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 <code>Config</code> 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.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* As an alternative to {@link ConfigObject#render()}, the
|
||||||
|
* <code>toString()</code> method produces a debug-output-oriented
|
||||||
|
* representation (which is not valid JSON).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Java serialization is supported as well for <code>Config</code> and all
|
||||||
|
* subtypes of <code>ConfigValue</code>.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>This is an interface but don't implement it yourself</strong>
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* <em>Do not implement {@code Config}</em>; it should only be implemented by
|
* <em>Do not implement {@code Config}</em>; it should only be implemented by
|
||||||
* the config library. Arbitrary implementations will not work because the
|
* the config library. Arbitrary implementations will not work because the
|
||||||
@ -114,19 +190,20 @@ public interface Config extends ConfigMergeable {
|
|||||||
* <code>Config</code> as the root object, that is, a substitution
|
* <code>Config</code> as the root object, that is, a substitution
|
||||||
* <code>${foo.bar}</code> will be replaced with the result of
|
* <code>${foo.bar}</code> will be replaced with the result of
|
||||||
* <code>getValue("foo.bar")</code>.
|
* <code>getValue("foo.bar")</code>.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This method uses {@link ConfigResolveOptions#defaults()}, there is
|
* This method uses {@link ConfigResolveOptions#defaults()}, there is
|
||||||
* another variant {@link Config#resolve(ConfigResolveOptions)} which lets
|
* another variant {@link Config#resolve(ConfigResolveOptions)} which lets
|
||||||
* you specify non-default options.
|
* you specify non-default options.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* A given {@link Config} must be resolved before using it to retrieve
|
* A given {@link Config} must be resolved before using it to retrieve
|
||||||
* config values, but ideally should be resolved one time for your entire
|
* config values, but ideally should be resolved one time for your entire
|
||||||
* stack of fallbacks (see {@link Config#withFallback}). Otherwise, some
|
* stack of fallbacks (see {@link Config#withFallback}). Otherwise, some
|
||||||
* substitutions that could have resolved with all fallbacks available may
|
* substitutions that could have resolved with all fallbacks available may
|
||||||
* not resolve, which will be a user-visible oddity.
|
* not resolve, which will be potentially confusing for your application's
|
||||||
*
|
* users.
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* <code>resolve()</code> should be invoked on root config objects, rather
|
* <code>resolve()</code> should be invoked on root config objects, rather
|
||||||
* than on a subtree (a subtree is the result of something like
|
* than on a subtree (a subtree is the result of something like
|
||||||
@ -136,24 +213,24 @@ public interface Config extends ConfigMergeable {
|
|||||||
* from the root. For example, if you did
|
* from the root. For example, if you did
|
||||||
* <code>config.getConfig("foo").resolve()</code> on the below config file,
|
* <code>config.getConfig("foo").resolve()</code> on the below config file,
|
||||||
* it would not work:
|
* it would not work:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* common-value = 10
|
* common-value = 10
|
||||||
* foo {
|
* foo {
|
||||||
* whatever = ${common-value}
|
* whatever = ${common-value}
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Many methods on {@link ConfigFactory} such as {@link
|
* Many methods on {@link ConfigFactory} such as
|
||||||
* ConfigFactory#load()} automatically resolve the loaded
|
* {@link ConfigFactory#load()} automatically resolve the loaded
|
||||||
* <code>Config</code> on the loaded stack of config files.
|
* <code>Config</code> on the loaded stack of config files.
|
||||||
*
|
*
|
||||||
* <p> Resolving an already-resolved config is a harmless
|
* <p>
|
||||||
* no-op, but again, it is best to resolve an entire stack of
|
* Resolving an already-resolved config is a harmless no-op, but again, it
|
||||||
* fallbacks (such as all your config files combined) rather
|
* is best to resolve an entire stack of fallbacks (such as all your config
|
||||||
* than resolving each one individually.
|
* files combined) rather than resolving each one individually.
|
||||||
*
|
*
|
||||||
* @return an immutable object with substitutions resolved
|
* @return an immutable object with substitutions resolved
|
||||||
* @throws ConfigException.UnresolvedSubstitution
|
* @throws ConfigException.UnresolvedSubstitution
|
||||||
* if any substitutions refer to nonexistent paths
|
* if any substitutions refer to nonexistent paths
|
||||||
@ -168,10 +245,67 @@ public interface Config extends ConfigMergeable {
|
|||||||
*
|
*
|
||||||
* @param options
|
* @param options
|
||||||
* resolve options
|
* resolve options
|
||||||
* @return the resolved <code>Config</code>
|
* @return the resolved <code>Config</code> (may be only partially resolved if options are set to allow unresolved)
|
||||||
*/
|
*/
|
||||||
Config resolve(ConfigResolveOptions options);
|
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
|
||||||
|
* <code>allowUnresolved</code> 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.
|
||||||
|
* <p>
|
||||||
|
* 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 <code>Config</code> (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
|
* 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
|
* if it is invalid. The purpose of this method is to "fail early" with a
|
||||||
@ -484,7 +618,7 @@ public interface Config extends ConfigMergeable {
|
|||||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
|
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
|
||||||
* spec</a>.
|
* spec</a>.
|
||||||
*
|
*
|
||||||
* @since 1.1
|
* @since 1.2.0
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* path expression
|
* path expression
|
||||||
@ -498,7 +632,7 @@ public interface Config extends ConfigMergeable {
|
|||||||
* @throws ConfigException.BadValue
|
* @throws ConfigException.BadValue
|
||||||
* if value cannot be parsed as a number of the given TimeUnit
|
* if value cannot be parsed as a number of the given TimeUnit
|
||||||
*/
|
*/
|
||||||
Long getDuration(String path, TimeUnit unit);
|
long getDuration(String path, TimeUnit unit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list value (with any element type) as a {@link ConfigList}, which
|
* Gets a list value (with any element type) as a {@link ConfigList}, which
|
||||||
@ -548,8 +682,8 @@ public interface Config extends ConfigMergeable {
|
|||||||
/**
|
/**
|
||||||
* Gets a list, converting each value in the list to a duration, using the
|
* Gets a list, converting each value in the list to a duration, using the
|
||||||
* same rules as {@link #getDuration(String, TimeUnit)}.
|
* same rules as {@link #getDuration(String, TimeUnit)}.
|
||||||
*
|
*
|
||||||
* @since 1.1
|
* @since 1.2.0
|
||||||
* @param path
|
* @param path
|
||||||
* a path expression
|
* a path expression
|
||||||
* @param unit
|
* @param unit
|
||||||
|
@ -219,21 +219,22 @@ public final class ConfigFactory {
|
|||||||
+ "', config.url='" + url + "', config.resource='" + resource
|
+ "', config.url='" + url + "', config.resource='" + resource
|
||||||
+ "'; don't know which one to use!");
|
+ "'; don't know which one to use!");
|
||||||
} else {
|
} else {
|
||||||
|
// the override file/url/resource MUST be present or it's an error
|
||||||
|
ConfigParseOptions overrideOptions = parseOptions.setAllowMissing(false);
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
if (resource.startsWith("/"))
|
if (resource.startsWith("/"))
|
||||||
resource = resource.substring(1);
|
resource = resource.substring(1);
|
||||||
// this deliberately does not parseResourcesAnySyntax; if
|
// this deliberately does not parseResourcesAnySyntax; if
|
||||||
// people want that they can use an include statement.
|
// people want that they can use an include statement.
|
||||||
return load(loader, parseResources(loader, resource, parseOptions), resolveOptions);
|
Config parsedResources = parseResources(loader, resource, overrideOptions);
|
||||||
|
return load(loader, parsedResources, resolveOptions);
|
||||||
} else if (file != null) {
|
} else if (file != null) {
|
||||||
return load(
|
Config parsedFile = parseFile(new File(file), overrideOptions);
|
||||||
loader,
|
return load(loader, parsedFile, resolveOptions);
|
||||||
parseFile(new File(file), parseOptions), resolveOptions);
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return load(
|
Config parsedURL = parseURL(new URL(url), overrideOptions);
|
||||||
loader,
|
return load(loader, parsedURL, resolveOptions);
|
||||||
parseURL(new URL(url), parseOptions), resolveOptions);
|
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
throw new ConfigException.Generic("Bad URL in config.url system property: '"
|
throw new ConfigException.Generic("Bad URL in config.url system property: '"
|
||||||
+ url + "': " + e.getMessage(), e);
|
+ url + "': " + e.getMessage(), e);
|
||||||
|
@ -19,43 +19,52 @@ package com.typesafe.config;
|
|||||||
public interface ConfigMergeable {
|
public interface ConfigMergeable {
|
||||||
/**
|
/**
|
||||||
* Returns a new value computed by merging this value with another, with
|
* Returns a new value computed by merging this value with another, with
|
||||||
* keys in this value "winning" over the other one. Only
|
* keys in this value "winning" over the other one.
|
||||||
* {@link ConfigObject} and {@link Config} instances do anything in this
|
|
||||||
* method (they need to merge the fallback keys into themselves). All other
|
|
||||||
* values just return the original value, since they automatically override
|
|
||||||
* any fallback.
|
|
||||||
*
|
*
|
||||||
* <p> The semantics of merging are described in the <a
|
* <p>
|
||||||
|
* This associative operation may be used to combine configurations from
|
||||||
|
* multiple sources (such as multiple configuration files).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The semantics of merging are described in the <a
|
||||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec
|
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec
|
||||||
* for HOCON</a>. Merging typically occurs when either the
|
* for HOCON</a>. Merging typically occurs when either the same object is
|
||||||
* same object is created twice in the same file, or two
|
* created twice in the same file, or two config files are both loaded. For
|
||||||
* config files are both loaded. For example:
|
* example:
|
||||||
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* foo = { a: 42 }
|
* foo = { a: 42 }
|
||||||
* foo = { b: 43 }
|
* foo = { b: 43 }
|
||||||
* </pre>
|
* </pre>
|
||||||
|
*
|
||||||
* Here, the two objects are merged as if you had written:
|
* Here, the two objects are merged as if you had written:
|
||||||
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* foo = { a: 42, b: 43 }
|
* foo = { a: 42, b: 43 }
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Note that objects do not merge "across" non-objects; if you write
|
* Only {@link ConfigObject} and {@link Config} instances do anything in
|
||||||
|
* this method (they need to merge the fallback keys into themselves). All
|
||||||
|
* other values just return the original value, since they automatically
|
||||||
|
* override any fallback. This means that objects do not merge "across"
|
||||||
|
* non-objects; if you write
|
||||||
* <code>object.withFallback(nonObject).withFallback(otherObject)</code>,
|
* <code>object.withFallback(nonObject).withFallback(otherObject)</code>,
|
||||||
* then <code>otherObject</code> will simply be ignored. This is an
|
* then <code>otherObject</code> will simply be ignored. This is an
|
||||||
* intentional part of how merging works, because non-objects such as
|
* intentional part of how merging works, because non-objects such as
|
||||||
* strings and integers replace (rather than merging with) any
|
* strings and integers replace (rather than merging with) any prior value:
|
||||||
* prior value:
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* foo = { a: 42 }
|
* foo = { a: 42 }
|
||||||
* foo = 10
|
* foo = 10
|
||||||
* </pre>
|
* </pre>
|
||||||
|
*
|
||||||
* Here, the number 10 "wins" and the value of <code>foo</code> would be
|
* Here, the number 10 "wins" and the value of <code>foo</code> would be
|
||||||
* simply 10. Again, for details see the spec.
|
* simply 10. Again, for details see the spec.
|
||||||
*
|
*
|
||||||
* @param other
|
* @param other
|
||||||
* an object whose keys should be used if the keys are not
|
* an object whose keys should be used as fallbacks, if the keys
|
||||||
* present in this one
|
* are not present in this one
|
||||||
* @return a new object (or the original one, if the fallback doesn't get
|
* @return a new object (or the original one, if the fallback doesn't get
|
||||||
* used)
|
* used)
|
||||||
*/
|
*/
|
||||||
|
@ -6,53 +6,58 @@ package com.typesafe.config;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtype of {@link ConfigValue} representing an object (dictionary, map)
|
* Subtype of {@link ConfigValue} representing an object (AKA dictionary or map)
|
||||||
* value, as in JSON's <code>{ "a" : 42 }</code> syntax.
|
* value, as in JSON's curly brace <code>{ "a" : 42 }</code> syntax.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* An object may also be viewed as a {@link Config} by calling
|
||||||
|
* {@link ConfigObject#toConfig()}.
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* {@code ConfigObject} implements {@code java.util.Map<String, ConfigValue>} so
|
* {@code ConfigObject} implements {@code java.util.Map<String, ConfigValue>} so
|
||||||
* you can use it like a regular Java map. Or call {@link #unwrapped()} to
|
* you can use it like a regular Java map. Or call {@link #unwrapped()} to
|
||||||
* unwrap the map to a map with plain Java values rather than
|
* unwrap the map to a map with plain Java values rather than
|
||||||
* {@code ConfigValue}.
|
* {@code ConfigValue}.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Like all {@link ConfigValue} subtypes, {@code ConfigObject} is immutable.
|
* Like all {@link ConfigValue} subtypes, {@code ConfigObject} is immutable.
|
||||||
* This makes it threadsafe and you never have to create "defensive copies." The
|
* This makes it threadsafe and you never have to create "defensive copies." The
|
||||||
* mutator methods from {@link java.util.Map} all throw
|
* mutator methods from {@link java.util.Map} all throw
|
||||||
* {@link java.lang.UnsupportedOperationException}.
|
* {@link java.lang.UnsupportedOperationException}.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The {@link ConfigValue#valueType} method on an object returns
|
* The {@link ConfigValue#valueType} method on an object returns
|
||||||
* {@link ConfigValueType#OBJECT}.
|
* {@link ConfigValueType#OBJECT}.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* In most cases you want to use the {@link Config} interface rather than this
|
* In most cases you want to use the {@link Config} interface rather than this
|
||||||
* one. Call {@link #toConfig()} to convert a {@code ConfigObject} to a
|
* one. Call {@link #toConfig()} to convert a {@code ConfigObject} to a
|
||||||
* {@code Config}.
|
* {@code Config}.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The API for a {@code ConfigObject} is in terms of keys, while the API for a
|
* The API for a {@code ConfigObject} is in terms of keys, while the API for a
|
||||||
* {@link Config} is in terms of path expressions. Conceptually,
|
* {@link Config} is in terms of path expressions. Conceptually,
|
||||||
* {@code ConfigObject} is a tree of maps from keys to values, while a
|
* {@code ConfigObject} is a tree of maps from keys to values, while a
|
||||||
* {@code Config} is a one-level map from paths to values.
|
* {@code Config} is a one-level map from paths to values.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
||||||
* between path expressions and individual path elements (keys).
|
* between path expressions and individual path elements (keys).
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* A {@code ConfigObject} may contain null values, which will have
|
* A {@code ConfigObject} may contain null values, which will have
|
||||||
* {@link ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If
|
* {@link ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If
|
||||||
* {@code get()} returns Java's null then the key was not present in the parsed
|
* {@link ConfigObject#get(Object)} returns Java's null then the key was not
|
||||||
* file (or wherever this value tree came from). If {@code get()} returns a
|
* present in the parsed file (or wherever this value tree came from). If
|
||||||
* {@link ConfigValue} with type {@code ConfigValueType#NULL} then the key was
|
* {@code get("key")} returns a {@link ConfigValue} with type
|
||||||
* set to null explicitly in the config file.
|
* {@code ConfigValueType#NULL} then the key was set to null explicitly in the
|
||||||
*
|
* config file.
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* <em>Do not implement {@code ConfigObject}</em>; it should only be implemented
|
* <em>Do not implement interface {@code ConfigObject}</em>; it should only be
|
||||||
* by the config library. Arbitrary implementations will not work because the
|
* implemented by the config library. Arbitrary implementations will not work
|
||||||
* library internals assume a specific concrete implementation. Also, this
|
* because the library internals assume a specific concrete implementation.
|
||||||
* interface is likely to grow new methods over time, so third-party
|
* Also, this interface is likely to grow new methods over time, so third-party
|
||||||
* implementations will break.
|
* implementations will break.
|
||||||
*/
|
*/
|
||||||
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||||
@ -82,11 +87,11 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
|||||||
* Gets a {@link ConfigValue} at the given key, or returns null if there is
|
* Gets a {@link ConfigValue} at the given key, or returns null if there is
|
||||||
* no value. The returned {@link ConfigValue} may have
|
* no value. The returned {@link ConfigValue} may have
|
||||||
* {@link ConfigValueType#NULL} or any other type, and the passed-in key
|
* {@link ConfigValueType#NULL} or any other type, and the passed-in key
|
||||||
* must be a key in this object, rather than a path expression.
|
* must be a key in this object (rather than a path expression).
|
||||||
*
|
*
|
||||||
* @param key
|
* @param key
|
||||||
* key to look up
|
* key to look up
|
||||||
*
|
*
|
||||||
* @return the value at the key or null if none
|
* @return the value at the key or null if none
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -115,7 +120,7 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
|||||||
* Returns a {@code ConfigObject} based on this one, but with the given key
|
* Returns a {@code ConfigObject} based on this one, but with the given key
|
||||||
* set to the given value. Does not modify this instance (since it's
|
* set to the given value. Does not modify this instance (since it's
|
||||||
* immutable). If the key already has a value, that value is replaced. To
|
* immutable). If the key already has a value, that value is replaced. To
|
||||||
* remove a value, use withoutKey().
|
* remove a value, use {@link ConfigObject#withoutKey(String)}.
|
||||||
*
|
*
|
||||||
* @param key
|
* @param key
|
||||||
* key to add
|
* key to add
|
||||||
|
@ -9,6 +9,9 @@ package com.typesafe.config;
|
|||||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
|
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
|
||||||
* spec.
|
* spec.
|
||||||
* <p>
|
* <p>
|
||||||
|
* Typically this class would be used with the method
|
||||||
|
* {@link Config#resolve(ConfigResolveOptions)}.
|
||||||
|
* <p>
|
||||||
* This object is immutable, so the "setters" return a new object.
|
* This object is immutable, so the "setters" return a new object.
|
||||||
* <p>
|
* <p>
|
||||||
* Here is an example of creating a custom {@code ConfigResolveOptions}:
|
* Here is an example of creating a custom {@code ConfigResolveOptions}:
|
||||||
@ -25,18 +28,21 @@ package com.typesafe.config;
|
|||||||
*/
|
*/
|
||||||
public final class ConfigResolveOptions {
|
public final class ConfigResolveOptions {
|
||||||
private final boolean useSystemEnvironment;
|
private final boolean useSystemEnvironment;
|
||||||
|
private final boolean allowUnresolved;
|
||||||
|
|
||||||
private ConfigResolveOptions(boolean useSystemEnvironment) {
|
private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolved) {
|
||||||
this.useSystemEnvironment = useSystemEnvironment;
|
this.useSystemEnvironment = useSystemEnvironment;
|
||||||
|
this.allowUnresolved = allowUnresolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default resolve options.
|
* Returns the default resolve options. By default the system environment
|
||||||
*
|
* will be used and unresolved substitutions are not allowed.
|
||||||
|
*
|
||||||
* @return the default resolve options
|
* @return the default resolve options
|
||||||
*/
|
*/
|
||||||
public static ConfigResolveOptions defaults() {
|
public static ConfigResolveOptions defaults() {
|
||||||
return new ConfigResolveOptions(true);
|
return new ConfigResolveOptions(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,9 +63,8 @@ public final class ConfigResolveOptions {
|
|||||||
* variables.
|
* variables.
|
||||||
* @return options with requested setting for use of environment variables
|
* @return options with requested setting for use of environment variables
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("static-method")
|
|
||||||
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
|
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
|
||||||
return new ConfigResolveOptions(value);
|
return new ConfigResolveOptions(value, allowUnresolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,4 +77,31 @@ public final class ConfigResolveOptions {
|
|||||||
public boolean getUseSystemEnvironment() {
|
public boolean getUseSystemEnvironment() {
|
||||||
return useSystemEnvironment;
|
return useSystemEnvironment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with "allow unresolved" set to the given value. By
|
||||||
|
* default, unresolved substitutions are an error. If unresolved
|
||||||
|
* substitutions are allowed, then a future attempt to use the unresolved
|
||||||
|
* value may fail, but {@link Config#resolve(ConfigResolveOptions)} itself
|
||||||
|
* will now throw.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to silently ignore unresolved substitutions.
|
||||||
|
* @return options with requested setting for whether to allow substitutions
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public ConfigResolveOptions setAllowUnresolved(boolean value) {
|
||||||
|
return new ConfigResolveOptions(useSystemEnvironment, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options allow unresolved substitutions. This method
|
||||||
|
* is mostly used by the config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return true if unresolved substitutions are allowed
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public boolean getAllowUnresolved() {
|
||||||
|
return allowUnresolved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,29 +4,33 @@
|
|||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The syntax of a character stream, <a href="http://json.org">JSON</a>, <a
|
* The syntax of a character stream (<a href="http://json.org">JSON</a>, <a
|
||||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
|
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
|
||||||
* aka ".conf", or <a href=
|
* aka ".conf", or <a href=
|
||||||
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
||||||
* >Java properties</a>.
|
* >Java properties</a>).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public enum ConfigSyntax {
|
public enum ConfigSyntax {
|
||||||
/**
|
/**
|
||||||
* Pedantically strict <a href="http://json.org">JSON</a> format; no
|
* Pedantically strict <a href="http://json.org">JSON</a> format; no
|
||||||
* comments, no unexpected commas, no duplicate keys in the same object.
|
* comments, no unexpected commas, no duplicate keys in the same object.
|
||||||
|
* Associated with the <code>.json</code> file extension and
|
||||||
|
* <code>application/json</code> Content-Type.
|
||||||
*/
|
*/
|
||||||
JSON,
|
JSON,
|
||||||
/**
|
/**
|
||||||
* The JSON-superset <a
|
* The JSON-superset <a
|
||||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md"
|
* href="https://github.com/typesafehub/config/blob/master/HOCON.md"
|
||||||
* >HOCON</a> format.
|
* >HOCON</a> format. Associated with the <code>.conf</code> file extension
|
||||||
|
* and <code>application/hocon</code> Content-Type.
|
||||||
*/
|
*/
|
||||||
CONF,
|
CONF,
|
||||||
/**
|
/**
|
||||||
* Standard <a href=
|
* Standard <a href=
|
||||||
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
||||||
* >Java properties</a> format.
|
* >Java properties</a> format. Associated with the <code>.properties</code>
|
||||||
|
* file extension and <code>text/x-java-properties</code> Content-Type.
|
||||||
*/
|
*/
|
||||||
PROPERTIES;
|
PROPERTIES;
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,16 @@ package com.typesafe.config;
|
|||||||
/**
|
/**
|
||||||
* An immutable value, following the <a href="http://json.org">JSON</a> type
|
* An immutable value, following the <a href="http://json.org">JSON</a> type
|
||||||
* schema.
|
* schema.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Because this object is immutable, it is safe to use from multiple threads and
|
* Because this object is immutable, it is safe to use from multiple threads and
|
||||||
* there's no need for "defensive copies."
|
* there's no need for "defensive copies."
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* <em>Do not implement {@code ConfigValue}</em>; it should only be implemented
|
* <em>Do not implement interface {@code ConfigValue}</em>; it should only be
|
||||||
* by the config library. Arbitrary implementations will not work because the
|
* implemented by the config library. Arbitrary implementations will not work
|
||||||
* library internals assume a specific concrete implementation. Also, this
|
* because the library internals assume a specific concrete implementation.
|
||||||
* interface is likely to grow new methods over time, so third-party
|
* Also, this interface is likely to grow new methods over time, so third-party
|
||||||
* implementations will break.
|
* implementations will break.
|
||||||
*/
|
*/
|
||||||
public interface ConfigValue extends ConfigMergeable {
|
public interface ConfigValue extends ConfigMergeable {
|
||||||
@ -47,36 +47,36 @@ public interface ConfigValue extends ConfigMergeable {
|
|||||||
* Renders the config value as a HOCON string. This method is primarily
|
* Renders the config value as a HOCON string. This method is primarily
|
||||||
* intended for debugging, so it tries to add helpful comments and
|
* intended for debugging, so it tries to add helpful comments and
|
||||||
* whitespace.
|
* whitespace.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If the config value has not been resolved (see {@link Config#resolve}),
|
* If the config value has not been resolved (see {@link Config#resolve}),
|
||||||
* it's possible that it can't be rendered as valid HOCON. In that case the
|
* it's possible that it can't be rendered as valid HOCON. In that case the
|
||||||
* rendering should still be useful for debugging but you might not be able
|
* rendering should still be useful for debugging but you might not be able
|
||||||
* to parse it.
|
* to parse it. If the value has been resolved, it will always be parseable.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This method is equivalent to
|
* This method is equivalent to
|
||||||
* {@code render(ConfigRenderOptions.defaults())}.
|
* {@code render(ConfigRenderOptions.defaults())}.
|
||||||
*
|
*
|
||||||
* @return the rendered value
|
* @return the rendered value
|
||||||
*/
|
*/
|
||||||
String render();
|
String render();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the config value to a string, using the provided options.
|
* Renders the config value to a string, using the provided options.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If the config value has not been resolved (see {@link Config#resolve}),
|
* If the config value has not been resolved (see {@link Config#resolve}),
|
||||||
* it's possible that it can't be rendered as valid HOCON. In that case the
|
* it's possible that it can't be rendered as valid HOCON. In that case the
|
||||||
* rendering should still be useful for debugging but you might not be able
|
* rendering should still be useful for debugging but you might not be able
|
||||||
* to parse it.
|
* to parse it. If the value has been resolved, it will always be parseable.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If the config value has been resolved and the options disable all
|
* If the config value has been resolved and the options disable all
|
||||||
* HOCON-specific features (such as comments), the rendering will be valid
|
* HOCON-specific features (such as comments), the rendering will be valid
|
||||||
* JSON. If you enable HOCON-only features such as comments, the rendering
|
* JSON. If you enable HOCON-only features such as comments, the rendering
|
||||||
* will not be valid JSON.
|
* will not be valid JSON.
|
||||||
*
|
*
|
||||||
* @param options
|
* @param options
|
||||||
* the rendering options
|
* the rendering options
|
||||||
* @return the rendered value
|
* @return the rendered value
|
||||||
@ -87,9 +87,9 @@ public interface ConfigValue extends ConfigMergeable {
|
|||||||
ConfigValue withFallback(ConfigMergeable other);
|
ConfigValue withFallback(ConfigMergeable other);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Places the value inside a {@code Config} at the given path. See also
|
* Places the value inside a {@link Config} at the given path. See also
|
||||||
* atKey().
|
* {@link ConfigValue#atKey(String)}.
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* path to store this value at.
|
* path to store this value at.
|
||||||
* @return a {@code Config} instance containing this value at the given
|
* @return a {@code Config} instance containing this value at the given
|
||||||
@ -98,9 +98,9 @@ public interface ConfigValue extends ConfigMergeable {
|
|||||||
Config atPath(String path);
|
Config atPath(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Places the value inside a {@code Config} at the given key. See also
|
* Places the value inside a {@link Config} at the given key. See also
|
||||||
* atPath().
|
* {@link ConfigValue#atPath(String)}.
|
||||||
*
|
*
|
||||||
* @param key
|
* @param key
|
||||||
* key to store this value at.
|
* key to store this value at.
|
||||||
* @return a {@code Config} instance containing this value at the given key.
|
* @return a {@code Config} instance containing this value at the given key.
|
||||||
|
@ -17,40 +17,45 @@ public final class ConfigValueFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a ConfigValue from a plain Java boxed value, which may be a
|
* Creates a {@link ConfigValue} from a plain Java boxed value, which may be
|
||||||
* Boolean, Number, String, Map, Iterable, or null. A Map must be a Map from
|
* a <code>Boolean</code>, <code>Number</code>, <code>String</code>,
|
||||||
* String to more values that can be supplied to fromAnyRef(). An Iterable
|
* <code>Map</code>, <code>Iterable</code>, or <code>null</code>. A
|
||||||
* must iterate over more values that can be supplied to fromAnyRef(). A Map
|
* <code>Map</code> must be a <code>Map</code> from String to more values
|
||||||
* will become a ConfigObject and an Iterable will become a ConfigList. If
|
* that can be supplied to <code>fromAnyRef()</code>. An
|
||||||
* the Iterable is not an ordered collection, results could be strange,
|
* <code>Iterable</code> must iterate over more values that can be supplied
|
||||||
* since ConfigList is ordered.
|
* to <code>fromAnyRef()</code>. A <code>Map</code> will become a
|
||||||
*
|
* {@link ConfigObject} and an <code>Iterable</code> will become a
|
||||||
|
* {@link ConfigList}. If the <code>Iterable</code> is not an ordered
|
||||||
|
* collection, results could be strange, since <code>ConfigList</code> is
|
||||||
|
* ordered.
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* In a Map passed to fromAnyRef(), the map's keys are plain keys, not path
|
* In a <code>Map</code> passed to <code>fromAnyRef()</code>, the map's keys
|
||||||
* expressions. So if your Map has a key "foo.bar" then you will get one
|
* are plain keys, not path expressions. So if your <code>Map</code> has a
|
||||||
* object with a key called "foo.bar", rather than an object with a key
|
* key "foo.bar" then you will get one object with a key called "foo.bar",
|
||||||
* "foo" containing another object with a key "bar".
|
* rather than an object with a key "foo" containing another object with a
|
||||||
*
|
* key "bar".
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The originDescription will be used to set the origin() field on the
|
* The originDescription will be used to set the origin() field on the
|
||||||
* ConfigValue. It should normally be the name of the file the values came
|
* ConfigValue. It should normally be the name of the file the values came
|
||||||
* from, or something short describing the value such as "default settings".
|
* from, or something short describing the value such as "default settings".
|
||||||
* The originDescription is prefixed to error messages so users can tell
|
* The originDescription is prefixed to error messages so users can tell
|
||||||
* where problematic values are coming from.
|
* where problematic values are coming from.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Supplying the result of ConfigValue.unwrapped() to this function is
|
* Supplying the result of ConfigValue.unwrapped() to this function is
|
||||||
* guaranteed to work and should give you back a ConfigValue that matches
|
* guaranteed to work and should give you back a ConfigValue that matches
|
||||||
* the one you unwrapped. The re-wrapped ConfigValue will lose some
|
* the one you unwrapped. The re-wrapped ConfigValue will lose some
|
||||||
* information that was present in the original such as its origin, but it
|
* information that was present in the original such as its origin, but it
|
||||||
* will have matching values.
|
* will have matching values.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This function throws if you supply a value that cannot be converted to a
|
* This function throws if you supply a value that cannot be converted to a
|
||||||
* ConfigValue, but supplying such a value is a bug in your program, so you
|
* ConfigValue, but supplying such a value is a bug in your program, so you
|
||||||
* should never handle the exception. Just fix your program (or report a bug
|
* should never handle the exception. Just fix your program (or report a bug
|
||||||
* against this library).
|
* against this library).
|
||||||
*
|
*
|
||||||
* @param object
|
* @param object
|
||||||
* object to convert to ConfigValue
|
* object to convert to ConfigValue
|
||||||
* @param originDescription
|
* @param originDescription
|
||||||
@ -62,23 +67,24 @@ public final class ConfigValueFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
* See the {@link #fromAnyRef(Object,String)} documentation for details.
|
||||||
* wrapper that only works on {@link java.util.Map} and returns
|
* This is a typesafe wrapper that only works on {@link java.util.Map} and
|
||||||
* {@link ConfigObject} rather than {@link ConfigValue}.
|
* returns {@link ConfigObject} rather than {@link ConfigValue}.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If your Map has a key "foo.bar" then you will get one object with a key
|
* If your <code>Map</code> has a key "foo.bar" then you will get one object
|
||||||
* called "foo.bar", rather than an object with a key "foo" containing
|
* with a key called "foo.bar", rather than an object with a key "foo"
|
||||||
* another object with a key "bar". The keys in the map are keys; not path
|
* containing another object with a key "bar". The keys in the map are keys;
|
||||||
* expressions. That is, the Map corresponds exactly to a single
|
* not path expressions. That is, the <code>Map</code> corresponds exactly
|
||||||
* {@code ConfigObject}. The keys will not be parsed or modified, and the
|
* to a single {@code ConfigObject}. The keys will not be parsed or
|
||||||
* values are wrapped in ConfigValue. To get nested {@code ConfigObject},
|
* modified, and the values are wrapped in ConfigValue. To get nested
|
||||||
* some of the values in the map would have to be more maps.
|
* {@code ConfigObject}, some of the values in the map would have to be more
|
||||||
*
|
* maps.
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* See also {@link ConfigFactory#parseMap(Map,String)} which interprets the
|
* See also {@link ConfigFactory#parseMap(Map,String)} which interprets the
|
||||||
* keys in the map as path expressions.
|
* keys in the map as path expressions.
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values
|
||||||
* @param originDescription
|
* @param originDescription
|
||||||
* @return a new {@link ConfigObject} value
|
* @return a new {@link ConfigObject} value
|
||||||
@ -89,10 +95,10 @@ public final class ConfigValueFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
* See the {@link #fromAnyRef(Object,String)} documentation for details.
|
||||||
* wrapper that only works on {@link java.lang.Iterable} and returns
|
* This is a typesafe wrapper that only works on {@link java.lang.Iterable}
|
||||||
* {@link ConfigList} rather than {@link ConfigValue}.
|
* and returns {@link ConfigList} rather than {@link ConfigValue}.
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values
|
||||||
* @param originDescription
|
* @param originDescription
|
||||||
* @return a new {@link ConfigList} value
|
* @return a new {@link ConfigList} value
|
||||||
|
@ -218,7 +218,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements Confi
|
|||||||
public abstract AbstractConfigValue get(Object key);
|
public abstract AbstractConfigValue get(Object key);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected abstract void render(StringBuilder sb, int indent, ConfigRenderOptions options);
|
protected abstract void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options);
|
||||||
|
|
||||||
private static UnsupportedOperationException weAreImmutable(String method) {
|
private static UnsupportedOperationException weAreImmutable(String method) {
|
||||||
return new UnsupportedOperationException("ConfigObject is immutable, you can't call Map."
|
return new UnsupportedOperationException("ConfigObject is immutable, you can't call Map."
|
||||||
|
@ -279,7 +279,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||||||
@Override
|
@Override
|
||||||
public final String toString() {
|
public final String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
render(sb, 0, null /* atKey */, ConfigRenderOptions.concise());
|
render(sb, 0, true /* atRoot */, null /* atKey */, ConfigRenderOptions.concise());
|
||||||
return getClass().getSimpleName() + "(" + sb.toString() + ")";
|
return getClass().getSimpleName() + "(" + sb.toString() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void render(StringBuilder sb, int indent, String atKey, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||||
if (atKey != null) {
|
if (atKey != null) {
|
||||||
String renderedKey;
|
String renderedKey;
|
||||||
if (options.getJson())
|
if (options.getJson())
|
||||||
@ -318,10 +318,10 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render(sb, indent, options);
|
render(sb, indent, atRoot, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
Object u = unwrapped();
|
Object u = unwrapped();
|
||||||
sb.append(u.toString());
|
sb.append(u.toString());
|
||||||
}
|
}
|
||||||
@ -334,7 +334,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||||||
@Override
|
@Override
|
||||||
public final String render(ConfigRenderOptions options) {
|
public final String render(ConfigRenderOptions options) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
render(sb, 0, null, options);
|
render(sb, 0, true, null, options);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,9 +232,9 @@ final class ConfigConcatenation extends AbstractConfigValue implements Unmergeab
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
for (AbstractConfigValue p : pieces) {
|
for (AbstractConfigValue p : pieces) {
|
||||||
p.render(sb, indent, options);
|
p.render(sb, indent, atRoot, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,12 +216,12 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, String atKey, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||||
render(stack, sb, indent, atKey, options);
|
render(stack, sb, indent, atRoot, atKey, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static method also used by ConfigDelayedMergeObject.
|
// static method also used by ConfigDelayedMergeObject.
|
||||||
static void render(List<AbstractConfigValue> stack, StringBuilder sb, int indent, String atKey,
|
static void render(List<AbstractConfigValue> stack, StringBuilder sb, int indent, boolean atRoot, String atKey,
|
||||||
ConfigRenderOptions options) {
|
ConfigRenderOptions options) {
|
||||||
boolean commentMerge = options.getComments();
|
boolean commentMerge = options.getComments();
|
||||||
if (commentMerge) {
|
if (commentMerge) {
|
||||||
@ -268,7 +268,7 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
|||||||
else
|
else
|
||||||
sb.append(":");
|
sb.append(":");
|
||||||
}
|
}
|
||||||
v.render(sb, indent, options);
|
v.render(sb, indent, atRoot, options);
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
if (options.getFormatted())
|
if (options.getFormatted())
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
|
@ -179,13 +179,13 @@ final class ConfigDelayedMergeObject extends AbstractConfigObject implements Unm
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, String atKey, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||||
ConfigDelayedMerge.render(stack, sb, indent, atKey, options);
|
ConfigDelayedMerge.render(stack, sb, indent, atRoot, atKey, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
render(sb, indent, null, options);
|
render(sb, indent, atRoot, null, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigException notResolved() {
|
private static ConfigException notResolved() {
|
||||||
|
@ -42,7 +42,7 @@ final class ConfigNull extends AbstractConfigValue implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
sb.append("null");
|
sb.append("null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,9 +81,13 @@ final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (v == null && !expr.optional()) {
|
if (v == null && !expr.optional()) {
|
||||||
throw new ConfigException.UnresolvedSubstitution(origin(), expr.toString());
|
if (context.options().getAllowUnresolved())
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
throw new ConfigException.UnresolvedSubstitution(origin(), expr.toString());
|
||||||
|
} else {
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
return v;
|
|
||||||
} finally {
|
} finally {
|
||||||
context.source().unreplace(this);
|
context.source().unreplace(this);
|
||||||
}
|
}
|
||||||
@ -127,7 +131,7 @@ final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
sb.append(expr.toString());
|
sb.append(expr.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ final class ConfigString extends AbstractConfigValue implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
String rendered;
|
String rendered;
|
||||||
if (options.getJson())
|
if (options.getJson())
|
||||||
rendered = ConfigImplUtil.renderJsonString(value);
|
rendered = ConfigImplUtil.renderJsonString(value);
|
||||||
|
@ -683,7 +683,9 @@ final class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!pathStack.isEmpty()) {
|
if (!pathStack.isEmpty()) {
|
||||||
Path prefix = new Path(pathStack);
|
// The stack is in reverse order (most recent first on the
|
||||||
|
// iterator), so build the path from the reversed iterator.
|
||||||
|
Path prefix = new Path(pathStack.descendingIterator());
|
||||||
obj = obj.relativized(prefix);
|
obj = obj.relativized(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,10 +35,14 @@ final class Path {
|
|||||||
|
|
||||||
// append all the paths in the list together into one path
|
// append all the paths in the list together into one path
|
||||||
Path(List<Path> pathsToConcat) {
|
Path(List<Path> pathsToConcat) {
|
||||||
if (pathsToConcat.isEmpty())
|
this(pathsToConcat.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
// append all the paths in the iterator together into one path
|
||||||
|
Path(Iterator<Path> i) {
|
||||||
|
if (!i.hasNext())
|
||||||
throw new ConfigException.BugOrBroken("empty path");
|
throw new ConfigException.BugOrBroken("empty path");
|
||||||
|
|
||||||
Iterator<Path> i = pathsToConcat.iterator();
|
|
||||||
Path firstPath = i.next();
|
Path firstPath = i.next();
|
||||||
this.first = firstPath.first;
|
this.first = firstPath.first;
|
||||||
|
|
||||||
|
@ -121,14 +121,16 @@ final class ResolveContext {
|
|||||||
memos.put(fullKey, resolved);
|
memos.put(fullKey, resolved);
|
||||||
} else {
|
} else {
|
||||||
// if we have an unresolved object then either we did a
|
// if we have an unresolved object then either we did a
|
||||||
// partial resolve restricted to a certain child, or it's
|
// partial resolve restricted to a certain child, or we are
|
||||||
// a bug.
|
// allowing incomplete resolution, or it's a bug.
|
||||||
if (isRestrictedToChild()) {
|
if (isRestrictedToChild()) {
|
||||||
if (restrictedKey == null) {
|
if (restrictedKey == null) {
|
||||||
throw new ConfigException.BugOrBroken(
|
throw new ConfigException.BugOrBroken(
|
||||||
"restrictedKey should not be null here");
|
"restrictedKey should not be null here");
|
||||||
}
|
}
|
||||||
memos.put(restrictedKey, resolved);
|
memos.put(restrictedKey, resolved);
|
||||||
|
} else if (options().getAllowUnresolved()) {
|
||||||
|
memos.put(fullKey, resolved);
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigException.BugOrBroken(
|
throw new ConfigException.BugOrBroken(
|
||||||
"resolveSubstitutions() did not give us a resolved object");
|
"resolveSubstitutions() did not give us a resolved object");
|
||||||
|
@ -57,7 +57,17 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SimpleConfig resolve(ConfigResolveOptions options) {
|
public SimpleConfig resolve(ConfigResolveOptions options) {
|
||||||
AbstractConfigValue resolved = ResolveContext.resolve(object, object, options);
|
return resolveWith(this, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfig resolveWith(Config source) {
|
||||||
|
return resolveWith(source, ConfigResolveOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleConfig resolveWith(Config source, ConfigResolveOptions options) {
|
||||||
|
AbstractConfigValue resolved = ResolveContext.resolve(object, ((SimpleConfig) source).object, options);
|
||||||
|
|
||||||
if (resolved == object)
|
if (resolved == object)
|
||||||
return this;
|
return this;
|
||||||
@ -65,7 +75,6 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
|||||||
return new SimpleConfig((AbstractConfigObject) resolved);
|
return new SimpleConfig((AbstractConfigObject) resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPath(String pathExpression) {
|
public boolean hasPath(String pathExpression) {
|
||||||
Path path = Path.newPath(pathExpression);
|
Path path = Path.newPath(pathExpression);
|
||||||
@ -247,9 +256,9 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getDuration(String path, TimeUnit unit) {
|
public long getDuration(String path, TimeUnit unit) {
|
||||||
ConfigValue v = find(path, ConfigValueType.STRING);
|
ConfigValue v = find(path, ConfigValueType.STRING);
|
||||||
Long result = unit.convert(
|
long result = unit.convert(
|
||||||
parseDuration((String) v.unwrapped(), v.origin(), path),
|
parseDuration((String) v.unwrapped(), v.origin(), path),
|
||||||
TimeUnit.NANOSECONDS);
|
TimeUnit.NANOSECONDS);
|
||||||
return result;
|
return result;
|
||||||
@ -815,6 +824,11 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isResolved() {
|
||||||
|
return root().resolveStatus() == ResolveStatus.RESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void checkValid(Config reference, String... restrictToPaths) {
|
public void checkValid(Config reference, String... restrictToPaths) {
|
||||||
SimpleConfig ref = (SimpleConfig) reference;
|
SimpleConfig ref = (SimpleConfig) reference;
|
||||||
|
@ -167,7 +167,7 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
sb.append("[]");
|
sb.append("[]");
|
||||||
} else {
|
} else {
|
||||||
@ -191,7 +191,7 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList,
|
|||||||
}
|
}
|
||||||
indent(sb, indent + 1, options);
|
indent(sb, indent + 1, options);
|
||||||
|
|
||||||
v.render(sb, indent + 1, options);
|
v.render(sb, indent + 1, atRoot, options);
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
if (options.getFormatted())
|
if (options.getFormatted())
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
|
@ -364,17 +364,22 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
sb.append("{}");
|
sb.append("{}");
|
||||||
} else {
|
} else {
|
||||||
boolean outerBraces = indent > 0 || options.getJson();
|
boolean outerBraces = options.getJson() || !atRoot;
|
||||||
|
|
||||||
if (outerBraces)
|
int innerIndent;
|
||||||
|
if (outerBraces) {
|
||||||
|
innerIndent = indent + 1;
|
||||||
sb.append("{");
|
sb.append("{");
|
||||||
|
|
||||||
if (options.getFormatted())
|
if (options.getFormatted())
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
|
} else {
|
||||||
|
innerIndent = indent;
|
||||||
|
}
|
||||||
|
|
||||||
int separatorCount = 0;
|
int separatorCount = 0;
|
||||||
for (String k : keySet()) {
|
for (String k : keySet()) {
|
||||||
@ -382,14 +387,14 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
|||||||
v = value.get(k);
|
v = value.get(k);
|
||||||
|
|
||||||
if (options.getOriginComments()) {
|
if (options.getOriginComments()) {
|
||||||
indent(sb, indent + 1, options);
|
indent(sb, innerIndent, options);
|
||||||
sb.append("# ");
|
sb.append("# ");
|
||||||
sb.append(v.origin().description());
|
sb.append(v.origin().description());
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
}
|
}
|
||||||
if (options.getComments()) {
|
if (options.getComments()) {
|
||||||
for (String comment : v.origin().comments()) {
|
for (String comment : v.origin().comments()) {
|
||||||
indent(sb, indent + 1, options);
|
indent(sb, innerIndent, options);
|
||||||
sb.append("#");
|
sb.append("#");
|
||||||
if (!comment.startsWith(" "))
|
if (!comment.startsWith(" "))
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
@ -397,8 +402,8 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
|||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
indent(sb, indent + 1, options);
|
indent(sb, innerIndent, options);
|
||||||
v.render(sb, indent + 1, k, options);
|
v.render(sb, innerIndent, false /* atRoot */, k, options);
|
||||||
|
|
||||||
if (options.getFormatted()) {
|
if (options.getFormatted()) {
|
||||||
if (options.getJson()) {
|
if (options.getJson()) {
|
||||||
@ -415,14 +420,18 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
|||||||
}
|
}
|
||||||
// chop last commas/newlines
|
// chop last commas/newlines
|
||||||
sb.setLength(sb.length() - separatorCount);
|
sb.setLength(sb.length() - separatorCount);
|
||||||
if (options.getFormatted()) {
|
|
||||||
sb.append("\n"); // put a newline back
|
|
||||||
indent(sb, indent, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerBraces)
|
if (outerBraces) {
|
||||||
|
if (options.getFormatted()) {
|
||||||
|
sb.append('\n'); // put a newline back
|
||||||
|
if (outerBraces)
|
||||||
|
indent(sb, indent, options);
|
||||||
|
}
|
||||||
sb.append("}");
|
sb.append("}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (atRoot && options.getFormatted())
|
||||||
|
sb.append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -15,7 +15,9 @@ for more information.
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use
|
Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use
|
||||||
it with methods in the {@link com.typesafe.config.Config} interface.
|
it with methods in the {@link com.typesafe.config.Config} interface. Configuration may be in the form of JSON files,
|
||||||
|
Java properties, or <a href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON files</a>; you may also
|
||||||
|
build your own configuration in code or from your own file formats.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@ -24,6 +26,8 @@ its configuration in "application.conf" on the classpath.
|
|||||||
If you use the default configuration from {@link com.typesafe.config.ConfigFactory#load()}
|
If you use the default configuration from {@link com.typesafe.config.ConfigFactory#load()}
|
||||||
there's no need to pass a configuration to your libraries
|
there's no need to pass a configuration to your libraries
|
||||||
and frameworks, as long as they all default to this same default, which they should.
|
and frameworks, as long as they all default to this same default, which they should.
|
||||||
|
<br/><strong>Example application code:</strong> <a href="https://github.com/typesafehub/config/tree/master/examples/java/simple-app/src/main">Java</a> and <a href="https://github.com/typesafehub/config/tree/master/examples/scala/simple-app/src/main">Scala</a>.
|
||||||
|
<br/>Showing a couple of more special-purpose features, <strong>a more complex example:</strong> <a href="https://github.com/typesafehub/config/tree/master/examples/java/complex-app/src/main">Java</a> and <a href="https://github.com/typesafehub/config/tree/master/examples/scala/complex-app/src/main">Scala</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@ -32,10 +36,22 @@ A library or framework should ship a file "reference.conf" in its jar, and allow
|
|||||||
call {@link com.typesafe.config.ConfigFactory#load()}
|
call {@link com.typesafe.config.ConfigFactory#load()}
|
||||||
to get the default one. Typically a library might offer two constructors, one with a <code>Config</code> parameter
|
to get the default one. Typically a library might offer two constructors, one with a <code>Config</code> parameter
|
||||||
and one which uses {@link com.typesafe.config.ConfigFactory#load()}.
|
and one which uses {@link com.typesafe.config.ConfigFactory#load()}.
|
||||||
|
<br/><strong>Example library code:</strong> <a href="https://github.com/typesafehub/config/tree/master/examples/java/simple-lib/src/main">Java</a> and <a href="https://github.com/typesafehub/config/tree/master/examples/scala/simple-lib/src/main">Scala</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You can find an example app and library <a href="https://github.com/typesafehub/config/tree/master/examples">on GitHub</a>.
|
Check out the full <a href="https://github.com/typesafehub/config/tree/master/examples">examples directory on GitHub</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
What else to read:
|
||||||
|
<ul>
|
||||||
|
<li>The overview documentation for interface {@link com.typesafe.config.Config}.</li>
|
||||||
|
<li>The <a href="https://github.com/typesafehub/config/blob/master/README.md">README</a> for the library.</li>
|
||||||
|
<li>If you want to use <code>.conf</code> files in addition to <code>.json</code> and <code>.properties</code>,
|
||||||
|
see the <a href="https://github.com/typesafehub/config/blob/master/README.md">README</a> for some short examples
|
||||||
|
and the full <a href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON spec</a> for the long version.</li>
|
||||||
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user