io.helidon.config.spi.OverrideSource Maven / Gradle / Ivy
Show all versions of helidon-config Show documentation
/*
* Copyright (c) 2017, 2022 Oracle and/or its affiliates.
*
* 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.spi;
import java.io.IOException;
import java.io.Reader;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import io.helidon.config.Config;
import io.helidon.config.ConfigException;
/**
* Source of config override settings.
*
* A config override setting provides an alternate, or overriding, value for a
* config element based on the element's key. Implementations of this interface
* furnish override settings as {@link OverrideData} objects.
*
* The {@link OverrideData#data} method returns a {@code List} of pairs, each of
* which contains a {@code Predicate} which evaluates the config key and a
* {@code String} which is the overriding value to be used if the predicate is
* {@code true}. The config system applies overrides before it applies
* {@link ConfigFilter filters}, and it applies only the first matching override
* it finds.
*
* The config override mechanism affects existing {@code Config} nodes that come
* from a {@link ConfigSource}. The override mechanism cannot create additional
* {@code Config} nodes, only modify existing ones.
*
* @see OverrideData
*/
public interface OverrideSource extends Source, Supplier {
@Override
default OverrideSource get() {
return this;
}
/**
* Load override data from the underlying source.
*
* @return override data if present, empty otherwise
* @throws ConfigException in case the loading of data failed
*/
Optional load() throws ConfigException;
/**
* Group of config override settings.
*
* {@code OverrideData} supports the {@code *}
* wildcard character which represents one or more regex word characters:
* [a-zA-Z_0-9]. In particular the {@link #create(java.io.Reader)} and
* {@link #createFromWildcards} static factory methods deal with pairs of
* {@code String}s; the first is a possible wildcard expression, and the
* second is the replacement value the config system will use as it loads
* any {@code Config} value node with a key that matches the wildcard
* expression.
*/
final class OverrideData {
/**
* A function to convert wildcards to {@link Predicate}<{@link Config.Key}>.
*/
static final Function> WILDCARDS_TO_PREDICATE =
(s) -> (Predicate) key ->
Pattern.compile(s.replace("*", "\\w+").replace(".", "\\."))
.matcher(key.toString())
.matches();
private List, String>> data = new ArrayList<>();
private OverrideData(List, String>> data) {
this.data = data;
}
/**
* Creates {@code OverrideData} from a {@code List} of
* predicate/replacement pairs. This method does not use
* wildcarding.
*
* @param data the predicate/replacement pairs
* @return {@code OverrideData} containing the specified pairs
*/
public static OverrideData create(List, String>> data) {
return new OverrideData(data);
}
/**
* Creates {@code OverrideData} from a {@code List} of {@code String}
* pairs.
*
* @param wildcards {@code List} of pairs of
* wildcard expressions and corresponding
* replacement values
* @return {@code OverrideData} object containing the
* {@code Predicate}/{@code String} pairs corresponding to the
* wildcard/replacement pairs
*/
public static OverrideData createFromWildcards(List> wildcards) {
List, String>> overrides = wildcards
.stream()
.map((e) -> new AbstractMap.SimpleEntry<>(
OverrideData.WILDCARDS_TO_PREDICATE.apply(e.getKey()), e.getValue()))
.collect(Collectors.toList());
return new OverrideData(overrides);
}
/**
* Creates {@code OverrideData} from a {@link Reader}.
*
* The {@code Reader} should provide lines in Java
* {@link java.util.Properties} file format. In each line the
* {@code String} to the left of the {@code =} sign is either a
* {@link io.helidon.config.Config.Key} or a
* wildcard expressions as described
* above. The {@code String} to the right of the {@code =} sign is the
* replacement value.
*
* @param reader a source
* @return a new instance
* @throws io.helidon.config.ConfigException when an error occurred when reading from the
* reader
*/
public static OverrideData create(Reader reader) {
OrderedProperties properties = new OrderedProperties();
try (Reader autoCloseableReader = reader) {
properties.load(autoCloseableReader);
} catch (IOException e) {
throw new ConfigException("Cannot load data from reader.", e);
}
List, String>> data = properties.orderedMap().entrySet()
.stream()
.map((e) -> new AbstractMap.SimpleEntry<>(WILDCARDS_TO_PREDICATE.apply(e.getKey()), e.getValue()))
.collect(Collectors.toList());
return create(data);
}
/**
* Creates an {@code OverrideData} object containing no overrides.
*
* @return an empty object
*/
public static OverrideData empty() {
return new OverrideData(List.of());
}
/**
* Returns the predicate/replacement value pairs.
*
* @return a list of pairs of predicate and replacement value
*/
public List, String>> data() {
return data;
}
}
}