diff --git a/contribs/application/src/test/java/org/matsim/application/ConfigYamlUpdateTest.java b/contribs/application/src/test/java/org/matsim/application/ConfigYamlUpdateTest.java index 7a9394bb760..a7e99904db2 100644 --- a/contribs/application/src/test/java/org/matsim/application/ConfigYamlUpdateTest.java +++ b/contribs/application/src/test/java/org/matsim/application/ConfigYamlUpdateTest.java @@ -51,20 +51,26 @@ void createParamSet() { config, input.resolve("multiLevel.yml") ); + testGroup.addParam("values", "1, 2, 3"); + + assertThat(testGroup.values) + .containsExactly(1, 2, 3); + Collection params = testGroup.getParameterSets("params"); assertThat(params).hasSize(2); Iterator it = params.iterator(); - ConfigGroup next = it.next(); + TestParamSet next = (TestParamSet) it.next(); assertThat(next.getParams().get("mode")).isEqualTo("car"); - assertThat(next.getParams().get("values")).isEqualTo("-1, -2"); + assertThat(next.getParams().get("values")).isEqualTo("-1.0, -2.0"); + assertThat(next.values).containsExactly(-1d, -2d); - next = it.next(); + next = (TestParamSet) it.next(); assertThat(next.getParams().get("mode")).isEqualTo("bike"); - assertThat(next.getParams().get("values")).isEqualTo("3, 4"); + assertThat(next.getParams().get("values")).isEqualTo("3.0, 4.0"); assertThat(next.getParams().get("extra")).isEqualTo("extra"); } @@ -91,10 +97,10 @@ void multiLevel() { ConfigGroup next = it.next(); // These parameters are recognized as lists correctly - assertThat(next.getParams().get("values")).isEqualTo("-1, -2"); + assertThat(next.getParams().get("values")).isEqualTo("-1.0, -2.0"); next = it.next(); - assertThat(next.getParams().get("values")).isEqualTo("3, 4"); + assertThat(next.getParams().get("values")).isEqualTo("3.0, 4.0"); assertThat(next.getParams().get("extra")).isEqualTo("extra"); } @@ -122,7 +128,7 @@ void ambiguous() { public static final class TestConfigGroup extends ReflectiveConfigGroup { @Parameter - private List values; + private List values; public TestConfigGroup() { super("test"); @@ -142,7 +148,7 @@ public ConfigGroup createParameterSet(String type) { public static final class TestParamSet extends ReflectiveConfigGroup { @Parameter - private List values; + private List values; public TestParamSet() { super("params", true); diff --git a/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/multiLevel.yml b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/multiLevel.yml index 63e40d29854..f1d3c105534 100644 --- a/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/multiLevel.yml +++ b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/multiLevel.yml @@ -1,5 +1,6 @@ test: + values: [1,2,3] params: - mode: car subpopulation: person diff --git a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java index 3a00441d761..55da3166090 100644 --- a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java @@ -256,7 +256,10 @@ private static boolean checkType(Type type) { var rawType = pType.getRawType(); if (rawType.equals(List.class) || rawType.equals(Set.class)) { var typeArgument = pType.getActualTypeArguments()[0]; - return typeArgument.equals(String.class) || (typeArgument instanceof Class && ((Class) typeArgument).isEnum()); + return typeArgument.equals(String.class) || + typeArgument.equals(Double.class) || + typeArgument.equals(Integer.class) || + (typeArgument instanceof Class && ((Class) typeArgument).isEnum()); } if (rawType.equals(Class.class)) @@ -412,6 +415,11 @@ private Object fromString(String value, Class type, @Nullable Field paramFiel List> enumConstants = getEnumConstants(paramField); return stream.map(s -> stringToEnumValue(s, enumConstants)).collect(toImmutableSet()); } + if (paramField != null && isCollectionOfDoubleType(paramField)) + return stream.map(Double::parseDouble).collect(toImmutableSet()); + if (paramField != null && isCollectionOfIntegerType(paramField)) + return stream.map(Integer::parseInt).collect(toImmutableSet()); + return stream.collect(toImmutableSet()); } else if (type.equals(List.class)) { if (value.isBlank()) { @@ -422,6 +430,11 @@ private Object fromString(String value, Class type, @Nullable Field paramFiel List> enumConstants = getEnumConstants(paramField); return stream.map(s -> stringToEnumValue(s, enumConstants)).toList(); } + if (paramField != null && isCollectionOfDoubleType(paramField)) + return stream.map(Double::parseDouble).toList(); + if (paramField != null && isCollectionOfIntegerType(paramField)) + return stream.map(Integer::parseInt).toList(); + return stream.toList(); } else if (type.equals(Class.class)) { try { @@ -488,7 +501,9 @@ private String getParamField(Field paramField) { boolean accessible = enforceAccessible(paramField); try { var result = paramField.get(this); - if (result != null && isCollectionOfEnumsWithUniqueStringValues(paramField)) { + if (result != null && (isCollectionOfEnumsWithUniqueStringValues(paramField) || + isCollectionOfDoubleType(paramField) || + isCollectionOfIntegerType(paramField))) { result = ((Collection) result).stream() .map(Object::toString) // map enum values to string .collect(Collectors.toList()); @@ -674,6 +689,30 @@ private static boolean isCollectionOfEnumsWithUniqueStringValues(Field paramFiel return false; } + private static boolean isCollectionOfIntegerType(Field paramField) { + var type = paramField.getGenericType(); + if (type instanceof ParameterizedType pType) { + var rawType = pType.getRawType(); + if (rawType.equals(List.class) || rawType.equals(Set.class)) { + var typeArgument = pType.getActualTypeArguments()[0]; + return typeArgument.equals(Integer.class) || typeArgument.equals(Integer.TYPE); + } + } + return false; + } + + private static boolean isCollectionOfDoubleType(Field paramField) { + var type = paramField.getGenericType(); + if (type instanceof ParameterizedType pType) { + var rawType = pType.getRawType(); + if (rawType.equals(List.class) || rawType.equals(Set.class)) { + var typeArgument = pType.getActualTypeArguments()[0]; + return typeArgument.equals(Double.class) || typeArgument.equals(Double.TYPE); + } + } + return false; + } + private static boolean enumStringsAreUnique(Class enumClass) { T[] enumConstants = enumClass.getEnumConstants(); long uniqueStringValues = Arrays.stream(enumConstants) diff --git a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java index 17b8278da9e..9e0f1e55fb1 100644 --- a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java +++ b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java @@ -37,6 +37,7 @@ import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.population.Person; import org.matsim.core.config.ReflectiveConfigGroup.InconsistentModuleException; import org.matsim.testcases.MatsimTestUtils; @@ -70,6 +71,7 @@ void testDumpAndRead() { dumpedModule.enumSetField = Set.of(MyEnum.VALUE2); dumpedModule.setField = ImmutableSet.of("a", "b", "c"); dumpedModule.listField = List.of("1", "2", "3"); + dumpedModule.ints = List.of(1, 2, 3); assertEqualAfterDumpAndRead(dumpedModule); } @@ -166,6 +168,7 @@ void testComments() { expectedComments.put("enumListField", "list of enum"); expectedComments.put("enumSetField", "set of enum"); expectedComments.put("setField", "set"); + expectedComments.put("ints", "list of ints"); assertThat(new MyModule().getComments()).isEqualTo(expectedComments); } @@ -324,7 +327,7 @@ void testFailUnsupportedType_StringCollections() { void testFailUnsupportedType_NonStringList() { assertThatThrownBy(() -> new ReflectiveConfigGroup("name") { @Parameter("field") - private List stuff; + private List stuff; }).isInstanceOf(InconsistentModuleException.class); } @@ -472,6 +475,10 @@ private static class MyModule extends ReflectiveConfigGroup { @Parameter private Set enumSetField; + @Comment("list of ints") + @Parameter + private List ints; + // Object fields: // Id: string representation is toString private Id idField;