All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.deephacks.tools4j.config.internal.core.runtime.typesafe.Config Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
/**
 *   Copyright (C) 2011-2012 Typesafe Inc. 
 */
package org.deephacks.tools4j.config.internal.core.runtime.typesafe;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * An immutable map from typesafe paths to typesafe values.
 *
 * 

* Contrast with {@link ConfigObject} which is a map from typesafe keys, * rather than paths, to typesafe values. A {@code Config} contains a tree of * {@code ConfigObject}, and {@link Config#root()} returns the tree's root * object. * *

* 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 spec for * Human-Optimized Config Object Notation. In brief, a path is * period-separated so "a.b.c" looks for key c in object b in object a in the * root object. Sometimes double quotes are needed around special characters in * path expressions. * *

* The API for a {@code Config} is in terms of path expressions, while the API * for a {@code ConfigObject} is in terms of keys. Conceptually, {@code Config} * is a one-level map from paths to values, while a * {@code ConfigObject} is a tree of nested maps from keys to values. * *

* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert * between path expressions and individual path elements (keys). * *

* Another difference between {@code Config} and {@code ConfigObject} is that * conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType() * valueType()} of {@link ConfigValueType#NULL NULL} exist in a * {@code ConfigObject}, while a {@code Config} treats null values as if they * were missing. * *

* {@code Config} is an immutable object and thus safe to use from multiple * threads. There's never a need for "defensive copies." * *

* The "getters" on a {@code Config} list work in the same way. They never return * null, nor do they return a {@code ConfigValue} with * {@link ConfigValue#valueType() valueType()} of {@link ConfigValueType#NULL * NULL}. Instead, they throw {@link ConfigException.Missing} if the value is * completely absent or set to null. If the value is set to null, a subtype of * {@code ConfigException.Missing} called {@link ConfigException.Null} will be * thrown. {@link ConfigException.WrongType} will be thrown anytime you ask for * a type and the value has an incompatible type. Reasonable type conversions * are performed for you though. * *

* If you want to iterate over the contents of a {@code Config}, you can get its * {@code ConfigObject} with {@link #root()}, and then iterate over the * {@code ConfigObject} (which implements java.util.Map). Or, you * can use {@link #entrySet()} which recurses the object tree for you and builds * up a Set of list path-value pairs where the value is not null. * *

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). * *

You can find an example app and library on * GitHub. Also be sure to read the package * overview which describes the big picture as shown in those * examples. * *

* Do not implement {@code Config}; it should only be implemented by * the typesafe library. Arbitrary implementations will not work because the * library internals assume a specific concrete implementation. Also, this * interface is likely to grow new methods over time, so third-party * implementations will break. */ public interface Config extends ConfigMergeable { /** * Gets the {@code Config} as a tree of {@link ConfigObject}. This is a * constant-time operation (it is not proportional to the number of values * in the {@code Config}). * * @return the root object in the configuration */ ConfigObject root(); /** * Gets the origin of the {@code Config}, which may be a file, or a file * with a line number, or just a descriptive phrase. * * @return the origin of the {@code Config} for use in error messages */ ConfigOrigin origin(); @Override Config withFallback(ConfigMergeable other); /** * Returns a replacement typesafe with list substitutions (the * ${foo.bar} syntax, see the * spec) resolved. Substitutions are looked up using this * Config as the root object, that is, a substitution * ${foo.bar} will be replaced with the result of * getValue("foo.bar"). * *

* This method uses {@link ConfigResolveOptions#defaults()}, there is * another variant {@link Config#resolve(ConfigResolveOptions)} which lets * you specify non-default options. * *

* A given {@link Config} must be resolved before using it to retrieve * typesafe 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 list fallbacks available may * not resolve, which will be a user-visible oddity. * *

* resolve() should be invoked on root typesafe objects, rather * than on a subtree (a subtree is the result of something like * typesafe.getConfig("foo")). The problem with * resolve() on a subtree is that substitutions are relative to * the root of the typesafe and the subtree will have no way to get values * from the root. For example, if you did * typesafe.getConfig("foo").resolve() on the below typesafe file, * it would not work: * *

     *   common-value = 10
     *   foo {
     *      whatever = ${common-value}
     *   }
     * 
* *

* Many methods on {@link ConfigFactory} such as {@link * ConfigFactory#load()} automatically resolve the loaded * Config on the loaded stack of typesafe files. * *

Resolving an already-resolved typesafe is a harmless * no-op, but again, it is best to resolve an entire stack of * fallbacks (such as list your typesafe files combined) rather * than resolving each one individually. * * @return an immutable object with substitutions resolved * @throws ConfigException.UnresolvedSubstitution * if any substitutions refer to nonexistent paths * @throws ConfigException * some other typesafe exception if there are other problems */ Config resolve(); /** * Like {@link Config#resolve()} but allows you to specify non-default * options. * * @param options * resolve options * @return the resolved Config */ Config resolve(ConfigResolveOptions options); /** * Validates this typesafe against a reference typesafe, throwing an exception * if it is invalid. The purpose of this method is to "fail early" with a * comprehensive list of problems; in general, anything this method can find * would be detected later when trying to use the typesafe, but it's often * more user-friendly to fail right away when loading the typesafe. * *

* Using this method is always optional, since you can "fail late" instead. * *

* You must restrict validation to paths you "own" (those whose meaning are * defined by your code module). If you validate globally, you may trigger * errors about paths that happen to be in the typesafe but have nothing to do * with your module. It's best to allow the modules owning those paths to * validate them. Also, if every module validates only its own stuff, there * isn't as much redundant work being done. * *

* If no paths are specified in checkValid()'s parameter list, * validation is for the entire typesafe. * *

* If you specify paths that are not in the reference typesafe, those paths * are ignored. (There's nothing to validate.) * *

* Here's what validation involves: * *

    *
  • All paths found in the reference typesafe must be present in this * typesafe or an exception will be thrown. *
  • * Some changes in type from the reference typesafe to this typesafe will cause * an exception to be thrown. Not list potential type problems are detected, * in particular it's assumed that strings are compatible with everything * except objects and lists. This is because string types are often "really" * some other type (system properties always start out as strings, or a * string like "5ms" could be used with {@link #getMilliseconds}). Also, * it's allowed to set any type to null or override null with any type. *
  • * Any unresolved substitutions in this typesafe will cause a validation * failure; both the reference typesafe and this typesafe should be resolved * before validation. If the reference typesafe is unresolved, it's a bug in * the caller of this method. *
* *

* If you want to allow a certain setting to have a flexible type (or * otherwise want validation to be looser for some settings), you could * either remove the problematic setting from the reference typesafe provided * to this method, or you could intercept the validation exception and * screen out certain problems. Of course, this will only work if list other * callers of this method are careful to restrict validation to their own * paths, as they should be. * *

* If validation fails, the thrown exception contains a list of list problems * found. See {@link ConfigException.ValidationFailed#problems}. The * exception's getMessage() will have list the problems * concatenated into one huge string, as well. * *

* Again, checkValid() can't guess every domain-specific way a * setting can be invalid, so some problems may arise later when attempting * to use the typesafe. checkValid() is limited to reporting * generic, but common, problems such as missing settings and blatant type * incompatibilities. * * @param reference * a reference configuration * @param restrictToPaths * only validate values underneath these paths that your code * module owns and understands * @throws ConfigException.ValidationFailed * if there are any validation issues * @throws ConfigException.NotResolved * if this typesafe is not resolved * @throws ConfigException.BugOrBroken * if the reference typesafe is unresolved or caller otherwise * misuses the API */ void checkValid(Config reference, String... restrictToPaths); /** * Checks whether a value is present and non-null at the given path. This * differs in two ways from {@code Map.containsKey()} as implemented by * {@link ConfigObject}: it looks for a path expression, not a key; and it * returns false for null values, while {@code containsKey()} returns true * indicating that the object contains a null value for the key. * *

* If a path exists according to {@link #hasPath(String)}, then * {@link #getValue(String)} will never throw an exception. However, the * typed getters, such as {@link #getInt(String)}, will still throw if the * value is not convertible to the requested type. * * @param path * the path expression * @return true if a non-null value is present at the path * @throws ConfigException.BadPath * if the path expression is invalid */ boolean hasPath(String path); /** * Returns true if the {@code Config}'s root object contains no key-value * pairs. * * @return true if the configuration is empty */ boolean isEmpty(); /** * Returns the set of path-value pairs, excluding any null values, found by * recursing {@link #root() the root object}. Note that this is very * different from root().entrySet() which returns the set of * immediate-child keys in the root object and includes null values. * * @return set of paths with non-null values, built up by recursing the * entire tree of {@link ConfigObject} */ Set> entrySet(); /** * * @param path * path expression * @return the boolean value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to boolean */ boolean getBoolean(String path); /** * @param path * path expression * @return the numeric value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to a number */ Number getNumber(String path); /** * @param path * path expression * @return the 32-bit integer value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to an int (for example it is out * of range, or it's a boolean value) */ int getInt(String path); /** * @param path * path expression * @return the 64-bit long value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to a long */ long getLong(String path); /** * @param path * path expression * @return the floating-point value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to a double */ double getDouble(String path); /** * @param path * path expression * @return the string value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to a string */ String getString(String path); /** * @param path * path expression * @return the {@link ConfigObject} value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to an object */ ConfigObject getObject(String path); /** * @param path * path expression * @return the nested {@code Config} value at the requested path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to a Config */ Config getConfig(String path); /** * Gets the value at the path as an unwrapped Java boxed value ( * {@link java.lang.Boolean Boolean}, {@link java.lang.Integer Integer}, and * so on - see {@link ConfigValue#unwrapped()}). * * @param path * path expression * @return the unwrapped value at the requested path * @throws ConfigException.Missing * if value is absent or null */ Object getAnyRef(String path); /** * Gets the value at the given path, unless the value is a * null value or missing, in which case it throws just like * the other getters. Use {@code get()} on the {@link * Config#root()} object (or other object in the tree) if you * want an unprocessed value. * * @param path * path expression * @return the value at the requested path * @throws ConfigException.Missing * if value is absent or null */ ConfigValue getValue(String path); /** * Gets a value as a size in bytes (parses special strings like "128M"). If * the value is already a number, then it's left alone; if it's a string, * it's parsed understanding unit suffixes such as "128K", as documented in * the the * spec. * * @param path * path expression * @return the value at the requested path, in bytes * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to Long or String * @throws ConfigException.BadValue * if value cannot be parsed as a size in bytes */ Long getBytes(String path); /** * Get value as a duration in milliseconds. If the value is already a * number, then it's left alone; if it's a string, it's parsed understanding * units suffixes like "10m" or "5ns" as documented in the the * spec. * * @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)} * * @param path * path expression * @return the duration value at the requested path, in milliseconds * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to Long or String * @throws ConfigException.BadValue * if value cannot be parsed as a number of milliseconds */ @Deprecated Long getMilliseconds(String path); /** * Get value as a duration in nanoseconds. If the value is already a number * it's taken as milliseconds and converted to nanoseconds. If it's a * string, it's parsed understanding unit suffixes, as for * {@link #getDuration(String, TimeUnit)}. * * @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)} * * @param path * path expression * @return the duration value at the requested path, in nanoseconds * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to Long or String * @throws ConfigException.BadValue * if value cannot be parsed as a number of nanoseconds */ @Deprecated Long getNanoseconds(String path); /** * Gets a value as a duration in a specified * {@link java.util.concurrent.TimeUnit TimeUnit}. If the value is already a * number, then it's taken as milliseconds and then converted to the * requested TimeUnit; if it's a string, it's parsed understanding units * suffixes like "10m" or "5ns" as documented in the the * spec. * * @since 1.1 * * @param path * path expression * @param unit * convert the return value to this time unit * @return the duration value at the requested path, in the given TimeUnit * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to Long or String * @throws ConfigException.BadValue * if value cannot be parsed as a number of the given TimeUnit */ Long getDuration(String path, TimeUnit unit); /** * Gets a list value (with any element type) as a {@link ConfigList}, which * implements {@code java.util.List}. Throws if the path is * unset or null. * * @param path * the path to the list value. * @return the {@link ConfigList} at the path * @throws ConfigException.Missing * if value is absent or null * @throws ConfigException.WrongType * if value is not convertible to a ConfigList */ ConfigList getList(String path); List getBooleanList(String path); List getNumberList(String path); List getIntList(String path); List getLongList(String path); List getDoubleList(String path); List getStringList(String path); List getObjectList(String path); List getConfigList(String path); List getAnyRefList(String path); List getBytesList(String path); /** * @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)} */ @Deprecated List getMillisecondsList(String path); /** * @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)} */ @Deprecated List getNanosecondsList(String path); /** * Gets a list, converting each value in the list to a duration, using the * same rules as {@link #getDuration(String, TimeUnit)}. * * @since 1.1 * @param path * a path expression * @param unit * time units of the returned values * @return list of durations, in the requested units */ List getDurationList(String path, TimeUnit unit); /** * Clone the typesafe with only the given path (and its children) retained; * list sibling paths are removed. * * @param path * path to keep * @return a copy of the typesafe minus list paths except the one specified */ Config withOnlyPath(String path); /** * Clone the typesafe with the given path removed. * * @param path * path to remove * @return a copy of the typesafe minus the specified path */ Config withoutPath(String path); /** * Places the typesafe inside another {@code Config} at the given path. * * @param path * path to store this typesafe at. * @return a {@code Config} instance containing this typesafe at the given * path. */ Config atPath(String path); /** * Places the typesafe inside a {@code Config} at the given key. See also * atPath(). * * @param key * key to store this typesafe at. * @return a {@code Config} instance containing this typesafe at the given * key. */ Config atKey(String key); /** * Returns a {@code Config} based on this one, but with the given path set * to the given value. Does not modify this instance (since it's immutable). * If the path already has a value, that value is replaced. To remove a * value, use withoutPath(). * * @param path * path to add * @param value * value at the new path * @return the new instance with the new map entry */ Config withValue(String path, ConfigValue value); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy