io.helidon.config.ConfigValue Maven / Gradle / Ivy
Show all versions of helidon-config Show documentation
/*
* Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.config;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* A typed value of a {@link Config} node.
*
* You can use accessor methods on {@link Config} to obtain this value, such as {@link Config#as(Class)}.
* A typed value that has all the methods of {@link Optional} - including the ones added in JDK9 and newer.
* In addition it has methods to access config values as {@link #supplier()}.
*
* @param type of the value
* @see Config#as(Class)
* @see Config#as(Function)
* @see Config#as(io.helidon.common.GenericType)
*/
public interface ConfigValue {
/**
* Returns the fully-qualified key of the originating {@code Config} node.
*
* The fully-qualified key is a sequence of tokens derived from the name of
* each node along the path from the config root to the current node. Tokens
* are separated by {@code .} (the dot character). See {@link #name()} for
* more information on the format of each token.
*
* @return current config node key
* @see #name()
*/
Config.Key key();
/**
* Returns the last token of the fully-qualified key for the originating {@code Config}
* node.
*
* The name of a node is the last token in its fully-qualified key.
*
* The exact format of the name depends on the {@code Type} of the
* containing node:
*
* - from a {@link Config.Type#OBJECT} node the token for a child is the
* name of the object member;
* - from a {@link Config.Type#LIST} node the token for a child is a zero-based
* index of the element, an unsigned base-10 integer value
* with no leading zeros.
*
*
* The ABNF syntax of config key is:
*
{@code
* config-key = *1( key-token *( "." key-token ) )
* key-token = *( unescaped / escaped )
* unescaped = %x00-2D / %x2F-7D / %x7F-10FFFF
* ; %x2E ('.') and %x7E ('~') are excluded from 'unescaped'
* escaped = "~" ( "0" / "1" )
* ; representing '~' and '.', respectively
* }
*
* @return current config node key
* @see #key()
* @see Config.Key#name()
*/
default String name() {
return key().name();
}
/**
* Returns a typed value as {@link Optional}.
* Returns a {@link Optional#empty() empty} for nodes without a value.
* As this class implements all methods of {@link Optional}, this is only a utility method if an actual {@link Optional}
* instance is needed.
*
* @return value as type instance as {@link Optional}, {@link Optional#empty() empty} in case the node does not have
* a direct value
* @throws ConfigMappingException in case the value cannot be converted to the expected type
* @see #get()
*/
Optional asOptional() throws ConfigMappingException;
/**
* Typed value of the represented {@link Config} node.
* Throws {@link MissingValueException} if the node is {@link Config.Type#MISSING} type.
*
* @return direct value of this node converted to the expected type
* @throws MissingValueException in case the node is {@link Config.Type#MISSING}.
* @throws ConfigMappingException in case the value cannot be converted to the expected type
*/
default T get() throws MissingValueException, ConfigMappingException {
return asOptional()
.orElseThrow(() -> MissingValueException.create(key()));
}
/**
* Convert this {@code ConfigValue} to a different type using a mapper function.
*
* @param mapper mapper to map the type of this {@code ConfigValue} to a type of the returned {@code ConfigValue}
* @param type of the returned {@code ConfigValue}
* @return a new value with the new type
*/
ConfigValue as(Function mapper);
/**
* Returns a supplier of a typed value. The value provided from the supplier is the latest value available.
* E.g. in case there is a file config source that is being watched and a value is changed, this supplier
* would return the latest value, whereas {@link #get()} would return the original value.
*
* Note that {@link Supplier#get()} can throw a {@link ConfigMappingException} or {@link MissingValueException} as the
* {@link #get()} method.
*
* @return a supplier of a typed value
*/
Supplier supplier();
/**
* Returns a supplier of a typed value with a default. The value provided from the supplier is the latest value available.
* E.g. in case there is a file config source that is being watched and a value is changed, this supplier
* would return the latest value, whereas {@link #orElse(Object)} would return the original value.
*
* Note that {@link Supplier#get()} can throw a {@link ConfigMappingException} as the
* {@link #orElse(Object)} method.
*
* @param defaultValue a value to be returned if the supplied value represents a {@link Config} node that has no direct
* value
* @return a supplier of a typed value
*/
Supplier supplier(T defaultValue);
/**
* Returns a {@link Supplier} of an {@link Optional Optional<T>} of the configuration node.
*
* Supplier returns a {@link Optional#empty() empty} if the node does not have a direct value.
*
* @return a supplier of the value as an {@link Optional} typed instance, {@link Optional#empty() empty} in case the node
* does not have a direct value
* @see #asOptional()
* @see #supplier()
*/
Supplier> optionalSupplier();
// it is a pity that Optional is not an interface :(
/**
* If the underlying {@code Optional} does not have a value, set it to the
* {@code Optional} produced by the supplying function.
*
* @param supplier the supplying function that produces an {@code Optional}
* @return returns current value using {@link #asOptional()} if present,
* otherwise value produced by the supplying function.
* @throws NullPointerException if the supplying function is {@code null} or
* produces a {@code null} result
*/
default Optional or(Supplier> supplier) {
Objects.requireNonNull(supplier);
Optional optional = asOptional();
if (!optional.isPresent()) {
Optional supplied = supplier.get();
Objects.requireNonNull(supplied);
optional = supplied;
}
return optional;
}
/**
* Return {@code true} if there is a value present, otherwise {@code false}.
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
*
* @return {@code true} if there is a value present, otherwise {@code false}
* @see Optional#isPresent()
*/
default boolean isPresent() {
return asOptional().isPresent();
}
/**
* If a value is present, performs the given action with the value,
* otherwise performs the given empty-based action.
*
* @param action the action to be performed, if a value is present
* @param emptyAction the empty-based action to be performed, if no value is
* present
* @throws NullPointerException if a value is present and the given action
* is {@code null}, or no value is present and the given empty-based
* action is {@code null}.
*/
default void ifPresentOrElse(Consumer action, Runnable emptyAction) {
Optional optional = asOptional();
if (optional.isPresent()) {
action.accept(optional.get());
} else {
emptyAction.run();
}
}
/**
* If a value is present, invoke the specified consumer with the value,
* otherwise do nothing.
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
*
* @param consumer block to be executed if a value is present
* @throws NullPointerException if value is present and {@code consumer} is
* null
* @see Optional#ifPresent(Consumer)
*/
default void ifPresent(Consumer consumer) {
asOptional().ifPresent(consumer);
}
/**
* If a value is present, and the value matches the given predicate,
* return an {@code Optional} describing the value, otherwise return an
* empty {@code Optional}.
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
*
* @param predicate a predicate to apply to the value, if present
* @return an {@code Optional} describing the value of this {@code Optional}
* if a value is present and the value matches the given predicate,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the predicate is null
* @see Optional#filter(Predicate)
*/
default Optional filter(Predicate predicate) {
return asOptional().filter(predicate);
}
/**
* If a value is present, apply the provided mapping function to it,
* and if the result is non-null, return an {@code Optional} describing the
* result. Otherwise return an empty {@code Optional}.
*
* @param The type of the result of the mapping function
* @param mapper a mapping function to apply to the value, if present
* @return an {@code Optional} describing the result of applying a mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null
*
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
* @see Optional#map(Function)
*/
default Optional map(Function mapper) {
return asOptional().map(mapper);
}
/**
* If a value is present, apply the provided {@code Optional}-bearing
* mapping function to it, return that result, otherwise return an empty
* {@code Optional}. This method is similar to {@link #map(Function)},
* but the provided mapper is one whose result is already an {@code Optional},
* and if invoked, {@code flatMap} does not wrap it with an additional
* {@code Optional}.
*
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
*
* @param The type parameter to the {@code Optional} returned by
* @param mapper a mapping function to apply to the value, if present
* the mapping function
* @return the result of applying an {@code Optional}-bearing mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null or returns
* a null result
* @see Optional#flatMap(Function)
*/
default Optional flatMap(Function> mapper) {
return asOptional().flatMap(mapper);
}
/**
* Return the value if present, otherwise return {@code other}.
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
* @see Optional#orElse(Object)
*/
default T orElse(T other) {
return asOptional().orElse(other);
}
/**
* Return the value if present, otherwise invoke {@code other} and return
* the result of that invocation.
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
*
* @param other a {@code Supplier} whose result is returned if no value
* is present
* @return the value if present otherwise the result of {@code other.get()}
* @throws NullPointerException if value is not present and {@code other} is
* null
* @see Optional#orElseGet(Supplier)
*/
default T orElseGet(Supplier other) {
return asOptional().orElseGet(other);
}
/**
* Return the contained value, if present, otherwise throw an exception
* to be created by the provided supplier.
*
*
* Copied from {@link Optional}. You can get real optional from {@link #asOptional()}.
*
* @param Type of the exception to be thrown
* @param exceptionSupplier The supplier which will return the exception to
* be thrown
* @return the present value
* @throws X if there is no value present
* @throws NullPointerException if no value is present and
* {@code exceptionSupplier} is null
* @see Optional#orElseThrow(Supplier)
*/
default T orElseThrow(Supplier exceptionSupplier) throws X {
return asOptional().orElseThrow(exceptionSupplier);
}
/**
* If a value is present, returns a sequential {@link Stream} containing
* only that value, otherwise returns an empty {@code Stream}.
*
* @return the optional value as a {@code Stream}
*/
default Stream stream() {
return asOptional().map(Stream::of).orElseGet(Stream::empty);
}
}