package org.wildfly.swarm.container.config;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.wildfly.swarm.spi.api.config.ConfigKey;
import org.wildfly.swarm.spi.api.config.SimpleKey;
/**
* A preferred-order configuration resolution strategy.
*
* <p>The strategy uses either an explicitly-passed {@link Properties} object
* or the System properties to augment any {@link ConfigNode} objects that have
* been added. The strategy will reflect any configuration from the {@code ConfigNode}
* sequence back to the given properties. If using the System properties approach,
* this will actively change properties as returned by {@code System.getProperty(...)}
* and similar methods.</p>
*
* <p>The given {@code ConfigNode} objects will be search in-order for the first match.</p>
*
* @author Bob McWhirter
*/
class ConfigResolutionStrategy {
/**
* Construct a strategy based upon Java System properties.
*/
ConfigResolutionStrategy() {
this(PropertiesManipulator.system());
}
/**
* Construct a strategy based upon a given properties object.
*
* @param properties The properties.
*/
ConfigResolutionStrategy(Properties properties) {
this(properties == null ? PropertiesManipulator.system() : PropertiesManipulator.forProperties(properties));
}
private ConfigResolutionStrategy(PropertiesManipulator properties) {
this.propertiesNode = PropertiesConfigNodeFactory.load(properties.getProperties());
this.nodes.add(this.propertiesNode);
this.properties = properties;
}
void withProperties(Properties properties) {
this.propertiesNode = PropertiesConfigNodeFactory.load(properties);
this.nodes.add(this.propertiesNode);
this.properties = PropertiesManipulator.forProperties(properties);
}
void withEnvironment(Map<String, String> environment) {
this.nodes.add(EnvironmentConfigNodeFactory.load(environment));
}
/**
* Add a {@code ConfigNode} to the search list.
*
* @param node The node to add.
*/
void add(ConfigNode node) {
this.nodes.add(node);
}
void defaults(ConfigNode defaults) {
this.defaults = defaults;
}
void withProperty(String name, String value) {
this.propertiesNode.recursiveChild(name, value);
}
/**
* Activate the strategy.
*/
void activate() {
nodes().flatMap(e -> e.allKeysRecursively())
.distinct()
.forEach(key -> {
activate(key);
});
}
Stream<ConfigNode> nodes() {
if (this.defaults == null) {
return this.nodes.stream();
}
return Stream.concat(this.nodes.stream(), Stream.of(this.defaults));
}
/**
* Activate a given key.
*
* @param key The key to activate.
*/
private void activate(ConfigKey key) {
optionalValueOf(key).ifPresent((v) -> {
this.properties.setProperty(key.name(), v.toString());
});
}
private void deactivate(ConfigKey key) {
optionalValueOf(key).ifPresent((v) -> {
this.properties.clearProperty(key.name());
});
}
/**
* Retrieve the configuration value for a key.
*
* @param key The possibly complex key.
* @return The value, otherwise {@code null}.
*/
public Object valueOf(ConfigKey key) {
return optionalValueOf(key).orElse(null);
}
Optional<Object> optionalValueOf(ConfigKey key) {
return nodes()
.map(e -> e.valueOf(key))
.filter(Objects::nonNull)
.findFirst();
}
Stream<ConfigKey> allKeysRecursively() {
return nodes().flatMap(e -> e.allKeysRecursively());
}
Set<SimpleKey> simpleSubkeysOf(ConfigKey prefix) {
return nodes()
.map(e -> e.descendant(prefix))
.filter(Objects::nonNull)
.flatMap(e -> e.childrenKeys().stream())
.collect(Collectors.toSet());
}
boolean hasKeyOrSubkeys(ConfigKey prefix) {
return nodes()
.map(e -> e.descendant(prefix))
.anyMatch(Objects::nonNull);
}
Properties asProperties() {
return this.properties.getProperties();
}
private PropertiesManipulator properties;
private List<ConfigNode> nodes = new ArrayList<>();
private ConfigNode defaults;
private ConfigNode propertiesNode;
}