Skip to content

Commit 80ac8a5

Browse files
authored
90 name collision with same class name in different packages (#92)
* Prepared for specifying non default adapter method name. * Fixes #90
1 parent 12b1fb5 commit 80ac8a5

File tree

24 files changed

+354
-88
lines changed

24 files changed

+354
-88
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.mapstruct.extensions.spring;
2+
3+
import static java.lang.annotation.ElementType.TYPE;
4+
import static java.lang.annotation.RetentionPolicy.SOURCE;
5+
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.Target;
8+
9+
/**
10+
* Overrides the default method name generated in the Adapter class. To be used exclusively on a
11+
* {@link org.springframework.core.convert.converter.Converter} annotated as {@code @Mapper}.
12+
*/
13+
@Target(TYPE)
14+
@Retention(SOURCE)
15+
public @interface AdapterMethodName {
16+
/**
17+
* The method name to be used instead of the default.
18+
*
19+
* @return The method name to be used instead of the default.
20+
*/
21+
String value();
22+
}
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
package org.mapstruct.extensions.spring;
22

3-
import java.lang.annotation.ElementType;
3+
import static java.lang.annotation.ElementType.TYPE;
4+
import static java.lang.annotation.RetentionPolicy.SOURCE;
5+
46
import java.lang.annotation.Retention;
5-
import java.lang.annotation.RetentionPolicy;
67
import java.lang.annotation.Target;
78

89
/**
910
* Allows the specification of a conversion that is available via the {@link
1011
* org.springframework.core.convert.ConversionService ConversionService}, but is <em>not</em>
1112
* declared as a MapStruct mapper within the scope of the {@link SpringMapperConfig}.
1213
*/
13-
@Target(ElementType.TYPE)
14-
@Retention(RetentionPolicy.SOURCE)
14+
@Target(TYPE)
15+
@Retention(SOURCE)
1516
public @interface ExternalConversion {
1617
Class<?> sourceType();
1718

1819
Class<?> targetType();
20+
String adapterMethodName() default "";
1921
}

docs/src/docs/asciidoc/chapter-3-mapper-as-converter.asciidoc

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,12 @@ public interface MapperSpringConfig {
142142

143143
When the `conversionServiceBeanName` property is set, the built-in <<converterScan>> cannot be used in tests as it does not pick up this property.
144144
However, setting the property `generateConverterScan` to `true` will create an alternative inside the project.
145-
Important to note: This version will _not_ create a `ConversionService` with the given bean name, but merely register all Mappers with the bean identified by the given name. This leads to two practical differences:
145+
Important to note: This version will _not_ create a `ConversionService` with the given bean name, but merely register all Mappers with the bean identified by the given name.
146+
This leads to two practical differences:
146147

147148
- Unlike its <<testExtensions>> counterpart, this version is perfectly suited to be used in production code.
148149
- In a test, the developer will still have to provide a `ConfigurableConversionService` themselves, e.g.:
150+
149151
====
150152
[source,java,linenums]
151153
[subs="verbatim,attributes"]
@@ -166,4 +168,44 @@ public class ConversionServiceAdapterIntegrationTest {
166168
private ConversionService conversionService;
167169
}
168170
----
169-
====
171+
====
172+
173+
[[adapterMethodName]]
174+
=== Modifying the name for the generated adapter method
175+
176+
By default, the adapter class will contain method names of the form `map<SourceTypeName>To<targetTypeName>`.
177+
If you wish to change this, you can do so on a per-Mapper basis by applying the annotation `@AdapterMethodName`:
178+
179+
====
180+
[source,java,linenums]
181+
[subs="verbatim,attributes"]
182+
----
183+
@Mapper(config = MapperSpringConfig.class)
184+
@AdapterMethodName("toDto")
185+
public interface WheelMapper extends Converter<Wheel, WheelDto> {
186+
@Override
187+
WheelDto convert(Wheel source);
188+
}
189+
----
190+
====
191+
192+
This changes the generated method name to be the annotation's `value` attribute:
193+
194+
====
195+
[source,java,linenums]
196+
[subs="verbatim,attributes"]
197+
----
198+
@Component
199+
public class ConversionServiceAdapter {
200+
private final ConversionService conversionService;
201+
202+
public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
203+
this.conversionService = conversionService;
204+
}
205+
206+
public WheelDto toDto(final Wheel source) {
207+
return (WheelDto) conversionService.convert(source, TypeDescriptor.valueOf(Wheel.class), TypeDescriptor.valueOf(WheelDto.class));
208+
}
209+
}
210+
----
211+
====

docs/src/docs/asciidoc/chapter-4-external-conversions.asciidoc

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,42 @@ public class ConversionServiceAdapter {
4747
}
4848
}
4949
----
50-
====
50+
====
51+
52+
[[externalAdapterMethodName]]
53+
=== Modifying the name for the generated adapter method
54+
55+
By default, the adapter class will contain method names of the form `map<SourceTypeName>To<targetTypeName>`.
56+
If you wish to change this, you can do so on a per-conversion basis by setting the property `adapterMethodName`:
57+
58+
====
59+
[source,java,linenums]
60+
[subs="verbatim,attributes"]
61+
----
62+
@MapperConfig(componentModel = "spring")
63+
@SpringMapperConfig(
64+
externalConversions = @ExternalConversion(sourceType = Blob.class, targetType = byte[].class, adapterMethodName = "blob2Bytes"))
65+
public interface MapstructConfig {}
66+
----
67+
====
68+
69+
This changes the generated method name to be the property's value:
70+
71+
====
72+
[source,java,linenums]
73+
[subs="verbatim,attributes"]
74+
----
75+
@Component
76+
public class ConversionServiceAdapter {
77+
private final ConversionService conversionService;
78+
79+
public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
80+
this.conversionService = conversionService;
81+
}
82+
83+
public byte[] blob2Bytes(final Blob source) {
84+
return (byte[]) conversionService.convert(source, TypeDescriptor.valueOf(Blob.class), TypeDescriptor.valueOf(byte[].class));
85+
}
86+
}
87+
----
88+
====

