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

io.helidon.config.ConfigValue Maven / Gradle / Ivy

There is a newer version: 4.1.1
Show newest version
/*
 * 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); } }