Skip to content

Commit 58f457e

Browse files
authored
Fixes #81 (#82)
* Fixes #81
1 parent efb49dd commit 58f457e

File tree

5 files changed

+148
-70
lines changed

5 files changed

+148
-70
lines changed

build.gradle

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,12 @@ subprojects {
6363
}
6464
}
6565

66-
test {
67-
// Use junit platform for unit tests
68-
useJUnitPlatform()
66+
testing {
67+
suites {
68+
test {
69+
useJUnitJupiter()
70+
}
71+
}
6972
}
7073

7174
jacoco {

extensions/src/main/java/org/mapstruct/extensions/spring/converter/ConversionServiceAdapterGenerator.java

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.mapstruct.extensions.spring.converter;
22

33
import static java.lang.Boolean.TRUE;
4+
import static java.lang.Boolean.parseBoolean;
45
import static java.time.format.DateTimeFormatter.ISO_INSTANT;
56
import static java.util.stream.Collectors.toList;
67
import static java.util.stream.Stream.concat;
@@ -51,6 +52,7 @@ public class ConversionServiceAdapterGenerator {
5152
JAVA_9_PLUS_ANNOTATION_GENERATED_PACKAGE, GENERATED_ANNOTATION_CLASS_NAME_STRING);
5253
private static final ClassName COMPONENT_ANNOTATION_CLASS_NAME =
5354
ClassName.get("org.springframework.stereotype", "Component");
55+
private static final String SUPPRESS_GENERATOR_TIMESTAMP = "mapstruct.suppressGeneratorTimestamp";
5456
private final Clock clock;
5557

5658
private final AtomicReference<ProcessingEnvironment> processingEnvironment;
@@ -60,11 +62,10 @@ public ConversionServiceAdapterGenerator(final Clock clock) {
6062
processingEnvironment = new AtomicReference<>();
6163
}
6264

63-
6465
ProcessingEnvironment getProcessingEnvironment() {
6566
return processingEnvironment.get();
6667
}
67-
68+
6869
public void writeConversionServiceAdapter(
6970
final ConversionServiceAdapterDescriptor descriptor, final Writer out) {
7071
try {
@@ -141,10 +142,11 @@ private String collectionOfMethodName(final ParameterizedTypeName parameterizedT
141142
return simpleName(parameterizedTypeName);
142143
}
143144

144-
private boolean isCollectionWithGenericParameter(final ParameterizedTypeName parameterizedTypeName) {
145+
private boolean isCollectionWithGenericParameter(
146+
final ParameterizedTypeName parameterizedTypeName) {
145147
return parameterizedTypeName.typeArguments != null
146-
&& parameterizedTypeName.typeArguments.size() > 0
147-
&& isCollection(parameterizedTypeName);
148+
&& parameterizedTypeName.typeArguments.size() > 0
149+
&& isCollection(parameterizedTypeName);
148150
}
149151

150152
private boolean isCollection(final ParameterizedTypeName parameterizedTypeName) {
@@ -194,8 +196,8 @@ private static TypeName rawType(final TypeName typeName) {
194196
}
195197

196198
private Iterable<MethodSpec> buildMappingMethods(
197-
final ConversionServiceAdapterDescriptor descriptor,
198-
final FieldSpec injectedConversionServiceFieldSpec) {
199+
final ConversionServiceAdapterDescriptor descriptor,
200+
final FieldSpec injectedConversionServiceFieldSpec) {
199201
return descriptor.getFromToMappings().stream()
200202
.map(
201203
sourceTargetPair ->
@@ -204,8 +206,8 @@ private Iterable<MethodSpec> buildMappingMethods(
204206
}
205207

206208
private MethodSpec toMappingMethodSpec(
207-
final FieldSpec injectedConversionServiceFieldSpec,
208-
final Pair<TypeName, TypeName> sourceTargetPair) {
209+
final FieldSpec injectedConversionServiceFieldSpec,
210+
final Pair<TypeName, TypeName> sourceTargetPair) {
209211
final ParameterSpec sourceParameterSpec = buildSourceParameterSpec(sourceTargetPair.getLeft());
210212
return MethodSpec.methodBuilder(
211213
String.format(
@@ -220,7 +222,8 @@ private MethodSpec toMappingMethodSpec(
220222
"return ($T) $N.convert($N, %s, %s)",
221223
typeDescriptorFormat(sourceTargetPair.getLeft()),
222224
typeDescriptorFormat(sourceTargetPair.getRight())),
223-
allTypeDescriptorArguments(injectedConversionServiceFieldSpec, sourceParameterSpec, sourceTargetPair))
225+
allTypeDescriptorArguments(
226+
injectedConversionServiceFieldSpec, sourceParameterSpec, sourceTargetPair))
224227
.build();
225228
}
226229

@@ -241,10 +244,10 @@ private Object[] allTypeDescriptorArguments(
241244

242245
private String typeDescriptorFormat(final TypeName typeName) {
243246
if (typeName instanceof ParameterizedTypeName
244-
&& isCollectionWithGenericParameter((ParameterizedTypeName) typeName)) {
247+
&& isCollectionWithGenericParameter((ParameterizedTypeName) typeName)) {
245248
return String.format(
246-
"$T.collection($T.class, %s)",
247-
typeDescriptorFormat(((ParameterizedTypeName) typeName).typeArguments.iterator().next()));
249+
"$T.collection($T.class, %s)",
250+
typeDescriptorFormat(((ParameterizedTypeName) typeName).typeArguments.iterator().next()));
248251
}
249252
return "$T.valueOf($T.class)";
250253
}
@@ -274,11 +277,19 @@ private AnnotationSpec buildGeneratedAnnotationSpec() {
274277
.map(
275278
build ->
276279
build.addMember("value", "$S", ConversionServiceAdapterGenerator.class.getName()))
277-
.map(build -> build.addMember("date", "$S", ISO_INSTANT.format(ZonedDateTime.now(clock))))
280+
.map(this::addDateIfNotSuppressed)
278281
.map(AnnotationSpec.Builder::build)
279282
.orElse(null);
280283
}
281284

285+
private AnnotationSpec.Builder addDateIfNotSuppressed(
286+
final AnnotationSpec.Builder generatedAnnotationSpecBuilder) {
287+
return parseBoolean(processingEnvironment.get().getOptions().get(SUPPRESS_GENERATOR_TIMESTAMP))
288+
? generatedAnnotationSpecBuilder
289+
: generatedAnnotationSpecBuilder.addMember(
290+
"date", "$S", ISO_INSTANT.format(ZonedDateTime.now(clock)));
291+
}
292+
282293
private AnnotationSpec.Builder baseAnnotationSpecBuilder() {
283294
final AnnotationSpec.Builder builder;
284295
if (isJava9PlusGeneratedAvailable()) {
@@ -296,8 +307,7 @@ private boolean isPreJava9GeneratedAvailable() {
296307
}
297308

298309
private boolean isJava9PlusGeneratedAvailable() {
299-
return isSourceVersionAtLeast9()
300-
&& isTypeAvailable(JAVA_9_PLUS_ANNOTATION_GENERATED);
310+
return isSourceVersionAtLeast9() && isTypeAvailable(JAVA_9_PLUS_ANNOTATION_GENERATED);
301311
}
302312

303313
private boolean isSourceVersionAtLeast9() {

extensions/src/test/java/org/mapstruct/extensions/spring/converter/ConversionServiceAdapterGeneratorTest.java

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.mapstruct.extensions.spring.converter;
22

3+
import static java.lang.Boolean.TRUE;
34
import static java.nio.charset.StandardCharsets.UTF_8;
5+
import static java.util.function.UnaryOperator.identity;
46
import static javax.lang.model.SourceVersion.RELEASE_8;
57
import static javax.lang.model.SourceVersion.RELEASE_9;
68
import static org.apache.commons.io.IOUtils.resourceToString;
@@ -17,6 +19,8 @@
1719
import java.time.ZoneId;
1820
import java.time.ZonedDateTime;
1921
import java.util.List;
22+
import java.util.Map;
23+
import java.util.function.UnaryOperator;
2024
import javax.annotation.processing.ProcessingEnvironment;
2125
import javax.lang.model.SourceVersion;
2226
import javax.lang.model.element.TypeElement;
@@ -46,22 +50,24 @@ class ConversionServiceAdapterGeneratorTest {
4650

4751
@Nested
4852
class DefaultProcessingEnvironment {
53+
@Mock private ProcessingEnvironment processingEnvironment;
54+
4955
@BeforeEach
5056
void initWithProcessingEnvironment() {
51-
final var processingEnvironment = mock(ProcessingEnvironment.class);
5257
given(processingEnvironment.getElementUtils()).willReturn(elements);
5358
given(processingEnvironment.getSourceVersion())
54-
.will((Answer<SourceVersion>)
55-
(invocation) -> {
56-
if (isAtLeastJava9) {
57-
return RELEASE_9;
58-
} else {
59-
return RELEASE_8;
60-
}
61-
});
59+
.will(
60+
(Answer<SourceVersion>)
61+
(invocation) -> {
62+
if (isAtLeastJava9) {
63+
return RELEASE_9;
64+
} else {
65+
return RELEASE_8;
66+
}
67+
});
6268
underTest.init(processingEnvironment);
6369
}
64-
70+
6571
@Nested
6672
class Java8Generated {
6773
@BeforeEach
@@ -83,6 +89,15 @@ void shouldGenerateMatchingOutputWhenUsingCustomConversionService() throws IOExc
8389
.shouldGenerateMatchingOutputWhenUsingCustomConversionService(
8490
"ConversionServiceAdapterCustomBeanJava8Generated.java");
8591
}
92+
93+
@Test
94+
void shouldSuppressDateGenerationWhenProcessingEnvironmentHasSuppressionSetToTrue()
95+
throws IOException {
96+
given(processingEnvironment.getOptions())
97+
.willReturn(Map.of("mapstruct.suppressGeneratorTimestamp", String.valueOf(TRUE)));
98+
ConversionServiceAdapterGeneratorTest.this.shouldGenerateMatchingOutput(
99+
"ConversionServiceAdapterJava8GeneratedNoDate.java");
100+
}
86101
}
87102

88103
@Nested
@@ -106,6 +121,15 @@ void shouldGenerateMatchingOutputWhenUsingCustomConversionService() throws IOExc
106121
.shouldGenerateMatchingOutputWhenUsingCustomConversionService(
107122
"ConversionServiceAdapterCustomBeanJava9PlusGenerated.java");
108123
}
124+
125+
@Test
126+
void shouldSuppressDateGenerationWhenProcessingEnvironmentHasSuppressionSetToTrue()
127+
throws IOException {
128+
given(processingEnvironment.getOptions())
129+
.willReturn(Map.of("mapstruct.suppressGeneratorTimestamp", String.valueOf(TRUE)));
130+
ConversionServiceAdapterGeneratorTest.this.shouldGenerateMatchingOutput(
131+
"ConversionServiceAdapterJava9PlusGeneratedNoDate.java");
132+
}
109133
}
110134

111135
@Nested
@@ -130,23 +154,26 @@ void shouldGenerateMatchingOutputWhenUsingCustomConversionService() throws IOExc
130154
}
131155
}
132156

133-
void shouldGenerateMatchingOutput(final String expectedContentFileName) throws IOException {
134-
// Given
157+
void shouldGenerateMatchingOutput(
158+
final String expectedContentFileName,
159+
final UnaryOperator<ConversionServiceAdapterDescriptor> descriptorDecorator)
160+
throws IOException {
135161
final ConversionServiceAdapterDescriptor descriptor =
136-
new ConversionServiceAdapterDescriptor()
137-
.adapterClassName(
138-
ClassName.get(
139-
ConversionServiceAdapterGeneratorTest.class.getPackage().getName(),
140-
"ConversionServiceAdapter"))
141-
.fromToMappings(
142-
List.of(
143-
Pair.of(ClassName.get("test", "Car"), ClassName.get("test", "CarDto")),
144-
Pair.of(
145-
ParameterizedTypeName.get(
146-
ClassName.get(List.class), ClassName.get("test", "Car")),
147-
ParameterizedTypeName.get(
148-
ClassName.get(List.class), ClassName.get("test", "CarDto")))))
149-
.lazyAnnotatedConversionServiceBean(true);
162+
descriptorDecorator.apply(
163+
new ConversionServiceAdapterDescriptor()
164+
.adapterClassName(
165+
ClassName.get(
166+
ConversionServiceAdapterGeneratorTest.class.getPackage().getName(),
167+
"ConversionServiceAdapter"))
168+
.fromToMappings(
169+
List.of(
170+
Pair.of(ClassName.get("test", "Car"), ClassName.get("test", "CarDto")),
171+
Pair.of(
172+
ParameterizedTypeName.get(
173+
ClassName.get(List.class), ClassName.get("test", "Car")),
174+
ParameterizedTypeName.get(
175+
ClassName.get(List.class), ClassName.get("test", "CarDto")))))
176+
.lazyAnnotatedConversionServiceBean(true));
150177
final StringWriter outputWriter = new StringWriter();
151178

152179
// When
@@ -157,33 +184,15 @@ void shouldGenerateMatchingOutput(final String expectedContentFileName) throws I
157184
.isEqualToIgnoringWhitespace(resourceToString('/' + expectedContentFileName, UTF_8));
158185
}
159186

187+
void shouldGenerateMatchingOutput(final String expectedContentFileName) throws IOException {
188+
shouldGenerateMatchingOutput(expectedContentFileName, identity());
189+
}
190+
160191
void shouldGenerateMatchingOutputWhenUsingCustomConversionService(
161192
final String expectedContentFileName) throws IOException {
162-
// Given
163-
final ConversionServiceAdapterDescriptor descriptor =
164-
new ConversionServiceAdapterDescriptor()
165-
.adapterClassName(
166-
ClassName.get(
167-
ConversionServiceAdapterGeneratorTest.class.getPackage().getName(),
168-
"ConversionServiceAdapter"))
169-
.conversionServiceBeanName("myConversionService")
170-
.fromToMappings(
171-
List.of(
172-
Pair.of(ClassName.get("test", "Car"), ClassName.get("test", "CarDto")),
173-
Pair.of(
174-
ParameterizedTypeName.get(
175-
ClassName.get(List.class), ClassName.get("test", "Car")),
176-
ParameterizedTypeName.get(
177-
ClassName.get(List.class), ClassName.get("test", "CarDto")))))
178-
.lazyAnnotatedConversionServiceBean(true);
179-
final StringWriter outputWriter = new StringWriter();
180-
181-
// When
182-
underTest.writeConversionServiceAdapter(descriptor, outputWriter);
183-
184-
// Then
185-
then(outputWriter.toString())
186-
.isEqualToIgnoringWhitespace(resourceToString('/' + expectedContentFileName, UTF_8));
193+
shouldGenerateMatchingOutput(
194+
expectedContentFileName,
195+
descriptor -> descriptor.conversionServiceBeanName("myConversionService"));
187196
}
188197

189198
@Nested
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.mapstruct.extensions.spring.converter;
2+
3+
import java.util.List;
4+
import javax.annotation.Generated;
5+
import org.springframework.context.annotation.Lazy;
6+
import org.springframework.core.convert.ConversionService;
7+
import org.springframework.core.convert.TypeDescriptor;
8+
import org.springframework.stereotype.Component;
9+
import test.Car;
10+
import test.CarDto;
11+
12+
@Generated("org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator")
13+
@Component
14+
public class ConversionServiceAdapter {
15+
private final ConversionService conversionService;
16+
17+
public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
18+
this.conversionService = conversionService;
19+
}
20+
21+
public CarDto mapCarToCarDto(final Car source) {
22+
return (CarDto) conversionService.convert(source, TypeDescriptor.valueOf(Car.class), TypeDescriptor.valueOf(CarDto.class));
23+
}
24+
25+
public List<CarDto> mapListOfCarToListOfCarDto(final List<Car> source) {
26+
return (List<CarDto>) conversionService.convert(source, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Car.class)), TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(CarDto.class)));
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.mapstruct.extensions.spring.converter;
2+
3+
import java.util.List;
4+
import javax.annotation.processing.Generated;
5+
import org.springframework.context.annotation.Lazy;
6+
import org.springframework.core.convert.ConversionService;
7+
import org.springframework.core.convert.TypeDescriptor;
8+
import org.springframework.stereotype.Component;
9+
import test.Car;
10+
import test.CarDto;
11+
12+
@Generated("org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator")
13+
@Component
14+
public class ConversionServiceAdapter {
15+
private final ConversionService conversionService;
16+
17+
public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
18+
this.conversionService = conversionService;
19+
}
20+
21+
public CarDto mapCarToCarDto(final Car source) {
22+
return (CarDto) conversionService.convert(source, TypeDescriptor.valueOf(Car.class), TypeDescriptor.valueOf(CarDto.class));
23+
}
24+
25+
public List<CarDto> mapListOfCarToListOfCarDto(final List<Car> source) {
26+
return (List<CarDto>) conversionService.convert(source, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Car.class)), TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(CarDto.class)));
27+
}
28+
}

0 commit comments

Comments
 (0)