mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-18 11:48:02 -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 out = new PrintWriter(file)
|
||||
out.write(config.root.render(renderSettings).lines.
|
||||
// Strip extra spaces in front.
|
||||
map(_.stripPrefix(" ")).
|
||||
// Indent two spaces instead of four.
|
||||
map(line => """^(\s*)""".r.replaceAllIn(line, m => m.group(1).replace(" ", " "))).
|
||||
// Finalize the string.
|
||||
|
@ -9,19 +9,50 @@ import java.util.Set;
|
||||
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>
|
||||
* Contrast with {@link ConfigObject} which is a map from config <em>keys</em>,
|
||||
* rather than paths, to config values. A {@code Config} contains a tree of
|
||||
* {@code ConfigObject}, and {@link Config#root()} returns the tree's root
|
||||
* object.
|
||||
* {@code Config} is an immutable object and thus safe to use from multiple
|
||||
* threads. There's never a need for "defensive copies."
|
||||
*
|
||||
* <p>
|
||||
* Throughout the API, there is a distinction between "keys" and "paths". A key
|
||||
* is a key in a JSON object; it's just a string that's the key in a map. A
|
||||
* "path" is a parseable expression with a syntax and it refers to a series of
|
||||
* keys. Path expressions are described in the <a
|
||||
* Fundamental operations on a {@code Config} include getting configuration
|
||||
* values, <em>resolving</em> substitutions with {@link Config#resolve()}, and
|
||||
* merging configs using {@link Config#withFallback(ConfigMergeable)}.
|
||||
*
|
||||
* <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
|
||||
* 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
|
||||
@ -46,8 +77,7 @@ import java.util.concurrent.TimeUnit;
|
||||
* were missing.
|
||||
*
|
||||
* <p>
|
||||
* {@code Config} is an immutable object and thus safe to use from multiple
|
||||
* threads. There's never a need for "defensive copies."
|
||||
* <strong>Getting configuration values</strong>
|
||||
*
|
||||
* <p>
|
||||
* The "getters" on a {@code Config} all work in the same way. They never return
|
||||
@ -61,22 +91,68 @@ import java.util.concurrent.TimeUnit;
|
||||
* are performed for you though.
|
||||
*
|
||||
* <p>
|
||||
* <strong>Iteration</strong>
|
||||
*
|
||||
* <p>
|
||||
* 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 <code>java.util.Map</code>). Or, you
|
||||
* 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.
|
||||
*
|
||||
* <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>Resolving substitutions</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>
|
||||
* <em>Substitutions</em> are the <code>${foo.bar}</code> syntax in config
|
||||
* files, described in the <a href=
|
||||
* "https://github.com/typesafehub/config/blob/master/HOCON.md#substitutions"
|
||||
* >specification</a>. Resolving substitutions replaces these references with real
|
||||
* values.
|
||||
*
|
||||
* <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>
|
||||
* <em>Do not implement {@code Config}</em>; it should only be implemented by
|
||||
@ -125,7 +201,8 @@ public interface Config extends ConfigMergeable {
|
||||
* 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 a user-visible oddity.
|
||||
* not resolve, which will be potentially confusing for your application's
|
||||
* users.
|
||||
*
|
||||
* <p>
|
||||
* <code>resolve()</code> should be invoked on root config objects, rather
|
||||
@ -145,14 +222,14 @@ public interface Config extends ConfigMergeable {
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Many methods on {@link ConfigFactory} such as {@link
|
||||
* ConfigFactory#load()} automatically resolve the loaded
|
||||
* Many methods on {@link ConfigFactory} such as
|
||||
* {@link ConfigFactory#load()} automatically resolve the loaded
|
||||
* <code>Config</code> on the loaded stack of config files.
|
||||
*
|
||||
* <p> 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.
|
||||
* <p>
|
||||
* 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
|
||||
@ -168,10 +245,67 @@ public interface Config extends ConfigMergeable {
|
||||
*
|
||||
* @param 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* spec</a>.
|
||||
*
|
||||
* @since 1.1
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param path
|
||||
* path expression
|
||||
@ -498,7 +632,7 @@ public interface Config extends ConfigMergeable {
|
||||
* @throws ConfigException.BadValue
|
||||
* 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
|
||||
@ -549,7 +683,7 @@ public interface Config extends ConfigMergeable {
|
||||
* Gets a list, converting each value in the list to a duration, using the
|
||||
* same rules as {@link #getDuration(String, TimeUnit)}.
|
||||
*
|
||||
* @since 1.1
|
||||
* @since 1.2.0
|
||||
* @param path
|
||||
* a path expression
|
||||
* @param unit
|
||||
|
@ -219,21 +219,22 @@ public final class ConfigFactory {
|
||||
+ "', config.url='" + url + "', config.resource='" + resource
|
||||
+ "'; don't know which one to use!");
|
||||
} else {
|
||||
// the override file/url/resource MUST be present or it's an error
|
||||
ConfigParseOptions overrideOptions = parseOptions.setAllowMissing(false);
|
||||
if (resource != null) {
|
||||
if (resource.startsWith("/"))
|
||||
resource = resource.substring(1);
|
||||
// this deliberately does not parseResourcesAnySyntax; if
|
||||
// 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) {
|
||||
return load(
|
||||
loader,
|
||||
parseFile(new File(file), parseOptions), resolveOptions);
|
||||
Config parsedFile = parseFile(new File(file), overrideOptions);
|
||||
return load(loader, parsedFile, resolveOptions);
|
||||
} else {
|
||||
try {
|
||||
return load(
|
||||
loader,
|
||||
parseURL(new URL(url), parseOptions), resolveOptions);
|
||||
Config parsedURL = parseURL(new URL(url), overrideOptions);
|
||||
return load(loader, parsedURL, resolveOptions);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new ConfigException.Generic("Bad URL in config.url system property: '"
|
||||
+ url + "': " + e.getMessage(), e);
|
||||
|
@ -19,43 +19,52 @@ package com.typesafe.config;
|
||||
public interface ConfigMergeable {
|
||||
/**
|
||||
* Returns a new value computed by merging this value with another, with
|
||||
* keys in this value "winning" over the other one. 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.
|
||||
* keys in this value "winning" over the other one.
|
||||
*
|
||||
* <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
|
||||
* for HOCON</a>. Merging typically occurs when either the
|
||||
* same object is created twice in the same file, or two
|
||||
* config files are both loaded. For example:
|
||||
* for HOCON</a>. Merging typically occurs when either the same object is
|
||||
* created twice in the same file, or two config files are both loaded. For
|
||||
* example:
|
||||
*
|
||||
* <pre>
|
||||
* foo = { a: 42 }
|
||||
* foo = { b: 43 }
|
||||
* </pre>
|
||||
*
|
||||
* Here, the two objects are merged as if you had written:
|
||||
*
|
||||
* <pre>
|
||||
* foo = { a: 42, b: 43 }
|
||||
* </pre>
|
||||
*
|
||||
* <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>,
|
||||
* then <code>otherObject</code> will simply be ignored. This is an
|
||||
* intentional part of how merging works, because non-objects such as
|
||||
* strings and integers replace (rather than merging with) any
|
||||
* prior value:
|
||||
* strings and integers replace (rather than merging with) any prior value:
|
||||
*
|
||||
* <pre>
|
||||
* foo = { a: 42 }
|
||||
* foo = 10
|
||||
* </pre>
|
||||
*
|
||||
* Here, the number 10 "wins" and the value of <code>foo</code> would be
|
||||
* simply 10. Again, for details see the spec.
|
||||
*
|
||||
* @param other
|
||||
* an object whose keys should be used if the keys are not
|
||||
* present in this one
|
||||
* an object whose keys should be used as fallbacks, if the keys
|
||||
* are not present in this one
|
||||
* @return a new object (or the original one, if the fallback doesn't get
|
||||
* used)
|
||||
*/
|
||||
|
@ -6,8 +6,12 @@ package com.typesafe.config;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Subtype of {@link ConfigValue} representing an object (dictionary, map)
|
||||
* value, as in JSON's <code>{ "a" : 42 }</code> syntax.
|
||||
* Subtype of {@link ConfigValue} representing an object (AKA dictionary or map)
|
||||
* 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>
|
||||
* {@code ConfigObject} implements {@code java.util.Map<String, ConfigValue>} so
|
||||
@ -43,16 +47,17 @@ import java.util.Map;
|
||||
* <p>
|
||||
* A {@code ConfigObject} may contain null values, which will have
|
||||
* {@link ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If
|
||||
* {@code get()} returns Java's null then the key was not present in the parsed
|
||||
* file (or wherever this value tree came from). If {@code get()} returns a
|
||||
* {@link ConfigValue} with type {@code ConfigValueType#NULL} then the key was
|
||||
* set to null explicitly in the config file.
|
||||
* {@link ConfigObject#get(Object)} returns Java's null then the key was not
|
||||
* present in the parsed file (or wherever this value tree came from). If
|
||||
* {@code get("key")} returns a {@link ConfigValue} with type
|
||||
* {@code ConfigValueType#NULL} then the key was set to null explicitly in the
|
||||
* config file.
|
||||
*
|
||||
* <p>
|
||||
* <em>Do not implement {@code ConfigObject}</em>; 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
|
||||
* <em>Do not implement interface {@code ConfigObject}</em>; 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 ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||
@ -82,7 +87,7 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||
* Gets a {@link ConfigValue} at the given key, or returns null if there is
|
||||
* no value. The returned {@link ConfigValue} may have
|
||||
* {@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
|
||||
* key to look up
|
||||
@ -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
|
||||
* 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
|
||||
* remove a value, use withoutKey().
|
||||
* remove a value, use {@link ConfigObject#withoutKey(String)}.
|
||||
*
|
||||
* @param key
|
||||
* key to add
|
||||
|
@ -9,6 +9,9 @@ package com.typesafe.config;
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
|
||||
* spec.
|
||||
* <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.
|
||||
* <p>
|
||||
* Here is an example of creating a custom {@code ConfigResolveOptions}:
|
||||
@ -25,18 +28,21 @@ package com.typesafe.config;
|
||||
*/
|
||||
public final class ConfigResolveOptions {
|
||||
private final boolean useSystemEnvironment;
|
||||
private final boolean allowUnresolved;
|
||||
|
||||
private ConfigResolveOptions(boolean useSystemEnvironment) {
|
||||
private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolved) {
|
||||
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
|
||||
*/
|
||||
public static ConfigResolveOptions defaults() {
|
||||
return new ConfigResolveOptions(true);
|
||||
return new ConfigResolveOptions(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,9 +63,8 @@ public final class ConfigResolveOptions {
|
||||
* variables.
|
||||
* @return options with requested setting for use of environment variables
|
||||
*/
|
||||
@SuppressWarnings("static-method")
|
||||
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() {
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* aka ".conf", or <a href=
|
||||
* "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 {
|
||||
/**
|
||||
* Pedantically strict <a href="http://json.org">JSON</a> format; no
|
||||
* 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,
|
||||
/**
|
||||
* The JSON-superset <a
|
||||
* 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,
|
||||
/**
|
||||
* Standard <a href=
|
||||
* "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;
|
||||
}
|
||||
|
@ -12,10 +12,10 @@ package com.typesafe.config;
|
||||
* there's no need for "defensive copies."
|
||||
*
|
||||
* <p>
|
||||
* <em>Do not implement {@code ConfigValue}</em>; 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
|
||||
* <em>Do not implement interface {@code ConfigValue}</em>; 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 ConfigValue extends ConfigMergeable {
|
||||
@ -52,7 +52,7 @@ public interface ConfigValue extends ConfigMergeable {
|
||||
* 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
|
||||
* 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>
|
||||
* This method is equivalent to
|
||||
@ -69,7 +69,7 @@ public interface ConfigValue extends ConfigMergeable {
|
||||
* 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
|
||||
* 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>
|
||||
* If the config value has been resolved and the options disable all
|
||||
@ -87,8 +87,8 @@ public interface ConfigValue extends ConfigMergeable {
|
||||
ConfigValue withFallback(ConfigMergeable other);
|
||||
|
||||
/**
|
||||
* Places the value inside a {@code Config} at the given path. See also
|
||||
* atKey().
|
||||
* Places the value inside a {@link Config} at the given path. See also
|
||||
* {@link ConfigValue#atKey(String)}.
|
||||
*
|
||||
* @param path
|
||||
* path to store this value at.
|
||||
@ -98,8 +98,8 @@ public interface ConfigValue extends ConfigMergeable {
|
||||
Config atPath(String path);
|
||||
|
||||
/**
|
||||
* Places the value inside a {@code Config} at the given key. See also
|
||||
* atPath().
|
||||
* Places the value inside a {@link Config} at the given key. See also
|
||||
* {@link ConfigValue#atPath(String)}.
|
||||
*
|
||||
* @param key
|
||||
* key to store this value at.
|
||||
|
@ -17,19 +17,24 @@ public final class ConfigValueFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ConfigValue from a plain Java boxed value, which may be a
|
||||
* Boolean, Number, String, Map, Iterable, or null. A Map must be a Map from
|
||||
* String to more values that can be supplied to fromAnyRef(). An Iterable
|
||||
* must iterate over more values that can be supplied to fromAnyRef(). A Map
|
||||
* will become a ConfigObject and an Iterable will become a ConfigList. If
|
||||
* the Iterable is not an ordered collection, results could be strange,
|
||||
* since ConfigList is ordered.
|
||||
* Creates a {@link ConfigValue} from a plain Java boxed value, which may be
|
||||
* a <code>Boolean</code>, <code>Number</code>, <code>String</code>,
|
||||
* <code>Map</code>, <code>Iterable</code>, or <code>null</code>. A
|
||||
* <code>Map</code> must be a <code>Map</code> from String to more values
|
||||
* that can be supplied to <code>fromAnyRef()</code>. An
|
||||
* <code>Iterable</code> must iterate over more values that can be supplied
|
||||
* 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>
|
||||
* In a Map passed to fromAnyRef(), the map's keys are plain keys, not path
|
||||
* expressions. So if your Map has a key "foo.bar" then you will get one
|
||||
* object with a key called "foo.bar", rather than an object with a key
|
||||
* "foo" containing another object with a key "bar".
|
||||
* In a <code>Map</code> passed to <code>fromAnyRef()</code>, the map's keys
|
||||
* are plain keys, not path expressions. So if your <code>Map</code> has a
|
||||
* key "foo.bar" then you will get one object with a key called "foo.bar",
|
||||
* rather than an object with a key "foo" containing another object with a
|
||||
* key "bar".
|
||||
*
|
||||
* <p>
|
||||
* The originDescription will be used to set the origin() field on the
|
||||
@ -62,18 +67,19 @@ public final class ConfigValueFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||
* wrapper that only works on {@link java.util.Map} and returns
|
||||
* {@link ConfigObject} rather than {@link ConfigValue}.
|
||||
* See the {@link #fromAnyRef(Object,String)} documentation for details.
|
||||
* This is a typesafe wrapper that only works on {@link java.util.Map} and
|
||||
* returns {@link ConfigObject} rather than {@link ConfigValue}.
|
||||
*
|
||||
* <p>
|
||||
* If your Map has a key "foo.bar" then you will get one object with a key
|
||||
* called "foo.bar", rather than an object with a key "foo" containing
|
||||
* another object with a key "bar". The keys in the map are keys; not path
|
||||
* expressions. That is, the Map corresponds exactly to a single
|
||||
* {@code ConfigObject}. The keys will not be parsed or modified, and the
|
||||
* values are wrapped in ConfigValue. To get nested {@code ConfigObject},
|
||||
* some of the values in the map would have to be more maps.
|
||||
* If your <code>Map</code> has a key "foo.bar" then you will get one object
|
||||
* with a key called "foo.bar", rather than an object with a key "foo"
|
||||
* containing another object with a key "bar". The keys in the map are keys;
|
||||
* not path expressions. That is, the <code>Map</code> corresponds exactly
|
||||
* to a single {@code ConfigObject}. The keys will not be parsed or
|
||||
* modified, and the values are wrapped in ConfigValue. To get nested
|
||||
* {@code ConfigObject}, some of the values in the map would have to be more
|
||||
* maps.
|
||||
*
|
||||
* <p>
|
||||
* See also {@link ConfigFactory#parseMap(Map,String)} which interprets the
|
||||
@ -89,9 +95,9 @@ public final class ConfigValueFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||
* wrapper that only works on {@link java.lang.Iterable} and returns
|
||||
* {@link ConfigList} rather than {@link ConfigValue}.
|
||||
* See the {@link #fromAnyRef(Object,String)} documentation for details.
|
||||
* This is a typesafe wrapper that only works on {@link java.lang.Iterable}
|
||||
* and returns {@link ConfigList} rather than {@link ConfigValue}.
|
||||
*
|
||||
* @param values
|
||||
* @param originDescription
|
||||
|
@ -218,7 +218,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements Confi
|
||||
public abstract AbstractConfigValue get(Object key);
|
||||
|
||||
@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) {
|
||||
return new UnsupportedOperationException("ConfigObject is immutable, you can't call Map."
|
||||
|
@ -279,7 +279,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
||||
@Override
|
||||
public final String toString() {
|
||||
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() + ")";
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
String renderedKey;
|
||||
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();
|
||||
sb.append(u.toString());
|
||||
}
|
||||
@ -334,7 +334,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
||||
@Override
|
||||
public final String render(ConfigRenderOptions options) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
render(sb, 0, null, options);
|
||||
render(sb, 0, true, null, options);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
@ -232,9 +232,9 @@ final class ConfigConcatenation extends AbstractConfigValue implements Unmergeab
|
||||
}
|
||||
|
||||
@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) {
|
||||
p.render(sb, indent, options);
|
||||
p.render(sb, indent, atRoot, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,12 +216,12 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, String atKey, ConfigRenderOptions options) {
|
||||
render(stack, sb, indent, atKey, options);
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||
render(stack, sb, indent, atRoot, atKey, options);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
boolean commentMerge = options.getComments();
|
||||
if (commentMerge) {
|
||||
@ -268,7 +268,7 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
else
|
||||
sb.append(":");
|
||||
}
|
||||
v.render(sb, indent, options);
|
||||
v.render(sb, indent, atRoot, options);
|
||||
sb.append(",");
|
||||
if (options.getFormatted())
|
||||
sb.append('\n');
|
||||
|
@ -179,13 +179,13 @@ final class ConfigDelayedMergeObject extends AbstractConfigObject implements Unm
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, String atKey, ConfigRenderOptions options) {
|
||||
ConfigDelayedMerge.render(stack, sb, indent, atKey, options);
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) {
|
||||
ConfigDelayedMerge.render(stack, sb, indent, atRoot, atKey, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
||||
render(sb, indent, null, options);
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||
render(sb, indent, atRoot, null, options);
|
||||
}
|
||||
|
||||
private static ConfigException notResolved() {
|
||||
|
@ -42,7 +42,7 @@ final class ConfigNull extends AbstractConfigValue implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||
sb.append("null");
|
||||
}
|
||||
|
||||
|
@ -81,9 +81,13 @@ final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
||||
}
|
||||
|
||||
if (v == null && !expr.optional()) {
|
||||
if (context.options().getAllowUnresolved())
|
||||
return this;
|
||||
else
|
||||
throw new ConfigException.UnresolvedSubstitution(origin(), expr.toString());
|
||||
}
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
} finally {
|
||||
context.source().unreplace(this);
|
||||
}
|
||||
@ -127,7 +131,7 @@ final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ final class ConfigString extends AbstractConfigValue implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||
String rendered;
|
||||
if (options.getJson())
|
||||
rendered = ConfigImplUtil.renderJsonString(value);
|
||||
|
@ -683,7 +683,9 @@ final class Parser {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,14 @@ final class Path {
|
||||
|
||||
// append all the paths in the list together into one path
|
||||
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");
|
||||
|
||||
Iterator<Path> i = pathsToConcat.iterator();
|
||||
Path firstPath = i.next();
|
||||
this.first = firstPath.first;
|
||||
|
||||
|
@ -121,14 +121,16 @@ final class ResolveContext {
|
||||
memos.put(fullKey, resolved);
|
||||
} else {
|
||||
// if we have an unresolved object then either we did a
|
||||
// partial resolve restricted to a certain child, or it's
|
||||
// a bug.
|
||||
// partial resolve restricted to a certain child, or we are
|
||||
// allowing incomplete resolution, or it's a bug.
|
||||
if (isRestrictedToChild()) {
|
||||
if (restrictedKey == null) {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"restrictedKey should not be null here");
|
||||
}
|
||||
memos.put(restrictedKey, resolved);
|
||||
} else if (options().getAllowUnresolved()) {
|
||||
memos.put(fullKey, resolved);
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"resolveSubstitutions() did not give us a resolved object");
|
||||
|
@ -57,7 +57,17 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
||||
|
||||
@Override
|
||||
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)
|
||||
return this;
|
||||
@ -65,7 +75,6 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
||||
return new SimpleConfig((AbstractConfigObject) resolved);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasPath(String pathExpression) {
|
||||
Path path = Path.newPath(pathExpression);
|
||||
@ -247,9 +256,9 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDuration(String path, TimeUnit unit) {
|
||||
public long getDuration(String path, TimeUnit unit) {
|
||||
ConfigValue v = find(path, ConfigValueType.STRING);
|
||||
Long result = unit.convert(
|
||||
long result = unit.convert(
|
||||
parseDuration((String) v.unwrapped(), v.origin(), path),
|
||||
TimeUnit.NANOSECONDS);
|
||||
return result;
|
||||
@ -815,6 +824,11 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResolved() {
|
||||
return root().resolveStatus() == ResolveStatus.RESOLVED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkValid(Config reference, String... restrictToPaths) {
|
||||
SimpleConfig ref = (SimpleConfig) reference;
|
||||
|
@ -167,7 +167,7 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList,
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||
if (value.isEmpty()) {
|
||||
sb.append("[]");
|
||||
} else {
|
||||
@ -191,7 +191,7 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList,
|
||||
}
|
||||
indent(sb, indent + 1, options);
|
||||
|
||||
v.render(sb, indent + 1, options);
|
||||
v.render(sb, indent + 1, atRoot, options);
|
||||
sb.append(",");
|
||||
if (options.getFormatted())
|
||||
sb.append('\n');
|
||||
|
@ -364,17 +364,22 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, ConfigRenderOptions options) {
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||
if (isEmpty()) {
|
||||
sb.append("{}");
|
||||
} else {
|
||||
boolean outerBraces = indent > 0 || options.getJson();
|
||||
boolean outerBraces = options.getJson() || !atRoot;
|
||||
|
||||
if (outerBraces)
|
||||
int innerIndent;
|
||||
if (outerBraces) {
|
||||
innerIndent = indent + 1;
|
||||
sb.append("{");
|
||||
|
||||
if (options.getFormatted())
|
||||
sb.append('\n');
|
||||
} else {
|
||||
innerIndent = indent;
|
||||
}
|
||||
|
||||
int separatorCount = 0;
|
||||
for (String k : keySet()) {
|
||||
@ -382,14 +387,14 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
||||
v = value.get(k);
|
||||
|
||||
if (options.getOriginComments()) {
|
||||
indent(sb, indent + 1, options);
|
||||
indent(sb, innerIndent, options);
|
||||
sb.append("# ");
|
||||
sb.append(v.origin().description());
|
||||
sb.append("\n");
|
||||
}
|
||||
if (options.getComments()) {
|
||||
for (String comment : v.origin().comments()) {
|
||||
indent(sb, indent + 1, options);
|
||||
indent(sb, innerIndent, options);
|
||||
sb.append("#");
|
||||
if (!comment.startsWith(" "))
|
||||
sb.append(' ');
|
||||
@ -397,8 +402,8 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
indent(sb, indent + 1, options);
|
||||
v.render(sb, indent + 1, k, options);
|
||||
indent(sb, innerIndent, options);
|
||||
v.render(sb, innerIndent, false /* atRoot */, k, options);
|
||||
|
||||
if (options.getFormatted()) {
|
||||
if (options.getJson()) {
|
||||
@ -415,15 +420,19 @@ final class SimpleConfigObject extends AbstractConfigObject implements Serializa
|
||||
}
|
||||
// chop last commas/newlines
|
||||
sb.setLength(sb.length() - separatorCount);
|
||||
|
||||
if (outerBraces) {
|
||||
if (options.getFormatted()) {
|
||||
sb.append("\n"); // put a newline back
|
||||
sb.append('\n'); // put a newline back
|
||||
if (outerBraces)
|
||||
indent(sb, indent, options);
|
||||
}
|
||||
|
||||
if (outerBraces)
|
||||
sb.append("}");
|
||||
}
|
||||
}
|
||||
if (atRoot && options.getFormatted())
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue get(Object key) {
|
||||
|
@ -15,7 +15,9 @@ for more information.
|
||||
|
||||
<p>
|
||||
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>
|
||||
@ -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()}
|
||||
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.
|
||||
<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>
|
||||
@ -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()}
|
||||
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()}.
|
||||
<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>
|
||||
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>
|
||||
|
||||
</body>
|
||||
|
Loading…
x
Reference in New Issue
Block a user