Skip to content

Commit 234b274

Browse files
committed
Simplified tree support.
1 parent 5b69e07 commit 234b274

File tree

4 files changed

+442
-2
lines changed

4 files changed

+442
-2
lines changed

api/src/main/java/org/eclipse/microprofile/config/Config.java

Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
import java.lang.reflect.Array;
3535
import java.util.Arrays;
3636
import java.util.List;
37+
import java.util.Map;
3738
import java.util.Optional;
39+
import java.util.function.Function;
3840

3941
import org.eclipse.microprofile.config.spi.ConfigSource;
4042
import org.eclipse.microprofile.config.spi.Converter;
@@ -129,7 +131,9 @@ public interface Config {
129131
* if the property cannot be converted to the specified type
130132
* @throws java.util.NoSuchElementException
131133
* if the property is not defined or is defined as an empty string or the converter returns {@code null}
134+
* @deprecated use {@link #get(String)} and {@link #as(Class)} instead
132135
*/
136+
@Deprecated
133137
<T> T getValue(String propertyName, Class<T> propertyType);
134138

135139
/**
@@ -148,7 +152,9 @@ public interface Config {
148152
* @param propertyName
149153
* The configuration property name
150154
* @return the resolved property value as a {@link ConfigValue}
155+
* @deprecated use {@link #get(String)} and methods on the config node
151156
*/
157+
@Deprecated
152158
ConfigValue getConfigValue(String propertyName);
153159

154160
/**
@@ -171,7 +177,9 @@ public interface Config {
171177
* @throws java.util.NoSuchElementException
172178
* if the property isn't present in the configuration or is defined as an empty string or the converter
173179
* returns {@code null}
180+
* @deprecated use {@link #get(String)} and #asList(Class)
174181
*/
182+
@Deprecated
175183
default <T> List<T> getValues(String propertyName, Class<T> propertyType) {
176184
@SuppressWarnings("unchecked")
177185
Class<T[]> arrayType = (Class<T[]>) Array.newInstance(propertyType, 0).getClass();
@@ -198,7 +206,9 @@ default <T> List<T> getValues(String propertyName, Class<T> propertyType) {
198206
*
199207
* @throws IllegalArgumentException
200208
* if the property cannot be converted to the specified type
209+
* @deprecated use {@link #get(String)} and {@link #as(Class)} instead
201210
*/
211+
@Deprecated
202212
<T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType);
203213

204214
/**
@@ -219,7 +229,9 @@ default <T> List<T> getValues(String propertyName, Class<T> propertyType) {
219229
*
220230
* @throws java.lang.IllegalArgumentException
221231
* if the property cannot be converted to the specified type
232+
* @deprecated use {@link #get(String)} and #asList(Class)
222233
*/
234+
@Deprecated
223235
default <T> Optional<List<T>> getOptionalValues(String propertyName, Class<T> propertyType) {
224236
@SuppressWarnings("unchecked")
225237
Class<T[]> arrayType = (Class<T[]>) Array.newInstance(propertyType, 0).getClass();
@@ -257,7 +269,7 @@ default <T> Optional<List<T>> getOptionalValues(String propertyName, Class<T> pr
257269
* The returned sources will be sorted by descending ordinal value and name, which can be iterated in a thread-safe
258270
* manner. The {@link java.lang.Iterable Iterable} contains a fixed number of {@linkplain ConfigSource configuration
259271
* sources}, determined at application start time, and the config sources themselves may be static or dynamic.
260-
*
272+
*
261273
* @return the configuration sources
262274
*/
263275
Iterable<ConfigSource> getConfigSources();
@@ -291,4 +303,181 @@ default <T> Optional<List<T>> getOptionalValues(String propertyName, Class<T> pr
291303
* If the current provider does not support unwrapping to the given type
292304
*/
293305
<T> T unwrap(Class<T> type);
306+
307+
/*
308+
* Tree handling methods
309+
*/
310+
311+
/**
312+
* Fully qualified key of this config node (such as {@code server.port}).
313+
* Returns an empty String for root config.
314+
*
315+
* @return key of this config
316+
*/
317+
String key();
318+
319+
/**
320+
* Name of this node - the last element of a fully qualified key.
321+
* <p>
322+
* For example for key {@code server.port} this method would return {@code port}.
323+
*
324+
* @return name of this node
325+
*/
326+
String name();
327+
328+
/**
329+
* Single sub-node for the specified name.
330+
* For example if requested for key {@code server}, this method would return a config
331+
* representing the {@code server} node, which would have for example a child {@code port}.
332+
*
333+
* @param name name of the nested node to retrieve
334+
* @return sub node, never null
335+
*/
336+
Config get(String name);
337+
338+
/**
339+
* A detached node removes prefixes of each sub-node of the current node.
340+
* <p>
341+
* Let's assume this node is {@code server} and contains {@code host} and {@code port}.
342+
* The method {@link #key()} for {@code host} would return {@code server.host}.
343+
* If we call a method {@link #key()} on a detached instance, it would return just {@code host}.
344+
*
345+
* @return a detached config instance
346+
*/
347+
Config detach();
348+
349+
/**
350+
* Type of this node.
351+
*
352+
* @return type
353+
*/
354+
Type type();
355+
356+
/**
357+
* Returns {@code true} if the node exists, whether an object, a list, or a
358+
* value node.
359+
*
360+
* @return {@code true} if the node exists
361+
*/
362+
default boolean exists() {
363+
return type() != Type.MISSING;
364+
}
365+
366+
/**
367+
* Returns {@code true} if this configuration node has a direct value.
368+
* <p>
369+
* Example (using properties files) for each node type:
370+
* <p>
371+
* {@link Type#OBJECT} - the node {@code server.tls} is an object node with direct value:
372+
* <pre>
373+
* # this is not recommended, yet it is possible:
374+
* server.tls=true
375+
* server.tls.version=1.2
376+
* server.tls.keystore=abc.p12
377+
* </pre>
378+
* <p>
379+
* {@link Type#LIST} - the node {@code server.ports} is a list node with direct value:
380+
* TODO this may actually not be supported by the spec, as it can only be achieved through properties
381+
* <pre>
382+
* # this is not recommended, yet it is possible:
383+
* server.ports=8080
384+
* server.ports.0=8081
385+
* server.ports.1=8082
386+
* </pre>
387+
* <p>
388+
* {@link Type#VALUE} - the nodes {@code server.port} and {@code server.host} are values
389+
* <pre>
390+
* server.port=8080
391+
* server.host=localhost
392+
* </pre>
393+
*
394+
* @return {@code true} if the node has direct value, {@code false} otherwise.
395+
*/
396+
boolean hasValue();
397+
398+
/**
399+
* Typed value created using a converter function.
400+
* The converter is called only if this config node exists.
401+
*
402+
* @param converter to create an instance from config node
403+
* @param <T> type of the object
404+
* @return converted value of this node, or an empty optional if this node does not exist
405+
* @throws java.lang.IllegalArgumentException if this config node cannot be converted to the desired type
406+
*/
407+
<T> Optional<T> as(Function<Config, T> converter);
408+
409+
/**
410+
* Typed value created using a configured converter.
411+
*
412+
* @param type class to convert to
413+
* @param <T> type of the object
414+
* @return converted value of this node, or empty optional if this node does not exist
415+
* @throws java.lang.IllegalArgumentException if this config node cannot be converted to the desired type
416+
*/
417+
<T> Optional<T> as(Class<T> type);
418+
419+
/**
420+
* Map to a list of typed values.
421+
* This method is only available if the current node is a {@link org.eclipse.microprofile.config.Config.Type#LIST}
422+
* or if it has a direct value. In case a direct value of type String exists, it expects comma separated elements.
423+
*
424+
* @param type class to convert each element to
425+
* @param <T> type of the object
426+
* @return list of typed values
427+
* @throws java.lang.IllegalArgumentException if this config node cannot be converted to the desired type
428+
*/
429+
<T> Optional<List<T>> asList(Class<T> type);
430+
431+
/**
432+
* Contains the (known) config values as a map of key->value pairs.
433+
*
434+
* @return map of sub keys of this config node, or empty if this node does not exist
435+
*/
436+
Optional<Map<String, String>> asMap();
437+
438+
/**
439+
* A list of child nodes.
440+
* In case this node is {@link org.eclipse.microprofile.config.Config.Type#LIST} returns the list in the correct order.
441+
* In case this node is {@link org.eclipse.microprofile.config.Config.Type#OBJECT} returns all direct child nodes
442+
* (in an unknown order)
443+
*
444+
* @return list of child nodes, or empty if this node does not exist
445+
*/
446+
Optional<List<Config>> asNodeList();
447+
448+
/*
449+
* Shortcut helper methods
450+
*/
451+
default Optional<String> asString() {
452+
return as(String.class);
453+
}
454+
455+
default Optional<Integer> asInt() {
456+
return as(Integer.class);
457+
}
458+
459+
/**
460+
* Config node type.
461+
*/
462+
enum Type {
463+
/**
464+
* Object node with named members and a possible direct value.
465+
*/
466+
OBJECT,
467+
/**
468+
* List node with a list of indexed parameters.
469+
* Note that a list node can also be accessed as an object node - child elements
470+
* have indexed keys starting from {@code 0}.
471+
* List nodes may also have a direct value.
472+
*/
473+
LIST,
474+
/**
475+
* Value node is a leaf node - it does not have any child nodes, only direct value.
476+
*/
477+
VALUE,
478+
/**
479+
* Node is missing, it will return only empty values.
480+
*/
481+
MISSING
482+
}
294483
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (c) 2017, 2021 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.eclipse.microprofile.config.spi;
18+
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.Optional;
22+
23+
/**
24+
* Marker interface identifying a config node implementation.
25+
*/
26+
public interface ConfigNode {
27+
/**
28+
* Key of this config node.
29+
*
30+
* @return key of this node
31+
*/
32+
String key();
33+
34+
/**
35+
* Get the type of this node.
36+
*
37+
* @return NodeType this node represents
38+
*/
39+
NodeType nodeType();
40+
41+
/**
42+
* Get the direct value of this config node. Any node type can have a direct value.
43+
*
44+
* @return a value if present, {@code empty} otherwise
45+
*/
46+
Optional<String> value();
47+
48+
/**
49+
* Each node may have a direct value, and in addition may be an object node or a list node.
50+
* This method returns true for any node with direct value.
51+
*
52+
* @return true if this node contains a value
53+
*/
54+
default boolean hasValue() {
55+
return value().isPresent();
56+
}
57+
58+
/**
59+
* Config source that provided this value.
60+
*
61+
* @return config source
62+
*/
63+
ConfigSource configSource();
64+
65+
/**
66+
* The actual priority of the config source that provided this value.
67+
*
68+
* @return config source priority
69+
* @see #configSource()
70+
*/
71+
Integer sourcePriority();
72+
73+
/**
74+
* Base types of config nodes.
75+
*/
76+
enum NodeType {
77+
/**
78+
* An object (complex structure), optionally may have a value.
79+
*/
80+
OBJECT,
81+
/**
82+
* A list of values, optionally may have a value.
83+
*/
84+
LIST,
85+
/**
86+
* Only has value.
87+
*/
88+
VALUE
89+
}
90+
91+
/**
92+
* Single string-based configuration value.
93+
*/
94+
interface ValueNode extends ConfigNode {
95+
@Override
96+
default NodeType nodeType() {
97+
return NodeType.VALUE;
98+
}
99+
100+
/**
101+
* Get the value of this value node.
102+
* @return string with the node value
103+
*/
104+
String get();
105+
}
106+
107+
/**
108+
* ConfigNode-based list of configuration values.
109+
* <p>
110+
* List may contains instances of
111+
* {@link ValueNode}, {@link ListNode} as well as {@link ObjectNode}.
112+
*/
113+
interface ListNode extends ConfigNode, List<ConfigNode> {
114+
@Override
115+
default NodeType nodeType() {
116+
return NodeType.LIST;
117+
}
118+
}
119+
120+
/**
121+
* Configuration node representing a hierarchical structure.
122+
* <p>
123+
* In the map exposed by this interface, the map keys are {@code String}s
124+
* containing the fully-qualified dotted names of the config keys and the
125+
* map values are the corresponding {@link ValueNode} or {@link ListNode}
126+
* instances. The map never contains {@link ObjectNode} values because the
127+
* {@link ObjectNode} is implemented as a flat map.
128+
*/
129+
interface ObjectNode extends ConfigNode, Map<String, ConfigNode> {
130+
@Override
131+
default NodeType nodeType() {
132+
return NodeType.OBJECT;
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)