Skip to content

Commit 90ef025

Browse files
authored
Merge pull request #6 from mapstruct/specifyNameForGeneratedBridgeClass
Specify name for generated adapter class Closes #1
2 parents b27a720 + 4bf2522 commit 90ef025

File tree

45 files changed

+995
-217
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+995
-217
lines changed

additions/build.gradle

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
plugins {
2-
id "io.freefair.lombok" version "5.0.0-rc6"
3-
}
4-
51
dependencies {
6-
implementation 'org.mapstruct:mapstruct:1.3.1.Final'
2+
implementation project(":annotations")
3+
implementation 'org.mapstruct:mapstruct:1.4.0-SNAPSHOT'
74
implementation 'org.springframework:spring-core:5.0.0.RELEASE'
85
implementation 'com.squareup:javapoet:1.12.1'
96
implementation 'org.apache.commons:commons-lang3:3.10'
107

118
testImplementation 'org.assertj:assertj-core:3.15.0'
129
testImplementation 'commons-io:commons-io:2.6'
10+
testImplementation 'com.google.guava:guava:28.2-jre'
11+
testImplementation 'org.mockito:mockito-junit-jupiter:3.3.3'
1312
}

additions/lombok.config

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package uk.co.kleindelao.mapstruct.spring.converter;
2+
3+
import com.squareup.javapoet.ClassName;
4+
import java.util.List;
5+
import org.apache.commons.lang3.tuple.Pair;
6+
7+
public class ConversionServiceAdapterDescriptor {
8+
private ClassName adapterClassName;
9+
private List<Pair<ClassName, ClassName>> fromToMappings;
10+
11+
public ClassName getAdapterClassName() {
12+
return adapterClassName;
13+
}
14+
15+
public void setAdapterClassName(final ClassName adapterClassName) {
16+
this.adapterClassName = adapterClassName;
17+
}
18+
19+
public List<Pair<ClassName, ClassName>> getFromToMappings() {
20+
return fromToMappings;
21+
}
22+
23+
public void setFromToMappings(final List<Pair<ClassName, ClassName>> fromToMappings) {
24+
this.fromToMappings = fromToMappings;
25+
}
26+
}

additions/src/main/java/uk/co/kleindelao/mapstruct/spring/converter/ConversionServiceBridgeGenerator.java renamed to additions/src/main/java/uk/co/kleindelao/mapstruct/spring/converter/ConversionServiceAdapterGenerator.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
package uk.co.kleindelao.mapstruct.spring.converter;
22

3-
import com.squareup.javapoet.*;
4-
import lombok.Value;
5-
import org.springframework.core.convert.ConversionService;
3+
import static java.util.stream.Collectors.toList;
4+
import static javax.lang.model.element.Modifier.*;
65

6+
import com.squareup.javapoet.*;
77
import java.io.IOException;
88
import java.io.UncheckedIOException;
99
import java.io.Writer;
1010
import java.time.Clock;
1111
import java.time.ZonedDateTime;
1212
import java.time.format.DateTimeFormatter;
13+
import org.springframework.core.convert.ConversionService;
1314

