Skip to content

Commit e73bb5e

Browse files
committed
ConfigMappingInterface implementation:
- Builder class generation - Simple leaf types and primitives - Simple optionals - @WithDefault
1 parent 0572b2e commit e73bb5e

File tree

8 files changed

+410
-86
lines changed

8 files changed

+410
-86
lines changed

implementation/src/main/java/io/smallrye/config/ConfigInstanceBuilder.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.OptionalLong;
88
import java.util.function.Function;
99
import java.util.function.Predicate;
10+
import java.util.function.ToDoubleFunction;
1011
import java.util.function.ToIntFunction;
1112
import java.util.function.ToLongFunction;
1213

@@ -69,7 +70,7 @@ public interface ConfigInstanceBuilder<I> {
6970
<F extends ToIntFunction<? super I> & Serializable> ConfigInstanceBuilder<I> with(F getter, int value);
7071

7172
/**
72-
* Set a property on the configuration object to an integer value.
73+
* Set a property on the configuration object to a long value.
7374
*
7475
* @param getter the property accessor (must not be {@code null})
7576
* @param value the value to set (must not be {@code null})
@@ -88,7 +89,7 @@ public interface ConfigInstanceBuilder<I> {
8889
* @param <F> the accessor type
8990
* @throws IllegalArgumentException if the getter is {@code null}
9091
*/
91-
<F extends ToLongFunction<? super I> & Serializable> ConfigInstanceBuilder<I> with(F getter, double value);
92+
<F extends ToDoubleFunction<? super I> & Serializable> ConfigInstanceBuilder<I> with(F getter, double value);
9293

9394
/**
9495
* Set a property on the configuration object to a boolean value.

implementation/src/main/java/io/smallrye/config/ConfigInstanceBuilderImpl.java

Lines changed: 42 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.function.Function;
1616
import java.util.function.Predicate;
1717
import java.util.function.Supplier;
18+
import java.util.function.ToDoubleFunction;
1819
import java.util.function.ToIntFunction;
1920
import java.util.function.ToLongFunction;
2021

@@ -46,20 +47,19 @@ final class ConfigInstanceBuilderImpl<I> implements ConfigInstanceBuilder<I> {
4647
private static final ClassValue<Supplier<?>> builderFactories = new ClassValue<>() {
4748
protected Supplier<?> computeValue(final Class<?> type) {
4849
assert type.isInterface();
49-
String interfaceName = type.getName();
50+
// TODO - Should we cache this eagerly in io.smallrye.config.ConfigMappingLoader.ConfigMappingImplementation?
5051
MethodHandles.Lookup lookup;
5152
try {
5253
lookup = MethodHandles.privateLookupIn(type, myLookup);
5354
} catch (IllegalAccessException e) {
5455
throw msg.accessDenied(getClass(), type);
5556
}
56-
String implInternalName = interfaceName.replace('.', '/') + "$$SC_BuilderImpl";
5757
Class<?> impl;
5858
try {
59-
impl = lookup.findClass(implInternalName);
59+
ConfigMappingLoader.ensureLoaded(type);
60+
impl = lookup.findClass(ConfigMappingInterface.ConfigMappingBuilder.getBuilderClassName(type));
6061
} catch (ClassNotFoundException e) {
61-
// generate the impl instead
62-
throw new UnsupportedOperationException("Todo");
62+
throw new IllegalStateException(e);
6363
} catch (IllegalAccessException e) {
6464
throw msg.accessDenied(getClass(), type);
6565
}
@@ -74,7 +74,7 @@ protected Supplier<?> computeValue(final Class<?> type) {
7474
// capture the constructor as a Supplier
7575
return () -> {
7676
try {
77-
return (ConfigInstanceBuilderImpl<?>) mh.invokeExact();
77+
return mh.invoke();
7878
} catch (RuntimeException | Error e) {
7979
throw e;
8080
} catch (Throwable e) {
@@ -83,33 +83,33 @@ protected Supplier<?> computeValue(final Class<?> type) {
8383
};
8484
}
8585
};
86+
8687
/**
8788
* Class value which holds the cached config class instance constructors.
8889
*/
8990
private static final ClassValue<Function<Object, ?>> configFactories = new ClassValue<>() {
91+
// TODO - This is to load the mapping class implementation, which we already have, just missing the right constructor in the ConfigMappingLoader, so we can probably remove this one
9092
protected Function<Object, ?> computeValue(final Class<?> type) {
9193
assert type.isInterface();
92-
String interfaceName = type.getName();
94+
// TODO - Should we cache this eagerly in io.smallrye.config.ConfigMappingLoader.ConfigMappingImplementation?
9395
MethodHandles.Lookup lookup;
9496
try {
9597
lookup = MethodHandles.privateLookupIn(type, myLookup);
9698
} catch (IllegalAccessException e) {
9799
throw msg.accessDenied(getClass(), type);
98100
}
99-
String implInternalName = interfaceName.replace('.', '/') + "$$SC_BuilderImpl";
100101
Class<?> impl;
102+
Class<?> builderClass;
101103
try {
102-
impl = lookup.findClass(implInternalName);
104+
impl = ConfigMappingLoader.ensureLoaded(type).implementation();
105+
builderClass = lookup.findClass(ConfigMappingInterface.ConfigMappingBuilder.getBuilderClassName(type));
103106
} catch (ClassNotFoundException e) {
104-
// generate the impl instead
105-
throw new UnsupportedOperationException("Todo");
107+
throw new IllegalStateException(e);
106108
} catch (IllegalAccessException e) {
107109
throw msg.accessDenied(getClass(), type);
108110
}
109111
MethodHandle mh;
110-
Class<?> builderClass = null;
111-
if (true)
112-
throw new UnsupportedOperationException("Not finished yet...");
112+
113113
try {
114114
mh = lookup.findConstructor(impl, MethodType.methodType(void.class, builderClass));
115115
} catch (NoSuchMethodException e) {
@@ -120,7 +120,7 @@ protected Supplier<?> computeValue(final Class<?> type) {
120120
// capture the constructor as a Function
121121
return builder -> {
122122
try {
123-
return type.cast(mh.invokeExact(builder));
123+
return mh.invoke(builder);
124124
} catch (RuntimeException | Error e) {
125125
throw e;
126126
} catch (Throwable e) {
@@ -143,7 +143,7 @@ protected Map<Object, BiConsumer<Object, Object>> computeValue(final Class<?> ty
143143

144144
static <I> ConfigInstanceBuilderImpl<I> forInterface(Class<I> configurationInterface)
145145
throws IllegalArgumentException, SecurityException {
146-
return new ConfigInstanceBuilderImpl<I>(configurationInterface, builderFactories.get(configurationInterface).get());
146+
return new ConfigInstanceBuilderImpl<>(configurationInterface, builderFactories.get(configurationInterface).get());
147147
}
148148

149149
// =====================================
@@ -196,7 +196,7 @@ public <F extends ToLongFunction<? super I> & Serializable> ConfigInstanceBuilde
196196
return this;
197197
}
198198

199-
public <F extends ToLongFunction<? super I> & Serializable> ConfigInstanceBuilder<I> with(final F getter,
199+
public <F extends ToDoubleFunction<? super I> & Serializable> ConfigInstanceBuilder<I> with(final F getter,
200200
final double value) {
201201
Assert.checkNotNullParam("getter", getter);
202202
Class<?> callerClass = sw.getCallerClass();
@@ -293,6 +293,14 @@ public I build() {
293293

294294
// =====================================
295295

296+
public static <T> T convertValue(final String value, final Class<T> type) {
297+
Converter<T> converter = Converters.getConverter(type);
298+
if (converter == null) {
299+
throw new IllegalArgumentException("No converter found for type " + type);
300+
}
301+
return converter.convert(value);
302+
}
303+
296304
private Converter<?> getConverter(final Object getter, final Class<?> callerClass) {
297305
throw new UnsupportedOperationException("Need class info registry");
298306
}
@@ -313,7 +321,7 @@ private BiConsumer<Object, Object> createSetter(Object lambda) {
313321
}
314322
Object replaced;
315323
try {
316-
replaced = writeReplace.invokeExact(lambda);
324+
replaced = writeReplace.invoke(lambda);
317325
} catch (RuntimeException | Error e) {
318326
throw e;
319327
} catch (Throwable e) {
@@ -326,7 +334,6 @@ private BiConsumer<Object, Object> createSetter(Object lambda) {
326334
if (sl.getCapturedArgCount() != 0) {
327335
throw msg.invalidGetter();
328336
}
329-
String implClassName = sl.getImplClass();
330337
// TODO: check implClassName against the supertype hierarchy of the config interface using shared info mapping
331338
String setterName = sl.getImplMethodName();
332339
Class<?> type = parseReturnType(sl.getImplMethodSignature());
@@ -337,7 +344,7 @@ private BiConsumer<Object, Object> createSetterByName(final String setterName, f
337344
Class<?> builderClass = builderObject.getClass();
338345
MethodHandle setter;
339346
try {
340-
setter = lookup.findVirtual(builderClass, setterName, MethodType.methodType(void.class, builderClass, type));
347+
setter = lookup.findVirtual(builderClass, setterName, MethodType.methodType(void.class, type));
341348
} catch (NoSuchMethodException e) {
342349
throw new RuntimeException(e);
343350
} catch (IllegalAccessException e) {
@@ -347,7 +354,7 @@ private BiConsumer<Object, Object> createSetterByName(final String setterName, f
347354
MethodHandle castSetter = setter.asType(MethodType.methodType(void.class, builderClass, Object.class));
348355
return (builder, val) -> {
349356
try {
350-
castSetter.invokeExact(builderObject, builder, val);
357+
castSetter.invoke(builderObject, val);
351358
} catch (RuntimeException | Error e) {
352359
throw e;
353360
} catch (Throwable e) {
@@ -369,46 +376,24 @@ private Class<?> parseReturnType(final String signature) {
369376
}
370377

371378
private Class<?> parseType(String desc, int start, int end) {
372-
switch (desc.charAt(start)) {
373-
case 'L': {
374-
return parseClassName(desc, start + 1, end - 1);
375-
}
376-
case '[': {
377-
return parseType(desc, start + 1, end).arrayType();
378-
}
379-
case 'B': {
380-
return byte.class;
381-
}
382-
case 'C': {
383-
return char.class;
384-
}
385-
case 'D': {
386-
return double.class;
387-
}
388-
case 'F': {
389-
return float.class;
390-
}
391-
case 'I': {
392-
return int.class;
393-
}
394-
case 'J': {
395-
return long.class;
396-
}
397-
case 'S': {
398-
return short.class;
399-
}
400-
case 'Z': {
401-
return boolean.class;
402-
}
403-
default: {
404-
throw msg.invalidGetter();
405-
}
406-
}
379+
return switch (desc.charAt(start)) {
380+
case 'L' -> parseClassName(desc, start + 1, end - 1);
381+
case '[' -> parseType(desc, start + 1, end).arrayType();
382+
case 'B' -> byte.class;
383+
case 'C' -> char.class;
384+
case 'D' -> double.class;
385+
case 'F' -> float.class;
386+
case 'I' -> int.class;
387+
case 'J' -> long.class;
388+
case 'S' -> short.class;
389+
case 'Z' -> boolean.class;
390+
default -> throw msg.invalidGetter();
391+
};
407392
}
408393

409394
private Class<?> parseClassName(final String signature, final int start, final int end) {
410395
try {
411-
return lookup.findClass(signature.substring(start, end));
396+
return lookup.findClass(signature.substring(start, end).replaceAll("/", "."));
412397
} catch (ClassNotFoundException e) {
413398
throw msg.invalidGetter();
414399
} catch (IllegalAccessException e) {

0 commit comments

Comments
 (0)