package org.wildfly.swarm.container.config; import java.util.HashMap; import java.util.List; import java.util.Map; 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.ConfigTree; import org.wildfly.swarm.spi.api.config.SimpleKey; /** * A configuration node capable of having a direct value in addition to key/value children. * * @author Bob McWhirter */ public class ConfigNode implements ConfigTree { public ConfigNode() { } ConfigNode(Object value) { this.value = value; } /** * Set the value of an immediate child. * * @param key The simple child key. * @param value The value to set. */ public void child(SimpleKey key, Object value) { if (value instanceof ConfigNode) { this.children.put(key, (ConfigNode) value); } else { this.children.put(key, new ConfigNode(value)); } } /** * Set the value of an immediate child. * * @param key The simple child key. * @param value The value to set. */ public void child(String key, Object value) { child(new SimpleKey(key), value); } /** * Set the value of a descendant. * * <p>Any intermediate leafs will be created as-needed.</p> * * @param key The possibly-complex key to a descendant. * @param value The value to set. */ public void recursiveChild(String key, Object value) { recursiveChild(ConfigKey.parse(key), value); } /** * Set the value of a descendant. * * <p>Any intermediate leafs will be created as-needed.</p> * * @param key The possibly-complex key to a descendant. * @param value The value to set. */ public void recursiveChild(ConfigKey key, Object value) { SimpleKey head = key.head(); if (head == ConfigKey.EMPTY) { value(value); } ConfigKey rest = key.subkey(1); if (rest == ConfigKey.EMPTY) { child(head, value); } else { ConfigNode child = child(head); if (child == null) { child = new ConfigNode(); child(head, child); } child.recursiveChild(rest, value); } } ConfigNode descendant(ConfigKey key) { SimpleKey head = key.head(); if (head == ConfigKey.EMPTY) { return this; } ConfigKey rest = key.subkey(1); ConfigNode child = child(head); if (child == null) { return null; } return child.descendant(rest); } /** * Retrieve the immediate child node. * * @param key The child's key. * @return The node or {@code null} is none present. */ ConfigNode child(SimpleKey key) { ConfigNode child = this.children.get(key); return child; } /** * Retrieve the immediate child node. * * @param key The child's key. * @return The node or {@code null} is none present. */ ConfigNode child(String key) { return child(new SimpleKey(key)); } /** * Retrieve all immediate children keys. * * @return All immediate children keys. */ public Set<SimpleKey> childrenKeys() { return this.children.keySet(); } /** * Retrieve all descendent keys. * * @return A stream of all descendent keys. */ public Stream<ConfigKey> allKeysRecursively() { Stream<ConfigKey> str = Stream.empty(); if (this.value != null) { str = Stream.of(ConfigKey.EMPTY); } str = Stream.concat(str, this.children.entrySet() .stream() .flatMap((kv) -> { ConfigKey key = kv.getKey(); Object value = kv.getValue(); if (value instanceof ConfigNode) { return ((ConfigNode) value).allKeysRecursively() .map(childKey -> key.append(childKey)); } return Stream.empty(); })); return str; } /** * Set the value on this node. * * @param value The value. */ void value(Object value) { if (value instanceof ConfigNode) { throw new RuntimeException("Cannot set config-node as a value of a tree config-node"); } this.value = value; } /** * Retrieve a value. * * @param key The possibly-complex key of the value to retrieve. * @return The value of {@code null} if none. */ public Object valueOf(ConfigKey key) { SimpleKey head = key.head(); if (head == ConfigKey.EMPTY) { if (this.value == null && this.children != null) { return this; } return this.value; } ConfigNode child = child(head); if (child != null) { ConfigKey rest = key.subkey(1); return child.valueOf(rest); } return null; } protected boolean isListLike() { return this.children.keySet().stream() .allMatch(e -> e.toString().matches("^[0-9]*$")); } public Object asObject() { if (this.value != null) { return this.value; } if (isListLike()) { return asList(); } return asMap(); } public List asList() { return this.children.values().stream() .map(e -> e.asObject()) .collect(Collectors.toList()); } public Map asMap() { Map<String,Object> map = new HashMap<>(); this.children.entrySet() .forEach(entry -> { map.put(entry.getKey().toString(), entry.getValue().asObject()); }); return map; } public String toString() { return "[ConfigNode: (" + System.identityHashCode(this.children) + ") children=" + this.children + "; value=" + this.value + "]"; } private Map<SimpleKey, ConfigNode> children = new HashMap<>(); private Object value; }