14-
import static java.util.stream.Collectors.toList;
15-
import static javax.lang.model.element.Modifier.*;
15+
public class ConversionServiceAdapterGenerator {
16+
private final Clock clock;
1617

17-
@Value
18-
public class ConversionServiceBridgeGenerator {
19-
Clock clock;
18+
public ConversionServiceAdapterGenerator(final Clock clock) {
19+
this.clock = clock;
20+
}
2021

21-
public void writeConversionServiceBridge(
22-
ConversionServiceBridgeDescriptor descriptor, Writer out) {
22+
public void writeConversionServiceAdapter(
23+
ConversionServiceAdapterDescriptor descriptor, Writer out) {
2324
try {
2425
JavaFile.builder(
25-
descriptor.getBridgeClassName().packageName(),
26+
descriptor.getAdapterClassName().packageName(),
2627
createConversionServiceTypeSpec(descriptor))
2728
.build()
2829
.writeTo(out);
@@ -32,9 +33,9 @@ public void writeConversionServiceBridge(
3233
}
3334

3435
private TypeSpec createConversionServiceTypeSpec(
35-
final ConversionServiceBridgeDescriptor descriptor) {
36+
final ConversionServiceAdapterDescriptor descriptor) {
3637
final FieldSpec injectedConversionServiceFieldSpec = buildInjectedConversionServiceFieldSpec();
37-
return TypeSpec.classBuilder(descriptor.getBridgeClassName())
38+
return TypeSpec.classBuilder(descriptor.getAdapterClassName())
3839
.addModifiers(PUBLIC)
3940
.addAnnotation(buildGeneratedAnnotationSpec())
4041
.addAnnotation(ClassName.get("org.springframework.stereotype", "Component"))
@@ -44,7 +45,7 @@ private TypeSpec createConversionServiceTypeSpec(
4445
}
4546

4647
private static Iterable<MethodSpec> buildMappingMethods(
47-
final ConversionServiceBridgeDescriptor descriptor,
48+
final ConversionServiceAdapterDescriptor descriptor,
4849
final FieldSpec injectedConversionServiceFieldSpec) {
4950
return descriptor.getFromToMappings().stream()
5051
.map(
@@ -81,7 +82,7 @@ private static FieldSpec buildInjectedConversionServiceFieldSpec() {
8182

8283
private AnnotationSpec buildGeneratedAnnotationSpec() {
8384
return AnnotationSpec.builder(ClassName.get("javax.annotation", "Generated"))
84-
.addMember("value", "$S", ConversionServiceBridgeGenerator.class.getName())
85+
.addMember("value", "$S", ConversionServiceAdapterGenerator.class.getName())
8586
.addMember("date", "$S", DateTimeFormatter.ISO_INSTANT.format(ZonedDateTime.now(clock)))
8687
.build();
8788
}

additions/src/main/java/uk/co/kleindelao/mapstruct/spring/converter/ConversionServiceBridgeDescriptor.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

additions/src/main/java/uk/co/kleindelao/mapstruct/spring/converter/ConverterMapperProcessor.java

Lines changed: 142 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,169 @@
11
package uk.co.kleindelao.mapstruct.spring.converter;
22

3-
import com.squareup.javapoet.ClassName;
4-
import org.apache.commons.lang3.tuple.Pair;
5-
import org.springframework.core.convert.converter.Converter;
6-
import uk.co.kleindelao.mapstruct.spring.converter.ConversionServiceBridgeDescriptor.ConversionServiceBridgeDescriptorBuilder;
3+
import static java.util.stream.Collectors.toList;
4+
import static javax.lang.model.element.ElementKind.METHOD;
5+
import static javax.lang.model.element.Modifier.PUBLIC;
6+
import static javax.lang.model.type.TypeKind.DECLARED;
7+
import static javax.tools.Diagnostic.Kind.ERROR;
78

9+
import com.squareup.javapoet.ClassName;
10+
import java.io.IOException;
11+
import java.io.Writer;
12+
import java.time.Clock;
13+
import java.util.List;
14+
import java.util.Objects;
15+
import java.util.Optional;
16+
import java.util.Set;
817
import javax.annotation.processing.AbstractProcessor;
918
import javax.annotation.processing.RoundEnvironment;
1019
import javax.annotation.processing.SupportedAnnotationTypes;
20+
import javax.lang.model.SourceVersion;
1121
import javax.lang.model.element.Element;
1222
import javax.lang.model.element.ExecutableElement;
1323
import javax.lang.model.element.TypeElement;
1424
import javax.lang.model.type.DeclaredType;
1525
import javax.lang.model.type.TypeMirror;
1626
import javax.lang.model.util.Types;
17-
import java.io.IOException;
18-
import java.io.Writer;
19-
import java.time.Clock;
20-
import java.util.Optional;
21-
import java.util.Set;
22-
23-
import static javax.lang.model.element.ElementKind.METHOD;
24-
import static javax.lang.model.element.Modifier.PUBLIC;
25-
import static javax.lang.model.type.TypeKind.DECLARED;
26-
import static javax.tools.Diagnostic.Kind.ERROR;
27+
import org.apache.commons.lang3.StringUtils;
28+
import org.apache.commons.lang3.tuple.MutablePair;
29+
import org.apache.commons.lang3.tuple.Pair;
30+
import org.springframework.core.convert.converter.Converter;
31+
import uk.co.kleindelao.mapstruct.spring.SpringMapperConfig;
2732

28-
@SupportedAnnotationTypes(ConverterMapperProcessor.ORG_MAPSTRUCT_MAPPER)
33+
@SupportedAnnotationTypes({
34+
ConverterMapperProcessor.MAPPER,
35+
ConverterMapperProcessor.SPRING_MAPPER_CONFIG
36+
})
2937
public class ConverterMapperProcessor extends AbstractProcessor {
30-
protected static final String ORG_MAPSTRUCT_MAPPER = "org.mapstruct.Mapper";
38+
protected static final String MAPPER = "org.mapstruct.Mapper";
39+
protected static final String SPRING_MAPPER_CONFIG =
40+
"uk.co.kleindelao.mapstruct.spring.SpringMapperConfig";
41+
42+
private final ConversionServiceAdapterGenerator adapterGenerator;
3143

32-
private final ConversionServiceBridgeGenerator bridgeGenerator =
33-
new ConversionServiceBridgeGenerator(Clock.systemUTC());
44+
public ConverterMapperProcessor() {
45+
this(new ConversionServiceAdapterGenerator(Clock.systemUTC()));
46+
}
47+
48+
ConverterMapperProcessor(final ConversionServiceAdapterGenerator adapterGenerator) {
49+
super();
50+
this.adapterGenerator = adapterGenerator;
51+
}
52+
53+
@Override
54+
public SourceVersion getSupportedSourceVersion() {
55+
return SourceVersion.latestSupported();
56+
}
3457

3558
@Override
3659
public boolean process(
3760
final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
38-
final Types typeUtils = processingEnv.getTypeUtils();
39-
final ConversionServiceBridgeDescriptorBuilder descriptorBuilder =
40-
ConversionServiceBridgeDescriptor.builder()
41-
.bridgeClassName(
42-
ClassName.get(
43-
ConverterMapperProcessor.class.getPackage().getName(),
44-
"ConversionServiceBridge"));
45-
for (final TypeElement annotation : annotations) {
46-
if (ORG_MAPSTRUCT_MAPPER.contentEquals(annotation.getQualifiedName())) {
61+
final ConversionServiceAdapterDescriptor descriptor = new ConversionServiceAdapterDescriptor();
62+
final Pair<String, String> adapterPackageAndClass =
63+
getAdapterPackageAndClassName(annotations, roundEnv);
64+
descriptor.setAdapterClassName(
65+
ClassName.get(adapterPackageAndClass.getLeft(), adapterPackageAndClass.getRight()));
66+
annotations.stream()
67+
.filter(annotation -> MAPPER.contentEquals(annotation.getQualifiedName()))
68+
.forEach(
69+
annotation ->
70+
processMapperAnnotation(roundEnv, descriptor, adapterPackageAndClass, annotation));
71+
return false;
72+
}
73+
74+
private void processMapperAnnotation(
75+
final RoundEnvironment roundEnv,
76+
final ConversionServiceAdapterDescriptor descriptor,
77+
final Pair<String, String> adapterPackageAndClass,
78+
final TypeElement annotation) {
79+
final List<Pair<ClassName, ClassName>> fromToMappings =
4780
roundEnv.getElementsAnnotatedWith(annotation).stream()
4881
.filter(mapper -> mapper.asType().getKind() == DECLARED)
4982
.filter(mapper -> getConverterSupertype(mapper).isPresent())
50-
.forEach(
51-
mapper ->
52-
mapper.getEnclosedElements().stream()
53-
.filter(element -> element.getKind() == METHOD)
54-
.filter(method -> method.getModifiers().contains(PUBLIC))
55-
.filter(method -> method.getSimpleName().contentEquals("convert"))
56-
.filter(
57-
convert -> ((ExecutableElement) convert).getParameters().size() == 1)
58-
.filter(
59-
convert ->
60-
typeUtils.isSameType(
61-
getFirstParameterType((ExecutableElement) convert),
62-
getFirstTypeArgument(getConverterSupertype(mapper).get())))
63-
.forEach(
64-
convert ->
65-
descriptorBuilder.fromToMapping(
66-
Pair.of(
67-
(ClassName)
68-
((ExecutableElement) convert)
69-
.getParameters().stream()
70-
.map(Element::asType)
71-
.map(ClassName::get)
72-
.findFirst()
73-
.get(),
74-
(ClassName)
75-
ClassName.get(
76-
((ExecutableElement) convert).getReturnType())))));
77-
try (final Writer outputWriter =
78-
processingEnv
79-
.getFiler()
80-
.createSourceFile(
81-
ConverterMapperProcessor.class.getPackage().getName() + ".ConversionServiceBridge")
82-
.openWriter()) {
83-
bridgeGenerator.writeConversionServiceBridge(descriptorBuilder.build(), outputWriter);
84-
} catch (IOException e) {
85-
processingEnv
86-
.getMessager()
87-
.printMessage(
88-
ERROR, "Error while opening ConversionServiceBridge output file: " + e.getMessage());
89-
}
83+
.map(this::toConvertMethod)
84+
.filter(Objects::nonNull)
85+
.map(ExecutableElement.class::cast)
86+
.map(this::toFromToMapping)
87+
.collect(toList());
88+
descriptor.setFromToMappings(fromToMappings);
89+
writeAdapterClassFile(descriptor, adapterPackageAndClass);
90+
}
91+
92+
private Pair<ClassName, ClassName> toFromToMapping(final ExecutableElement convert) {
93+
return Pair.of(
94+
(ClassName)
95+
convert.getParameters().stream()
96+
.map(Element::asType)
97+
.map(ClassName::get)
98+
.findFirst()
99+
.get(),
100+
(ClassName) ClassName.get(convert.getReturnType()));
101+
}
102+
103+
private Element toConvertMethod(final Element mapper) {
104+
return mapper.getEnclosedElements().stream()
105+
.filter(element -> element.getKind() == METHOD)
106+
.filter(method -> method.getModifiers().contains(PUBLIC))
107+
.filter(method -> method.getSimpleName().contentEquals("convert"))
108+
.filter(convert -> ((ExecutableElement) convert).getParameters().size() == 1)
109+
.filter(
110+
convert ->
111+
processingEnv
112+
.getTypeUtils()
113+
.isSameType(
114+
getFirstParameterType((ExecutableElement) convert),
115+
getFirstTypeArgument(getConverterSupertype(mapper).get())))
116+
.findFirst()
117+
.orElse(null);
118+
}
119+
120+
private void writeAdapterClassFile(
121+
final ConversionServiceAdapterDescriptor descriptor,
122+
final Pair<String, String> adapterPackageAndClass) {
123+
try (final Writer outputWriter =
124+
processingEnv
125+
.getFiler()
126+
.createSourceFile(
127+
adapterPackageAndClass.getLeft() + "." + adapterPackageAndClass.getRight())
128+
.openWriter()) {
129+
adapterGenerator.writeConversionServiceAdapter(descriptor, outputWriter);
130+
} catch (IOException e) {
131+
processingEnv
132+
.getMessager()
133+
.printMessage(
134+
ERROR,
135+
"Error while opening "
136+
+ adapterPackageAndClass.getRight()
137+
+ " output file: "
138+
+ e.getMessage());
139+
}
140+
}
141+
142+
private Pair<String, String> getAdapterPackageAndClassName(
143+
final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
144+
final MutablePair<String, String> packageAndClass =
145+
MutablePair.of(
146+
ConverterMapperProcessor.class.getPackage().getName(), "ConversionServiceAdapter");
147+
for (final TypeElement annotation : annotations) {
148+
if (SPRING_MAPPER_CONFIG.contentEquals(annotation.getQualifiedName())) {
149+
roundEnv
150+
.getElementsAnnotatedWith(annotation)
151+
.forEach(element -> updateFromDeclaration(element, packageAndClass));
90152
}
91153
}
92-
return false;
154+
return packageAndClass;
155+
}
156+
157+
private void updateFromDeclaration(
158+
final Element element, final MutablePair<String, String> adapterPackageAndClass) {
159+
final SpringMapperConfig springMapperConfig = element.getAnnotation(SpringMapperConfig.class);
160+
adapterPackageAndClass.setLeft(
161+
Optional.of(springMapperConfig.conversionServiceAdapterPackage())
162+
.filter(StringUtils::isNotBlank)
163+
.orElse(
164+
String.valueOf(
165+
processingEnv.getElementUtils().getPackageOf(element).getQualifiedName())));
166+
adapterPackageAndClass.setRight(springMapperConfig.conversionServiceAdapterClassName());
93167
}
94168

95169
private Optional<? extends TypeMirror> getConverterSupertype(final Element mapper) {

0 commit comments

Comments
 (0)