diff --git a/bom/pom.xml b/bom/pom.xml index e450863064e..9728df55b3e 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -581,6 +581,11 @@ helidon-common ${helidon.version} + + io.helidon.common + helidon-common-config + ${helidon.version} + 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: + *

+ *

+ * 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> asMap() throws ConfigException; + + /** + * Object represents fully-qualified key of config node. + *

+ * Fully-qualified key is list of key tokens separated by {@code .} (dot character). + * Depending on context the key token is evaluated one by one: + *

+ *

+ * 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
+     * }
+ * + * @see Config#key() + */ + interface Key extends Comparable { + /** + * Escape {@code '~'} to {@code ~0} and {@code '.'} to {@code ~1} in specified name. + * + * @param name name to be escaped + * @return escaped name + */ + static String escapeName(String name) { + if (!name.contains("~") && !name.contains(".")) { + return name; + } + StringBuilder sb = new StringBuilder(); + char[] chars = name.toCharArray(); + for (char ch : chars) { + if (ch == '~') { + sb.append("~0"); + } else if (ch == '.') { + sb.append("~1"); + } else { + sb.append(ch); + } + } + return sb.toString(); + } + + /** + * Unescape {@code ~0} to {@code '~'} and {@code ~1} to {@code '.'} in specified escaped name. + * + * @param escapedName escaped name + * @return unescaped name + */ + static String unescapeName(String escapedName) { + return escapedName.replaceAll("~1", ".") + .replaceAll("~0", "~"); + } + + /** + * Returns instance of Key that represents key of parent config node. + *

+ * If the key represents root config node it throws an exception. + * + * @return key that represents key of parent config node. + * @see #isRoot() + * @throws IllegalStateException in case you attempt to call this method on a root node + * @throws io.helidon.common.config.ConfigException if not defined + */ + Key parent() throws ConfigException; + + /** + * Returns {@code true} in case the key represents root config node, + * otherwise it returns {@code false}. + * + * @return {@code true} in case the key represents root node, otherwise {@code false}. + * @see #parent() + * @throws io.helidon.common.config.ConfigException if not defined + */ + boolean isRoot(); + + /** + * Returns the name of Config node. + *

+ * The name of a node is the last token in fully-qualified key. + * Depending on context the name is evaluated one by one: + *

+ * + * @return name of config node + * @see Config#name() + */ + String name(); + + /** + * Returns formatted fully-qualified key. + * + * @return formatted fully-qualified key. + */ + @Override + String toString(); + + /** + * Create a child key to the current key. + * + * @param key child key (relative to current key) + * @return a new resolved key + * @throws io.helidon.common.config.ConfigException if not defined + */ + Key child(Key key); + + } + +} diff --git a/common/config/src/main/java/io/helidon/common/config/ConfigException.java b/common/config/src/main/java/io/helidon/common/config/ConfigException.java new file mode 100644 index 00000000000..fcdb4deb617 --- /dev/null +++ b/common/config/src/main/java/io/helidon/common/config/ConfigException.java @@ -0,0 +1,45 @@ +/* + * 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.common.config; + +/** + * Exception is thrown by {@link Config} implementations. + */ +public class ConfigException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Constructor with the detailed message. + * + * @param message the message + */ + public ConfigException(String message) { + super(message); + } + + /** + * Constructor with the detailed message. + * + * @param message the message + * @param cause the cause + */ + public ConfigException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/common/config/src/main/java/io/helidon/common/config/ConfigValue.java b/common/config/src/main/java/io/helidon/common/config/ConfigValue.java new file mode 100644 index 00000000000..1faa622723f --- /dev/null +++ b/common/config/src/main/java/io/helidon/common/config/ConfigValue.java @@ -0,0 +1,287 @@ +/* + * 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.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 java.util.Optional} - including the ones added in JDK9 and newer. + * + * @param type of the value + */ +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: + *

+ *