examples/external-conversions/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ dependencies {
1010
annotationProcessor libs.mapstruct.processor
1111
implementation libs.spring.context
1212
implementation libs.spring.core
13+
testImplementation libs.mockito
1314
testImplementation libs.spring.test
1415
}

examples/external-conversions/src/main/java/org/mapstruct/extensions/spring/example/externalconversions/MapstructConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
@SpringMapperConfig(
1212
externalConversions = {
1313
@ExternalConversion(sourceType = String.class, targetType = Locale.class),
14-
@ExternalConversion(sourceType = Blob.class, targetType = byte[].class)
14+
@ExternalConversion(sourceType = Blob.class, targetType = byte[].class, adapterMethodName = "blob2Bytes")
1515
})
1616
public interface MapstructConfig {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.mapstruct.extensions.spring.example.externalconversions;
2+
3+
import static java.nio.charset.StandardCharsets.UTF_8;
4+
import static org.assertj.core.api.BDDAssertions.then;
5+
import static org.mockito.BDDMockito.given;
6+
import static org.mockito.Mockito.mock;
7+
8+
import java.sql.Blob;
9+
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.ExtendWith;
12+
import org.mockito.InjectMocks;
13+
import org.mockito.Mock;
14+
import org.mockito.junit.jupiter.MockitoExtension;
15+
import org.springframework.core.convert.ConversionService;
16+
import org.springframework.core.convert.TypeDescriptor;
17+
18+
@ExtendWith(MockitoExtension.class)
19+
class ConversionServiceAdapterTest {
20+
@Mock private ConversionService conversionService;
21+
22+
@InjectMocks private ConversionServiceAdapter conversionServiceAdapter;
23+
24+
@Test
25+
void shouldCallConversionServiceFromGeneratedMethodWithOverriddenMethodName() {
26+
final var blob = mock(Blob.class);
27+
final var expectedBytes = "Hello World!".getBytes(UTF_8);
28+
given(
29+
conversionService.convert(
30+
blob, TypeDescriptor.valueOf(Blob.class), TypeDescriptor.valueOf(byte[].class)))
31+
.willReturn(expectedBytes);
32+
33+
final var actualBytes = conversionServiceAdapter.blob2Bytes(blob);
34+
35+
then(actualBytes).isSameAs(expectedBytes);
36+
}
37+
}

examples/noconfig/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
dependencies {
22
annotationProcessor project(":extensions")
33
implementation projects.examples.model
4+
implementation projects.annotations
45

56
testImplementation projects.testExtensions
67
testImplementation libs.assertj
@@ -10,5 +11,6 @@ dependencies {
1011
annotationProcessor libs.mapstruct.processor
1112
implementation libs.spring.context
1213
implementation libs.spring.core
14+
testImplementation libs.mockito
1315
testImplementation libs.spring.test
1416
}

examples/noconfig/src/main/java/org/mapstruct/extensions/spring/example/noconfig/WheelMapper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package org.mapstruct.extensions.spring.example.noconfig;
22

33
import org.mapstruct.Mapper;
4+
import org.mapstruct.extensions.spring.AdapterMethodName;
45
import org.mapstruct.extensions.spring.example.Wheel;
56
import org.mapstruct.extensions.spring.example.WheelDto;
67
import org.springframework.core.convert.converter.Converter;
78

89
@Mapper(config = MapperSpringConfig.class)
10+
@AdapterMethodName("toDto")
911
public interface WheelMapper extends Converter<Wheel, WheelDto> {
1012
@Override
1113
WheelDto convert(Wheel source);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.mapstruct.extensions.spring.example;
2+
3+
import static org.assertj.core.api.BDDAssertions.then;
4+
import static org.mapstruct.extensions.spring.example.WheelPosition.RIGHT_FRONT;
5+
import static org.mockito.BDDMockito.given;
6+
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.ExtendWith;
9+
import org.mapstruct.extensions.spring.converter.ConversionServiceAdapter;
10+
import org.mockito.InjectMocks;
11+
import org.mockito.Mock;
12+
import org.mockito.junit.jupiter.MockitoExtension;
13+
import org.springframework.core.convert.ConversionService;
14+
import org.springframework.core.convert.TypeDescriptor;
15+
16+
@ExtendWith(MockitoExtension.class)
17+
class ConversionServiceAdapterTest {
18+
@Mock private ConversionService conversionService;
19+
20+
@InjectMocks private ConversionServiceAdapter conversionServiceAdapter;
21+
22+
@Test
23+
void shouldCallConversionServiceFromGeneratedMethodWithOverriddenMethodName() {
24+
final var wheel = new Wheel();
25+
wheel.setPosition(RIGHT_FRONT);
26+
wheel.setDiameter(16);
27+
final var expectedDto = new WheelDto();
28+
expectedDto.setDiameter(16);
29+
expectedDto.setPosition("RIGHT_FRONT");
30+
given(
31+
conversionService.convert(
32+
wheel, TypeDescriptor.valueOf(Wheel.class), TypeDescriptor.valueOf(WheelDto.class)))
33+
.willReturn(expectedDto);
34+
35+
final var actualDto = conversionServiceAdapter.toDto(wheel);
36+
37+
then(actualDto).isSameAs(expectedDto);
38+
}
39+
}

0 commit comments

Comments
 (0)