io.helidon.common
helidon-common-http
diff --git a/common/config/pom.xml b/common/config/pom.xml
new file mode 100644
index 00000000000..8657f6c8175
--- /dev/null
+++ b/common/config/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ 4.0.0
+
+ io.helidon.common
+ helidon-common-project
+ 4.0.0-SNAPSHOT
+
+ helidon-common-config
+ Helidon Common Config
+
+
+
+ 11
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
+
+
+
diff --git a/common/config/src/main/java/io/helidon/common/config/Config.java b/common/config/src/main/java/io/helidon/common/config/Config.java
new file mode 100644
index 00000000000..af516d6768e
--- /dev/null
+++ b/common/config/src/main/java/io/helidon/common/config/Config.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2018, 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.common.config;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Immutable tree-structured configuration.
+ *
+ * See {@link ConfigValue}.
+ */
+public interface Config {
+
+ /**
+ * Returns the fully-qualified key of the {@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()
+ */
+ Key key();
+
+ /**
+ * Returns the last token of the fully-qualified key for the {@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 Type#OBJECT node the token for a child is the
+ * name of the object member;
+ * - from a 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 io.helidon.common.config.Config.Key#name()
+ */
+ default String name() {
+ return key().name();
+ }
+
+ /**
+ * Returns the single sub-node for the specified sub-key.
+ *
+ * The format of the key is described on {@link #key()} method.
+ *
+ * @param key sub-key of requested sub-node
+ * @return config node for specified sub-key, never returns {@code null}.
+ * @see #get(io.helidon.common.config.Config.Key)
+ * @throws io.helidon.common.config.ConfigException if not defined
+ */
+ Config get(String key) throws ConfigException;
+
+ /**
+ * Returns the single sub-node for the specified sub-key.
+ *
+ * @param key sub-key of requested sub-node
+ * @return config node for specified sub-key, never returns {@code null}.
+ * @see #get(String)
+ */
+ default Config get(Key key) {
+ return get(key.name());
+ }
+
+ /**
+ * Returns a copy of the {@code Config} node with no parent.
+ *
+ * The returned node acts as a root node for the subtree below it. Its key
+ * is the empty string; {@code ""}. The original config node is unchanged,
+ * and the original and the copy point to the same children.
+ *
+ * Consider the following configuration:
+ *
+ * app:
+ * name: Example 1
+ * page-size: 20
+ * logging:
+ * app.level = INFO
+ * level = WARNING
+ *
+ * The {@code Config} instances {@code name1} and {@code name2} represents same data and
+ * in fact refer to the same object:
+ *
+ * Config name1 = config
+ * .get("app")
+ * .get("name");
+ * Config name2 = config
+ * .get("app")
+ * .detach() //DETACHED node
+ * .get("name");
+ *
+ * assert name1.asString() == "Example 1";
+ * assert name2.asString() == "Example 1"; //DETACHED node
+ *
+ * The only difference is the key each node returns:
+ *
+ * assert name1.key() == "app.name";
+ * assert name2.key() == "name"; //DETACHED node
+ *
+ *
+ * See asMap() for example of config detaching.
+ *
+ * @return returns detached Config instance of same config node
+ * @throws io.helidon.common.config.ConfigException if not defined
+ */
+ Config detach() throws ConfigException;
+
+ /**
+ * Returns {@code true} if the node exists, whether an object, a list, a
+ * value node, etc.
+ *
+ * @return {@code true} if the node exists
+ */
+ boolean exists();
+
+ /**
+ * Returns {@code true} if this node exists and is a leaf node (has no
+ * children).
+ *
+ * A leaf node has no nested configuration subtree and has a single value.
+ *
+ * @return {@code true} if the node is existing leaf node, {@code false}
+ * otherwise.
+ */
+ boolean isLeaf();
+
+ /**
+ * Returns {@code true} if this node exists and is Type#Object.
+ *
+ * @return {@code true} if the node exists and is Type#Object, {@code false}
+ * otherwise.
+ */
+ boolean isObject();
+
+ /**
+ * Returns {@code true} if this node exists and is Type#List.
+ *
+ * @return {@code true} if the node exists and is Type#List, {@code false}
+ * otherwise.
+ */
+ boolean isList();
+
+ /**
+ * Returns {@code true} if this configuration node has a direct value.
+ *
+ * This may be a value node (e.g. a leaf) or object node or a list node
+ * (e.g. a branch with value). The application can invoke methods such as
+ * {@link #as(Class)} on nodes that have value.
+ *
+ * @return {@code true} if the node has direct value, {@code false} otherwise.
+ */
+ boolean hasValue();
+
+ //
+ // accessors
+ //
+
+ /**
+ * Typed value as a {@link ConfigValue}.
+ *
+ * @param type type class
+ * @param type
+ * @return typed value
+ * @see ConfigValue#map(java.util.function.Function)
+ * @see ConfigValue#get()
+ * @see ConfigValue#orElse(Object)
+ */
+ ConfigValue as(Class type);
+
+ // shortcut methods
+
+ /**
+ * Returns list of specified type.
+ *
+ * @param type type class
+ * @param type of list elements
+ * @return a typed list with values
+ * @throws io.helidon.common.config.ConfigException in case of problem to map property value.
+ */
+ ConfigValue> asList(Class type) throws ConfigException;
+
+ /**
+ * Transform all leaf nodes (values) into Map instance.
+ *
+ * @return new Map instance that contains all config leaf node values
+ * @throws io.helidon.common.config.ConfigException in case the node is Type#MISSING.
+ */
+ ConfigValue
+
io.helidon.common
helidon-common
diff --git a/config/config/src/main/java/io/helidon/config/Config.java b/config/config/src/main/java/io/helidon/config/Config.java
index ccb47ce812f..d3b751952a2 100644
--- a/config/config/src/main/java/io/helidon/config/Config.java
+++ b/config/config/src/main/java/io/helidon/config/Config.java
@@ -238,7 +238,7 @@
* config system merges these together so that values from config sources with higher priority have
* precedence over values from config sources with lower priority.
*/
-public interface Config {
+public interface Config extends io.helidon.common.config.Config {
/**
* Generic type of configuration.
*/
@@ -446,6 +446,7 @@ public Config reload() {
* @return current config node key
* @see #name()
*/
+ @Override
Key key();
/**
@@ -478,6 +479,7 @@ public Config reload() {
* @see #key()
* @see Key#name()
*/
+ @Override
default String name() {
return key().name();
}
@@ -491,6 +493,7 @@ default String name() {
* @return config node for specified sub-key, never returns {@code null}.
* @see #get(Key)
*/
+ @Override
default Config get(String key) {
Objects.requireNonNull(key, "Key argument is null.");
@@ -546,6 +549,7 @@ default Config get(String key) {
*
* @return returns detached Config instance of same config node
*/
+ @Override
Config detach();
/**
@@ -561,6 +565,7 @@ default Config get(String key) {
*
* @return {@code true} if the node exists
*/
+ @Override
default boolean exists() {
return type().exists();
}
@@ -574,10 +579,33 @@ default boolean exists() {
* @return {@code true} if the node is existing leaf node, {@code false}
* otherwise.
*/
+ @Override
default boolean isLeaf() {
return type().isLeaf();
}
+ /**
+ * Returns {@code true} if this node exists and is Type#Object.
+ *
+ * @return {@code true} if the node exists and is Type#Object, {@code false}
+ * otherwise.
+ */
+ @Override
+ default boolean isObject() {
+ return (Type.OBJECT == type());
+ }
+
+ /**
+ * Returns {@code true} if this node exists and is Type#List.
+ *
+ * @return {@code true} if the node exists and is Type#List, {@code false}
+ * otherwise.
+ */
+ @Override
+ default boolean isList() {
+ return (Type.LIST == type());
+ }
+
/**
* Returns {@code true} if this configuration node has a direct value.
*
@@ -587,6 +615,7 @@ default boolean isLeaf() {
*
* @return {@code true} if the node has direct value, {@code false} otherwise.
*/
+ @Override
boolean hasValue();
/**
@@ -698,6 +727,7 @@ default Stream traverse() {
* @see ConfigValue#get()
* @see ConfigValue#orElse(Object)
*/
+ @Override
ConfigValue as(Class type);
/**
@@ -766,6 +796,7 @@ default ConfigValue asDouble() {
* @return a typed list with values
* @throws ConfigMappingException in case of problem to map property value.
*/
+ @Override
ConfigValue> asList(Class type) throws ConfigMappingException;
/**
@@ -833,6 +864,7 @@ default ConfigValue asNode() {
* @see #traverse()
* @see #detach()
*/
+ @Override
ConfigValue
+