77
88import org .slf4j .Logger ;
99
10+ import java .lang .reflect .Array ;
1011import java .lang .reflect .Field ;
1112import java .lang .reflect .Method ;
13+ import java .lang .reflect .ParameterizedType ;
14+ import java .lang .reflect .Type ;
15+ import java .util .ArrayList ;
16+ import java .util .HashSet ;
17+ import java .util .List ;
1218import java .util .Optional ;
19+ import java .util .Set ;
1320
1421import static org .slf4j .LoggerFactory .getLogger ;
1522
@@ -68,7 +75,7 @@ public static <V> void applyOverrides(V object, ValueSupplier supplier) {
6875 field .setAccessible (true );
6976 // The override value comes as a String (from env / prop), but the field
7077 // might be an int, boolean, etc. convertValue handles that conversion.
71- Object value = convertValue (field .getType (), override .get ());
78+ Object value = convertValue (field .getType (), field . getGenericType (), override .get ());
7279 if (value != null ) {
7380 // Actually write the override value into the field on the config object
7481 field .set (object , value );
@@ -91,7 +98,7 @@ public static <V> void applyOverrides(V object, ValueSupplier supplier) {
9198 try {
9299 method .setAccessible (true );
93100 // Convert the string value to match the method's parameter type
94- Object value = convertValue (method .getParameterTypes ()[0 ], override .get ());
101+ Object value = convertValue (method .getParameterTypes ()[0 ], method . getGenericParameterTypes ()[ 0 ], override .get ());
95102 if (value != null ) {
96103 // Call the method on the config object, passing the override value
97104 method .invoke (object , value );
@@ -119,7 +126,7 @@ public static <V> void applyOverrides(V object, ValueSupplier supplier) {
119126 if (targetField != null ) {
120127 try {
121128 targetField .setAccessible (true );
122- Object value = convertValue (targetField .getType (), override .get ());
129+ Object value = convertValue (targetField .getType (), targetField . getGenericType (), override .get ());
123130 if (value != null ) {
124131 targetField .set (object , value );
125132 }
@@ -253,9 +260,11 @@ private static void logOverwriteSources(Overwrite overwrite, String prefix, bool
253260 /**
254261 * Converts a string override value to the target field/parameter type.
255262 * Supports all Java primitive types and their boxed equivalents, plus String.
263+ * Also supports arrays, {@link List} and {@link Set} of String, where the input
264+ * value is a comma-separated string.
256265 * Returns null for unsupported types (with a warning logged).
257266 */
258- private static Object convertValue (Class <?> type , Object value ) {
267+ private static Object convertValue (Class <?> type , Type genericType , Object value ) {
259268 if (value == null ) return null ;
260269 // Environment variables and system properties are always strings, so we need to parse
261270 // them into the correct Java type. For example, the string "8080" becomes the int 8080.
@@ -270,6 +279,44 @@ private static Object convertValue(Class<?> type, Object value) {
270279 if (type == short .class || type == Short .class ) return Short .parseShort (stringValue );
271280 if (type == byte .class || type == Byte .class ) return Byte .parseByte (stringValue );
272281
282+ // Arrays, Lists and Sets are provided as comma-separated strings
283+ if (type .isArray ()) {
284+ Class <?> componentType = type .getComponentType ();
285+ String [] parts = stringValue .split ("," , -1 );
286+ Object array = Array .newInstance (componentType , parts .length );
287+ for (int i = 0 ; i < parts .length ; i ++) {
288+ Object element = convertValue (componentType , componentType , parts [i ].trim ());
289+ Array .set (array , i , element );
290+ }
291+ return array ;
292+ }
293+
294+ // Resolve the element type from the generic type parameter (e.g. List<Integer> -> Integer)
295+ Class <?> elementType = String .class ;
296+ if (genericType instanceof ParameterizedType parameterized ) {
297+ Type [] typeArgs = parameterized .getActualTypeArguments ();
298+ if (typeArgs .length == 1 && typeArgs [0 ] instanceof Class <?> cls ) {
299+ elementType = cls ;
300+ }
301+ }
302+
303+ if (type == List .class || type == ArrayList .class ) {
304+ String [] parts = stringValue .split ("," , -1 );
305+ List <Object > list = new ArrayList <>();
306+ for (String part : parts ) {
307+ list .add (convertValue (elementType , elementType , part .trim ()));
308+ }
309+ return list ;
310+ }
311+ if (type == Set .class || type == HashSet .class ) {
312+ String [] parts = stringValue .split ("," , -1 );
313+ Set <Object > set = new HashSet <>();
314+ for (String part : parts ) {
315+ set .add (convertValue (elementType , elementType , part .trim ()));
316+ }
317+ return set ;
318+ }
319+
273320 log .warn ("Unsupported override type: {}" , type .getName ());
274321 return null ;
275322 }
0 commit comments