From 5d89e1af1d88c50d423c7e2746ea1cc06530bc68 Mon Sep 17 00:00:00 2001 From: LordJust Date: Sun, 6 Jul 2025 17:01:59 +0200 Subject: [PATCH 1/2] fix: restrict contructor parameter to a type from `primitives` The `matches` method of `PrimitivesToIdentifierConverter` returned false positives when an `Indetifier` exposes a constructor with the same parameter type as the primitive type. Now the defined constructors are filtered against the defined `primitives` in the same fashion static creator methods are filtered. --- .../org/jmolecules/spring/PrimitivesToIdentifierConverter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java b/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java index 30a93091..377540f1 100644 --- a/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java +++ b/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java @@ -168,6 +168,7 @@ private Optional detectConstructor(Class type, Class paramet return Arrays.stream(type.getDeclaredConstructors()) .filter(it -> it.getParameterCount() == 1) + .filter(it -> primitives.contains(it.getParameterTypes()[0])) .filter(it -> isAssignableOrConvertable(parameterType, it.getParameterTypes()[0])) .peek(ReflectionUtils::makeAccessible) .findFirst() From 5a84b327e4cac22bf16466100909d72e64e86a37 Mon Sep 17 00:00:00 2001 From: LordJust Date: Sun, 6 Jul 2025 19:06:29 +0200 Subject: [PATCH 2/2] feat: convert interger backed `Identfier` automatically to its primitive adds `int`, `long`, and their boxed counterparts to the list allowed primitives --- .../IdentifierToPrimitivesConverter.java | 2 +- .../PrimitivesToIdentifierConverter.java | 2 +- ...ntifierToPrimitivesConverterUnitTests.java | 41 +++++++++++++++---- ...mitivesToIdentifierConverterUnitTests.java | 38 +++++++++++++---- .../spring/SampleIntegerIdentifier.java | 25 +++++++++++ 5 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 jmolecules-spring/src/test/java/org/jmolecules/spring/SampleIntegerIdentifier.java diff --git a/jmolecules-spring/src/main/java/org/jmolecules/spring/IdentifierToPrimitivesConverter.java b/jmolecules-spring/src/main/java/org/jmolecules/spring/IdentifierToPrimitivesConverter.java index 14a65956..3802ebf3 100644 --- a/jmolecules-spring/src/main/java/org/jmolecules/spring/IdentifierToPrimitivesConverter.java +++ b/jmolecules-spring/src/main/java/org/jmolecules/spring/IdentifierToPrimitivesConverter.java @@ -45,7 +45,7 @@ public class IdentifierToPrimitivesConverter implements ConditionalGenericConverter { private static final Map, Optional> CACHE = new ConcurrentReferenceHashMap<>(); - private static final List> DEFAULT_PRIMITIVES = Arrays.asList(UUID.class, String.class); + private static final List> DEFAULT_PRIMITIVES = Arrays.asList(UUID.class, String.class, Long.class, long.class, Integer.class, int.class); private final Supplier conversionService; private Set> primitives; diff --git a/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java b/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java index 377540f1..2902582b 100644 --- a/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java +++ b/jmolecules-spring/src/main/java/org/jmolecules/spring/PrimitivesToIdentifierConverter.java @@ -52,7 +52,7 @@ public class PrimitivesToIdentifierConverter implements ConditionalGenericConver private static final Map, Optional> CREATORS = new ConcurrentReferenceHashMap<>(); - private static final Set> DEFAULT_PRIMITIVES = new HashSet<>(Arrays.asList(String.class, UUID.class)); + private static final Set> DEFAULT_PRIMITIVES = new HashSet<>(Arrays.asList(String.class, UUID.class, Long.class, long.class, Integer.class, int.class)); private static final Set DEFAULT_FACTORY_METHOD_NAMES = new HashSet<>(Arrays.asList("of")); private final Supplier conversionService; diff --git a/jmolecules-spring/src/test/java/org/jmolecules/spring/IdentifierToPrimitivesConverterUnitTests.java b/jmolecules-spring/src/test/java/org/jmolecules/spring/IdentifierToPrimitivesConverterUnitTests.java index 6f315e03..1b27e934 100644 --- a/jmolecules-spring/src/test/java/org/jmolecules/spring/IdentifierToPrimitivesConverterUnitTests.java +++ b/jmolecules-spring/src/test/java/org/jmolecules/spring/IdentifierToPrimitivesConverterUnitTests.java @@ -15,7 +15,8 @@ */ package org.jmolecules.spring; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.util.UUID; @@ -30,7 +31,9 @@ class IdentifierToPrimitivesConverterUnitTests { static final TypeDescriptor UUID_DESCRIPTOR = TypeDescriptor.valueOf(UUID.class); static final TypeDescriptor STRING_DESCRIPTOR = TypeDescriptor.valueOf(String.class); - static final TypeDescriptor IDENTIFIER_DESCRIPTOR = TypeDescriptor.valueOf(SampleIdentifier.class); + static final TypeDescriptor INT_DESCRIPTOR = TypeDescriptor.valueOf(int.class); + static final TypeDescriptor UUID_IDENTIFIER_DESCRIPTOR = TypeDescriptor.valueOf(SampleIdentifier.class); + static final TypeDescriptor INT_IDENTIFIER_DESCRIPTOR = TypeDescriptor.valueOf(SampleIntegerIdentifier.class); ConversionService conversionService; IdentifierToPrimitivesConverter converter; @@ -46,7 +49,7 @@ void setUp() { } @Test // #16 - void convertsToPrimitiveSources() { + void convertsUuidIdetifierToPrimitiveSources() { UUID uuid = UUID.randomUUID(); SampleIdentifier identifier = SampleIdentifier.of(uuid); @@ -54,21 +57,41 @@ void convertsToPrimitiveSources() { assertThat(conversionService.convert(identifier, UUID.class)).isEqualTo(uuid); assertThat(conversionService.convert(identifier, String.class)).isEqualTo(uuid.toString()); } + + @Test // #16 + void convertsIntegerIdentifierToPrimitiveSources() { + + int id = 1; + SampleIntegerIdentifier identifier = SampleIntegerIdentifier.of(id); + + assertThat(conversionService.convert(identifier, Long.class)).isEqualTo(id); + assertThat(conversionService.convert(identifier, long.class)).isEqualTo(id); + assertThat(conversionService.convert(identifier, Integer.class)).isEqualTo(id); + assertThat(conversionService.convert(identifier, int.class)).isEqualTo(id); + assertThat(conversionService.convert(identifier, String.class)).isEqualTo(Integer.toString(id)); + } @Test // #16 void matchesPrimitives() { - assertThat(converter.matches(IDENTIFIER_DESCRIPTOR, UUID_DESCRIPTOR)).isTrue(); - assertThat(converter.matches(IDENTIFIER_DESCRIPTOR, STRING_DESCRIPTOR)).isTrue(); - assertThat(converter.matches(IDENTIFIER_DESCRIPTOR, TypeDescriptor.valueOf(Long.class))).isFalse(); + assertThat(converter.matches(UUID_IDENTIFIER_DESCRIPTOR, UUID_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(UUID_IDENTIFIER_DESCRIPTOR, STRING_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(UUID_IDENTIFIER_DESCRIPTOR, TypeDescriptor.valueOf(Long.class))).isFalse(); + + assertThat(converter.matches(INT_IDENTIFIER_DESCRIPTOR, TypeDescriptor.valueOf(Long.class))).isTrue(); + assertThat(converter.matches(INT_IDENTIFIER_DESCRIPTOR, TypeDescriptor.valueOf(long.class))).isTrue(); + assertThat(converter.matches(INT_IDENTIFIER_DESCRIPTOR, TypeDescriptor.valueOf(Integer.class))).isTrue(); + assertThat(converter.matches(INT_IDENTIFIER_DESCRIPTOR, INT_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(INT_IDENTIFIER_DESCRIPTOR, STRING_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(INT_IDENTIFIER_DESCRIPTOR, UUID_DESCRIPTOR)).isFalse(); } @Test // #16 void handlesNullValues() { - assertThat(converter.convert(null, IDENTIFIER_DESCRIPTOR, UUID_DESCRIPTOR)).isNull(); + assertThat(converter.convert(null, UUID_IDENTIFIER_DESCRIPTOR, UUID_DESCRIPTOR)).isNull(); assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { - converter.convert(SampleIdentifier.of(null), IDENTIFIER_DESCRIPTOR, UUID_DESCRIPTOR); + converter.convert(SampleIdentifier.of(null), UUID_IDENTIFIER_DESCRIPTOR, UUID_DESCRIPTOR); }); } @@ -94,7 +117,7 @@ void avoidsSelfCreationIfConversionTargetIsSameType() { var identifier = SampleIdentifier.of(UUID.randomUUID()); - assertThat(converter.convert(identifier, IDENTIFIER_DESCRIPTOR, IDENTIFIER_DESCRIPTOR)).isSameAs(identifier); + assertThat(converter.convert(identifier, UUID_IDENTIFIER_DESCRIPTOR, UUID_IDENTIFIER_DESCRIPTOR)).isSameAs(identifier); } static abstract class IdentifierBase implements Identifier { diff --git a/jmolecules-spring/src/test/java/org/jmolecules/spring/PrimitivesToIdentifierConverterUnitTests.java b/jmolecules-spring/src/test/java/org/jmolecules/spring/PrimitivesToIdentifierConverterUnitTests.java index 19c6fd46..10fbd004 100644 --- a/jmolecules-spring/src/test/java/org/jmolecules/spring/PrimitivesToIdentifierConverterUnitTests.java +++ b/jmolecules-spring/src/test/java/org/jmolecules/spring/PrimitivesToIdentifierConverterUnitTests.java @@ -33,8 +33,9 @@ class PrimitivesToIdentifierConverterUnitTests { static final TypeDescriptor UUID_DESCRIPTOR = TypeDescriptor.valueOf(UUID.class); static final TypeDescriptor STRING_DESCRIPTOR = TypeDescriptor.valueOf(String.class); - static final TypeDescriptor IDENTIFIER_DESCRIPTOR = TypeDescriptor.valueOf(SampleIdentifier.class); - static final TypeDescriptor LONG_DESCRIPTOR = TypeDescriptor.valueOf(Long.class); + static final TypeDescriptor INT_DESCRIPTOR = TypeDescriptor.valueOf(int.class); + static final TypeDescriptor UUID_IDENTIFIER_DESCRIPTOR = TypeDescriptor.valueOf(SampleIdentifier.class); + static final TypeDescriptor INT_IDENTIFIER_DESCRIPTOR = TypeDescriptor.valueOf(SampleIntegerIdentifier.class); ConfigurableConversionService conversionService; PrimitivesToIdentifierConverter converter; @@ -48,7 +49,7 @@ void setUp() { } @Test // #16 - void convertsFromPrimitiveSources() { + void convertsToUuidIdentifierFromPrimitiveSources() { UUID uuid = UUID.randomUUID(); @@ -56,20 +57,41 @@ void convertsFromPrimitiveSources() { assertThat(conversionService.convert(uuid.toString(), SampleIdentifier.class).getId()).isEqualTo(uuid); assertThatExceptionOfType(ConverterNotFoundException.class) - .isThrownBy(() -> converter.convert(1L, LONG_DESCRIPTOR, IDENTIFIER_DESCRIPTOR)); + .isThrownBy(() -> converter.convert(1L, INT_DESCRIPTOR, UUID_IDENTIFIER_DESCRIPTOR)); + } + + @Test // #16 + void convertsToIntegerIdentifierFromPrimitiveSources() { + + int id = 1; + + assertThat(conversionService.convert(id, SampleIntegerIdentifier.class).getId()).isEqualTo(id); + assertThat(conversionService.convert(Integer.valueOf(id), SampleIntegerIdentifier.class).getId()).isEqualTo(id); + assertThat(conversionService.convert(Long.valueOf(id), SampleIntegerIdentifier.class).getId()).isEqualTo(id); + assertThat(conversionService.convert(Long.valueOf(id).longValue(), SampleIntegerIdentifier.class).getId()).isEqualTo(id); + + UUID uuid = UUID.randomUUID(); + assertThatExceptionOfType(ConverterNotFoundException.class) + .isThrownBy(() -> converter.convert(uuid, UUID_DESCRIPTOR, INT_IDENTIFIER_DESCRIPTOR)); } @Test // #16 void matchesPrimitives() { - assertThat(converter.matches(UUID_DESCRIPTOR, IDENTIFIER_DESCRIPTOR)).isTrue(); - assertThat(converter.matches(STRING_DESCRIPTOR, IDENTIFIER_DESCRIPTOR)).isTrue(); - assertThat(converter.matches(TypeDescriptor.valueOf(Long.class), IDENTIFIER_DESCRIPTOR)).isFalse(); + assertThat(converter.matches(UUID_DESCRIPTOR, UUID_IDENTIFIER_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(STRING_DESCRIPTOR, UUID_IDENTIFIER_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(INT_DESCRIPTOR, INT_IDENTIFIER_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(TypeDescriptor.valueOf(Integer.class), INT_IDENTIFIER_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(TypeDescriptor.valueOf(long.class), INT_IDENTIFIER_DESCRIPTOR)).isTrue(); + assertThat(converter.matches(TypeDescriptor.valueOf(Long.class), INT_IDENTIFIER_DESCRIPTOR)).isTrue(); + + assertThat(converter.matches(INT_DESCRIPTOR, UUID_IDENTIFIER_DESCRIPTOR)).isFalse(); + assertThat(converter.matches(UUID_DESCRIPTOR, INT_IDENTIFIER_DESCRIPTOR)).isFalse(); } @Test // #16 void handlesNullValues() { - assertThat(converter.convert(null, UUID_DESCRIPTOR, IDENTIFIER_DESCRIPTOR)).isNull(); + assertThat(converter.convert(null, UUID_DESCRIPTOR, UUID_IDENTIFIER_DESCRIPTOR)).isNull(); } @Test // #46 diff --git a/jmolecules-spring/src/test/java/org/jmolecules/spring/SampleIntegerIdentifier.java b/jmolecules-spring/src/test/java/org/jmolecules/spring/SampleIntegerIdentifier.java new file mode 100644 index 00000000..090c2b57 --- /dev/null +++ b/jmolecules-spring/src/test/java/org/jmolecules/spring/SampleIntegerIdentifier.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021-2025 the original author or authors. + * + * 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 + * + * https://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 org.jmolecules.spring; + +import org.jmolecules.ddd.types.Identifier; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class SampleIntegerIdentifier implements Identifier { + int id; +}