FFFF Fixes #81 by Chessray · Pull Request #82 · mapstruct/mapstruct-spring-extensions · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,12 @@ subprojects {
}
}

test {
// Use junit platform for unit tests
useJUnitPlatform()
testing {
suites {
test {
useJUnitJupiter()
}
}
}

jacoco {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.mapstruct.extensions.spring.converter;

import static java.lang.Boolean.TRUE;
import static java.lang.Boolean.parseBoolean;
import static java.time.format.DateTimeFormatter.ISO_INSTANT;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Stream.concat;
Expand Down Expand Up @@ -51,6 +52,7 @@ public class ConversionServiceAdapterGenerator {
JAVA_9_PLUS_ANNOTATION_GENERATED_PACKAGE, GENERATED_ANNOTATION_CLASS_NAME_STRING);
private static final ClassName COMPONENT_ANNOTATION_CLASS_NAME =
ClassName.get("org.springframework.stereotype", "Component");
private static final String SUPPRESS_GENERATOR_TIMESTAMP = "mapstruct.suppressGeneratorTimestamp";
private final Clock clock;

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


ProcessingEnvironment getProcessingEnvironment() {
return processingEnvironment.get();
}

public void writeConversionServiceAdapter(
final ConversionServiceAdapterDescriptor descriptor, final Writer out) {
try {
Expand Down Expand Up @@ -141,10 +142,11 @@ private String collectionOfMethodName(final ParameterizedTypeName parameterizedT
return simpleName(parameterizedTypeName);
}

private boolean isCollectionWithGenericParameter(final ParameterizedTypeName parameterizedTypeName) {
private boolean isCollectionWithGenericParameter(
final ParameterizedTypeName parameterizedTypeName) {
return parameterizedTypeName.typeArguments != null
&& parameterizedTypeName.typeArguments.size() > 0
&& isCollection(parameterizedTypeName);
&& parameterizedTypeName.typeArguments.size() > 0
&& isCollection(parameterizedTypeName);
}

private boolean isCollection(final ParameterizedTypeName parameterizedTypeName) {
Expand Down Expand Up @@ -194,8 +196,8 @@ private static TypeName rawType(final TypeName typeName) {
}

private Iterable<MethodSpec> buildMappingMethods(
final ConversionServiceAdapterDescriptor descriptor,
final FieldSpec injectedConversionServiceFieldSpec) {
final ConversionServiceAdapterDescriptor descriptor,
final FieldSpec injectedConversionServiceFieldSpec) {
return descriptor.getFromToMappings().stream()
.map(
sourceTargetPair ->
Expand All @@ -204,8 +206,8 @@ private Iterable<MethodSpec> buildMappingMethods(
}

private MethodSpec toMappingMethodSpec(
final FieldSpec injectedConversionServiceFieldSpec,
final Pair<TypeName, TypeName> sourceTargetPair) {
final FieldSpec injectedConversionServiceFieldSpec,
final Pair<TypeName, TypeName> sourceTargetPair) {
final ParameterSpec sourceParameterSpec = buildSourceParameterSpec(sourceTargetPair.getLeft());
return MethodSpec.methodBuilder(
String.format(
Expand All @@ -220,7 +222,8 @@ private MethodSpec toMappingMethodSpec(
"return ($T) $N.convert($N, %s, %s)",
typeDescriptorFormat(sourceTargetPair.getLeft()),
typeDescriptorFormat(sourceTargetPair.getRight())),
allTypeDescriptorArguments(injectedConversionServiceFieldSpec, sourceParameterSpec, sourceTargetPair))
allTypeDescriptorArguments(
injectedConversionServiceFieldSpec, sourceParameterSpec, sourceTargetPair))
.build();
}

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

private String typeDescriptorFormat(final TypeName typeName) {
if (typeName instanceof ParameterizedTypeName
&& isCollectionWithGenericParameter((ParameterizedTypeName) typeName)) {
&& isCollectionWithGenericParameter((ParameterizedTypeName) typeName)) {
return String.format(
"$T.collection($T.class, %s)",
typeDescriptorFormat(((ParameterizedTypeName) typeName).t 10BC0 ypeArguments.iterator().next()));
"$T.collection($T.class, %s)",
typeDescriptorFormat(((ParameterizedTypeName) typeName).typeArguments.iterator().next()));
}
return "$T.valueOf($T.class)";
}
Expand Down Expand Up @@ -274,11 +277,19 @@ private AnnotationSpec buildGeneratedAnnotationSpec() {
.map(
build ->
build.addMember("value", "$S", ConversionServiceAdapterGenerator.class.getName()))
.map(build -> build.addMember("date", "$S", ISO_INSTANT.format(ZonedDateTime.now(clock))))
.map(this::addDateIfNotSuppressed)
.map(AnnotationSpec.Builder::build)
.orElse(null);
}

private AnnotationSpec.Builder addDateIfNotSuppressed(
final AnnotationSpec.Builder generatedAnnotationSpecBuilder) {
return parseBoolean(processingEnvironment.get().getOptions().get(SUPPRESS_GENERATOR_TIMESTAMP))
? generatedAnnotationSpecBuilder
: generatedAnnotationSpecBuilder.addMember(
"date", "$S", ISO_INSTANT.format(ZonedDateTime.now(clock)));
}

private AnnotationSpec.Builder baseAnnotationSpecBuilder() {
final AnnotationSpec.Builder builder;
if (isJava9PlusGeneratedAvailable()) {
Expand All @@ -296,8 +307,7 @@ private boolean isPreJava9GeneratedAvailable() {
}

private boolean isJava9PlusGeneratedAvailable() {
return isSourceVersionAtLeast9()
&& isTypeAvailable(JAVA_9_PLUS_ANNOTATION_GENERATED);
return isSourceVersionAtLeast9() && isTypeAvailable(JAVA_9_PLUS_ANNOTATION_GENERATED);
}

private boolean isSourceVersionAtLeast9() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.mapstruct.extensions.spring.converter;

import static java.lang.Boolean.TRUE;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.function.UnaryOperator.identity;
import static javax.lang.model.SourceVersion.RELEASE_8;
import static javax.lang.model.SourceVersion.RELEASE_9;
import static org.apache.commons.io.IOUtils.resourceToString;
Expand All @@ -17,6 +19,8 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
Expand Down Expand Up @@ -46,22 +50,24 @@ class ConversionServiceAdapterGeneratorTest {

@Nested
class DefaultProcessingEnvironment {
@Mock private ProcessingEnvironment processingEnvironment;

@BeforeEach
void initWithProcessingEnvironment() {
final var processingEnvironment = mock(ProcessingEnvironment.class);
given(processingEnvironment.getElementUtils()).willReturn(elements);
given(processingEnvironment.getSourceVersion())
.will((Answer<SourceVersion>)
(invocation) -> {
if (isAtLeastJava9) {
return RELEASE_9;
} else {
return RELEASE_8;
}
});
.will(
(Answer<SourceVersion>)
(invocation) -> {
if (isAtLeastJava9) {
return RELEASE_9;
} else {
return RELEASE_8;
}
});
underTest.init(processingEnvironment);
}

@Nested
class Java8Generated {
@BeforeEach
Expand All @@ -83,6 +89,15 @@ void shouldGenerateMatchingOutputWhenUsingCustomConversionService() throws IOExc
.shouldGenerateMatchingOutputWhenUsingCustomConversionService(
"ConversionServiceAdapterCustomBeanJava8Generated.java");
}

@Test
void shouldSuppressDateGenerationWhenProcessingEnvironmentHasSuppressionSetToTrue()
throws IOException {
given(processingEnvironment.getOptions())
.willReturn(Map.of("mapstruct.suppressGeneratorTimestamp", String.valueOf(TRUE)));
ConversionServiceAdapterGeneratorTest.this.shouldGenerateMatchingOutput(
"ConversionServiceAdapterJava8GeneratedNoDate.java");
}
}

@Nested
Expand All @@ -106,6 +121,15 @@ void shouldGenerateMatchingOutputWhenUsingCustomConversionService() throws IOExc
.shouldGenerateMatchingOutputWhenUsingCustomConversionService(
"ConversionServiceAdapterCustomBeanJava9PlusGenerated.java");
}

@Test
void shouldSuppressDateGenerationWhenProcessingEnvironmentHasSuppressionSetToTrue()
throws IOException {
given(processingEnvironment.getOptions())
.willReturn(Map.of("mapstruct.suppressGeneratorTimestamp", String.valueOf(TRUE)));
ConversionServiceAdapterGeneratorTest.this.shouldGenerateMatchingOutput(
"ConversionServiceAdapterJava9PlusGeneratedNoDate.java");
}
}

@Nested
Expand All @@ -130,23 +154,26 @@ void shouldGenerateMatchingOutputWhenUsingCustomConversionService() throws IOExc
}
}

void shouldGenerateMatchingOutput(final String expectedContentFileName) throws IOException {
// Given
void shouldGenerateMatchingOutput(
final String expectedContentFileName,
final UnaryOperator<ConversionServiceAdapterDescriptor> descriptorDecorator)
throws IOException {
final ConversionServiceAdapterDescriptor descriptor =
new ConversionServiceAdapterDescriptor()
.adapterClassName(
ClassName.get(
ConversionServiceAdapterGeneratorTest.class.getPackage().getName(),
"ConversionServiceAdapter"))
.fromToMappings(
List.of(
Pair.of(ClassName.get("test", "Car"), ClassName.get("test", "CarDto")),
Pair.of(
ParameterizedTypeName.get(
ClassName.get(List.class), ClassName.get("test", "Car")),
ParameterizedTypeName.get(
ClassName.get(List.class), ClassName.get("test", "CarDto")))))
.lazyAnnotatedConversionServiceBean(true);
descriptorDecorator.apply(
new ConversionServiceAdapterDescriptor()
.adapterClassName(
ClassName.get(
ConversionServiceAdapterGeneratorTest.class.getPackage().getName(),
"ConversionServiceAdapter"))
.fromToMappings(
List.of(
Pair.of(ClassName.get("test", "Car"), ClassName.get("test", "CarDto")),
Pair.of(
ParameterizedTypeName.get(
ClassName.get(List.class), ClassName.get("test", "Car")),
ParameterizedTypeName.get(
ClassName.get(List.class), ClassName.get("test", "CarDto")))))
.lazyAnnotatedConversionServiceBean(true));
final StringWriter outputWriter = new StringWriter();

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

void shouldGenerateMatchingOutput(final String expectedContentFileName) throws IOException {
shouldGenerateMatchingOutput(expectedContentFileName, identity());
}

void shouldGenerateMatchingOutputWhenUsingCustomConversionService(
final String expectedContentFileName) throws IOException {
// Given
final ConversionServiceAdapterDescriptor descriptor =
new ConversionServiceAdapterDescriptor()
.adapterClassName(
ClassName.get(
ConversionServiceAdapterGeneratorTest.class.getPackage().getName(),
"ConversionServiceAdapter"))
.conversionServiceBeanName("myConversionService")
.fromToMappings(
List.of(
Pair.of(ClassName.get("test", "Car"), ClassName.get("test", "CarDto")),
Pair.of(
ParameterizedTypeName.get(
ClassName.get(List.class), ClassName.get("test", "Car")),
ParameterizedTypeName.get(
ClassName.get(List.class), ClassName.get("test", "CarDto")))))
.lazyAnnotatedConversionServiceBean(true);
final StringWriter outputWriter = new StringWriter();

// When
underTest.writeConversionServiceAdapter(descriptor, outputWriter);

// Then
then(outputWriter.toString())
.isEqualToIgnoringWhitespace(resourceToString('/' + expectedContentFileName, UTF_8));
shouldGenerateMatchingOutput(
expectedContentFileName,
descriptor -> descriptor.conversionServiceBeanName("myConversionService"));
}

@Nested
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.mapstruct.extensions.spring.converter;

import java.util.List;
import javax.annotation.Generated;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.stereotype.Component;
import test.Car;
import test.CarDto;

@Generated("org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator")
@Component
public class ConversionServiceAdapter {
private final ConversionService conversionService;

public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
this.conversionService = conversionService;
}

public CarDto mapCarToCarDto(final Car source) {
return (CarDto) conversionService.convert(source, TypeDescriptor.valueOf(Car.class), TypeDescriptor.valueOf(CarDto.class));
}

public List<CarDto> mapListOfCarToListOfCarDto(final List<Car> source) {
return (List<CarDto>) conversionService.convert(source, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Car.class)), TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(CarDto.class)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.mapstruct.extensions.spring.converter;

import java.util.List;
import javax.annotation.processing.Generated;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.stereotype.Component;
import test.Car;
import test.CarDto;

@Generated("org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator")
@Component
4C1F public class ConversionServiceAdapter {
private final ConversionService conversionService;

public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
this.conversionService = conversionService;
}

public CarDto mapCarToCarDto(final Car source) {
return (CarDto) conversionService.convert(source, TypeDescriptor.valueOf(Car.class), TypeDescriptor.valueOf(CarDto.class));
}

public List<CarDto> mapListOfCarToListOfCarDto(final List<Car> source) {
return (List<CarDto>) conversionService.convert(source, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Car.class)), TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(CarDto.class)));
}
}
0