+ * 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 a typed value as {@link java.util.Optional}. + * Returns a {@link java.util.Optional#empty() empty} for nodes without a value. + * As this class implements all methods of {@link java.util.Optional}, this is only a utility method if an actual + * {@link java.util.Optional} + * instance is needed. + * + * @return value as type instance as {@link java.util.Optional}, {@link java.util.Optional#empty() empty} in case the node + * does not have + * a direct value + * @throws io.helidon.common.config.ConfigException in case the value cannot be converted to the expected type + * @see #get() + */ + Optional asOptional() throws ConfigException; + + /** + * Typed value of the represented {@link Config} node. + * Throws {@link io.helidon.common.config.ConfigException} if the node is Type#MISSING type. + * + * @return direct value of this node converted to the expected type + * @throws io.helidon.common.config.ConfigException in case the value cannot be converted to the expected type + */ + T get() throws ConfigException; + + /** + * 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); + + /** + * Return {@code true} if there is a value present, otherwise {@code false}. + *

+ * Copied from {@link java.util.Optional}. You can get real optional from {@link #asOptional()}. + * + * @return {@code true} if there is a value present, otherwise {@code false} + * @see java.util.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 java.util.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 java.util.Optional#ifPresent(java.util.function.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 java.util.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 java.util.Optional#filter(java.util.function.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 java.util.Optional}. You can get real optional from {@link #asOptional()}. + * @see java.util.Optional#map(java.util.function.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(java.util.function.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 java.util.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 java.util.Optional#flatMap(java.util.function.Function) + */ + default Optional flatMap(Function> mapper) { + return asOptional().flatMap(mapper); + } + + /** + * Return the value if present, otherwise return {@code other}. + *

+ * Copied from {@link java.util.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 java.util.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 java.util.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 java.util.Optional#orElseGet(java.util.function.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 java.util.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 java.util.Optional#orElseThrow(java.util.function.Supplier) + */ + default T orElseThrow(Supplier exceptionSupplier) throws X { + return asOptional().orElseThrow(exceptionSupplier); + } + + /** + * If a value is present, returns a sequential {@link java.util.stream.Stream} containing + * only that value, otherwise returns an empty {@code Stream}. + * + * @return the optional value as a {@code Stream} + */ + default Stream stream() { + return asOptional().stream(); + } + +} diff --git a/common/config/src/main/java/io/helidon/common/config/package-info.java b/common/config/src/main/java/io/helidon/common/config/package-info.java new file mode 100644 index 00000000000..5f2a023411e --- /dev/null +++ b/common/config/src/main/java/io/helidon/common/config/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 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. + */ + +/** + * Provides the minimal set of types for immutable, tree-structured configuration access. + * See the helidon-config module for more information for common runtime usage patterns. + */ +package io.helidon.common.config; diff --git a/common/config/src/main/java/module-info.java b/common/config/src/main/java/module-info.java new file mode 100644 index 00000000000..789c2d599f3 --- /dev/null +++ b/common/config/src/main/java/module-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 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. + */ + +/** + * Helidon Common Config Library. + */ +module io.helidon.common.config { + exports io.helidon.common.config; +} diff --git a/common/pom.xml b/common/pom.xml index 34e605d15d0..ff76e20a3ea 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -33,6 +33,7 @@ common reactive + config configurable key-util http diff --git a/config/config/etc/spotbugs/exclude.xml b/config/config/etc/spotbugs/exclude.xml index d664ecf5ef3..5e1451958f9 100644 --- a/config/config/etc/spotbugs/exclude.xml +++ b/config/config/etc/spotbugs/exclude.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + diff --git a/config/config/pom.xml b/config/config/pom.xml index c5aba5841dd..a1ee38149c5 100644 --- a/config/config/pom.xml +++ b/config/config/pom.xml @@ -42,6 +42,10 @@ jakarta.annotation jakarta.annotation-api + + io.helidon.common + helidon-common-config + 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> asMap() throws MissingValueException; // @@ -882,7 +914,7 @@ default void onChange(Consumer onChangeConsumer) { * * @see Config#key() */ - interface Key extends Comparable { + interface Key extends io.helidon.common.config.Config.Key { /** * Returns instance of Key that represents key of parent config node. *

@@ -892,40 +924,8 @@ interface Key extends Comparable { * @see #isRoot() * @throws java.lang.IllegalStateException in case you attempt to call this method on a root node */ - Key parent(); - - /** - * Returns {@code true} in case the key represents root config node, - * otherwise it returns {@code false}. - * - * @return {@code true} in case the key represents root node, otherwise {@code false}. - * @see #parent() - */ - boolean isRoot(); - - /** - * Returns the name of Config node. - *

- * The name of a node is the last token in fully-qualified key. - * Depending on context the name is evaluated one by one: - *

    - *
  • in {@link Type#OBJECT} node the name represents a name of object member;
  • - *
  • in {@link Type#LIST} node the name represents an zero-based index of list element, - * an unsigned base-10 integer value, leading zeros are not allowed.
  • - *
- * - * @return name of config node - * @see Config#name() - */ - String name(); - - /** - * Returns formatted fully-qualified key. - * - * @return formatted fully-qualified key. - */ @Override - String toString(); + Key parent(); /** * Create a child key to the current key. @@ -933,7 +933,8 @@ interface Key extends Comparable { * @param key child key (relative to current key) * @return a new resolved key */ - Key child(Key key); + @Override + Key child(io.helidon.common.config.Config.Key key); /** * Creates new instance of Key for specified {@code key} literal. diff --git a/config/config/src/main/java/io/helidon/config/ConfigException.java b/config/config/src/main/java/io/helidon/config/ConfigException.java index 360a58af7d9..9446f893a04 100644 --- a/config/config/src/main/java/io/helidon/config/ConfigException.java +++ b/config/config/src/main/java/io/helidon/config/ConfigException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021 Oracle and/or its affiliates. + * 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. @@ -19,7 +19,7 @@ /** * Exception is thrown by {@link Config} implementations. */ -public class ConfigException extends RuntimeException { +public class ConfigException extends io.helidon.common.config.ConfigException { private static final long serialVersionUID = 1L; diff --git a/config/config/src/main/java/io/helidon/config/ConfigKeyImpl.java b/config/config/src/main/java/io/helidon/config/ConfigKeyImpl.java index 6a00929344e..f920e4f8883 100644 --- a/config/config/src/main/java/io/helidon/config/ConfigKeyImpl.java +++ b/config/config/src/main/java/io/helidon/config/ConfigKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. + * Copyright (c) 2020, 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. @@ -107,7 +107,7 @@ ConfigKeyImpl child(String key) { * @return new child instance of ConfigKeyImpl. */ @Override - public ConfigKeyImpl child(Config.Key key) { + public ConfigKeyImpl child(io.helidon.common.config.Config.Key key) { final List path; if (key instanceof ConfigKeyImpl) { path = ((ConfigKeyImpl) key).path; @@ -147,12 +147,13 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof io.helidon.common.config.Config.Key)) { return false; } - ConfigKeyImpl key = (ConfigKeyImpl) o; - return Objects.equals(name, key.name) - && Objects.equals(parent, key.parent); + io.helidon.common.config.Config.Key key = (io.helidon.common.config.Config.Key) o; + return Objects.equals(name(), key.name()) + && Objects.equals(isRoot(), key.isRoot()) + && (isRoot() || Objects.equals(parent(), key.parent())); } @Override @@ -161,7 +162,8 @@ public int hashCode() { } @Override - public int compareTo(Config.Key that) { + public int compareTo(io.helidon.common.config.Config.Key that) { return toString().compareTo(that.toString()); } + } diff --git a/config/config/src/main/java/io/helidon/config/ConfigValue.java b/config/config/src/main/java/io/helidon/config/ConfigValue.java index d6bceaf9798..272c5091b6f 100644 --- a/config/config/src/main/java/io/helidon/config/ConfigValue.java +++ b/config/config/src/main/java/io/helidon/config/ConfigValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * 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. @@ -35,7 +35,7 @@ * @see Config#as(Function) * @see Config#as(io.helidon.common.GenericType) */ -public interface ConfigValue { +public interface ConfigValue extends io.helidon.common.config.ConfigValue { /** * Returns the fully-qualified key of the originating {@code Config} node. *

diff --git a/config/config/src/main/java/module-info.java b/config/config/src/main/java/module-info.java index 65f0efe11b3..da42f7919f1 100644 --- a/config/config/src/main/java/module-info.java +++ b/config/config/src/main/java/module-info.java @@ -27,6 +27,7 @@ requires transitive jakarta.annotation; + requires transitive io.helidon.common.config; requires transitive io.helidon.common; requires transitive io.helidon.common.media.type; diff --git a/integrations/cdi/datasource/pom.xml b/integrations/cdi/datasource/pom.xml index 7add1ca9942..e34812db07b 100644 --- a/integrations/cdi/datasource/pom.xml +++ b/integrations/cdi/datasource/pom.xml @@ -47,6 +47,11 @@ jakarta.annotation-api provided + + jakarta.inject + jakarta.inject-api + provided + jakarta.enterprise jakarta.enterprise.cdi-api