Skip to content
This repository was archived by the owner on Jun 11, 2023. It is now read-only.

Commit 91c9723

Browse files
committed
<master> Merge branch 'feature/nested_field'
2 parents 358f54c + d5902ee commit 91c9723

File tree

9 files changed

+339
-52
lines changed

9 files changed

+339
-52
lines changed

processor/src/main/java/com/devindi/mapper/MapperGenerator.java

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.squareup.javapoet.ClassName;
44
import com.squareup.javapoet.MethodSpec;
5+
import com.squareup.javapoet.ParameterSpec;
6+
import com.squareup.javapoet.TypeName;
57
import com.squareup.javapoet.TypeSpec;
68

79
import java.util.ArrayList;
@@ -16,7 +18,6 @@
1618
import javax.lang.model.element.Modifier;
1719
import javax.lang.model.element.TypeElement;
1820
import javax.lang.model.element.VariableElement;
19-
import javax.lang.model.type.TypeKind;
2021
import javax.lang.model.type.TypeMirror;
2122
import javax.lang.model.util.ElementFilter;
2223
import javax.tools.Diagnostic;
@@ -34,12 +35,14 @@ static MapperGenerator create(TypeElement element, ProcessingEnvironment env) {
3435

3536
private final TypeElement element;
3637
private final ProcessingEnvironment processingEnv;
37-
private List<Mapping> mappings;
38+
private List<MappingInfo> mappings;
39+
private List<MappingInfo> autoMappings;
3840

3941
private MapperGenerator(TypeElement mapperElement, ProcessingEnvironment processingEnvironment) {
4042
this.processingEnv = processingEnvironment;
4143
this.element = mapperElement;
4244
mappings = new ArrayList<>();
45+
autoMappings = new ArrayList<>();
4346
}
4447

4548
public TypeSpec generate() {
@@ -49,85 +52,93 @@ public TypeSpec generate() {
4952
.addModifiers(Modifier.PUBLIC)
5053
.addSuperinterface(ClassName.get(element));
5154

52-
for (Mapping mapping : mappings) {
55+
for (MappingInfo mapping : mappings) {
5356
MethodSpec methodSpec = generateMappingMethod(mapping);
5457
implBuilder.addMethod(methodSpec);
5558
}
5659

60+
for (MappingInfo autoMapping : autoMappings) {
61+
MethodSpec methodSpec = generateMappingMethod(autoMapping);
62+
implBuilder.addMethod(methodSpec);
63+
}
64+
65+
5766
return implBuilder.build();
5867
}
5968

6069
private void collectMappings() {
6170
for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
6271
try {
63-
mappings.add(new Mapping(method));
72+
mappings.add(new MappingInfo(method));
6473
} catch (IllegalArgumentException exc) {
6574
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, exc.getMessage(), method);
6675
}
6776
}
6877
}
6978

70-
private MethodSpec generateMappingMethod(Mapping mapping) {
71-
ExecutableElement constructorElement = getConstructorElement(mapping.target.toString());
79+
private MethodSpec generateMappingMethod(MappingInfo mapping) {
80+
ExecutableElement constructorElement = getConstructorElement(mapping.getTargetType().toString());
7281
List<? extends VariableElement> constructorParameters = constructorElement.getParameters();
7382

74-
Map<String, ExecutableElement> argumentGetters = getGetters(mapping.source.toString());
83+
Map<String, ExecutableElement> argumentGetters = getGetters(mapping.getSourceType().toString());
7584

7685

7786
if (constructorParameters.size() != argumentGetters.size()) {
78-
processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "Target constructor have different arguments count", mapping.method);
87+
processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "Target constructor have different arguments count", mapping.getMethod());
7988
}
8089

81-
MethodSpec.Builder methodBuilder = MethodSpec.overriding(mapping.method);
90+
91+
MethodSpec.Builder methodBuilder = createMethodBuilder(mapping);
8292
StringBuilder statementBuilder = new StringBuilder();
8393
statementBuilder
8494
.append("return new ")
85-
.append(mapping.target.toString())
95+
.append(mapping.getTargetType().toString())
8696
.append("(");
8797

8898
String separator = "";
8999
for (VariableElement constructorParameter : constructorParameters) {
90-
String sourceFieldName = constructorParameter.getSimpleName().toString().toLowerCase();
91-
com.devindi.mapper.Mapping annotation = mapping.method.getAnnotation(com.devindi.mapper.Mapping.class);
92-
if (annotation != null && annotation.target().toLowerCase().equals(constructorParameter.getSimpleName().toString().toLowerCase())) {
93-
sourceFieldName = annotation.source();
94-
}
95-
Mappings mappings = mapping.method.getAnnotation(Mappings.class);
96-
if (mappings != null) {
97-
for (com.devindi.mapper.Mapping fieldMapping : mappings.value()) {
98-
if (fieldMapping != null && fieldMapping.target().toLowerCase().equals(constructorParameter.getSimpleName().toString().toLowerCase())) {
99-
sourceFieldName = fieldMapping.source();
100-
}
101-
}
102-
}
100+
String sourceFieldName = mapping.getSourceFieldName(constructorParameter.getSimpleName().toString().toLowerCase());
103101
ExecutableElement getter = argumentGetters.get(sourceFieldName.toLowerCase());
104102
if (getter == null) {
105103
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to find getter at source for target field", constructorParameter);
106104
return null;
107105
}
108106
if (!constructorParameter.asType().equals(getter.getReturnType())) {
109107
//getter and constructor parameter have different types
110-
// TODO: 01.09.17 try to map/convert source field to target field
111-
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Getter return type and constructor argument type are different", mapping.method);
108+
MappingInfo depMapping = findMapping(getter.getReturnType(), constructorParameter.asType());
109+
if (depMapping == null) {
110+
depMapping = new MappingInfo(getter.getReturnType(), constructorParameter.asType());
111+
autoMappings.add(depMapping);
112+
}
113+
statementBuilder
114+
.append(separator)
115+
.append('\n')
116+
.append("this.")
117+
.append(depMapping.getMethodName())
118+
.append("(")
119+
.append(mapping.getSourceName())
120+
.append(".")
121+
.append(getter.getSimpleName())
122+
.append("()")
123+
.append(")");
124+
separator = ",";
125+
} else {
126+
statementBuilder
127+
.append(separator)
128+
.append('\n')
129+
.append(mapping.getSourceName())
130+
.append(".")
131+
.append(getter.getSimpleName())
132+
.append("()");
133+
separator = ",";
112134
}
113-
114-
statementBuilder
115-
.append(separator)
116-
.append('\n')
117-
.append(mapping.method.getParameters().get(0).getSimpleName())
118-
.append(".")
119-
.append(getter.getSimpleName())
120-
.append("()");
121-
separator = ",";
122135
}
123136

124137
statementBuilder
125138
.append('\n')
126139
.append(")");
127140
methodBuilder.addStatement(statementBuilder.toString());
128141

129-
130-
131142
return methodBuilder.build();
132143
}
133144

@@ -151,24 +162,24 @@ private Map<String, ExecutableElement> getGetters(String className) {
151162
return gettersMap;
152163
}
153164

154-
private static class Mapping {
155-
private final TypeMirror source;
156-
private final TypeMirror target;
157-
private final ExecutableElement method;
158-
159-
public Mapping(ExecutableElement method) {
160-
this.method = method;
161-
target = method.getReturnType();
162-
if (target.getKind().equals(TypeKind.VOID)) {
163-
throw new IllegalArgumentException("Mapper method should return value. Method will be ignored");
165+
private MappingInfo findMapping(TypeMirror source, TypeMirror target) {
166+
for (MappingInfo mapping : mappings) {
167+
if (mapping.getSourceType().equals(source) && mapping.getTargetType().equals(target)) {
168+
return mapping;
164169
}
165-
List<? extends VariableElement> parameters = method.getParameters();
166-
if (parameters.size() != 1) {
167-
throw new IllegalArgumentException("Mapper method should have only 1 parameter. Method will be ignored");
168-
}
169-
source = parameters.get(0).asType();
170170
}
171+
return null;
171172
}
172173

173-
174+
private MethodSpec.Builder createMethodBuilder(MappingInfo info) {
175+
ExecutableElement method = info.getMethod();
176+
if (method == null) {
177+
MethodSpec.Builder builder = MethodSpec.methodBuilder(info.getMethodName().toString());
178+
builder.addModifiers(Modifier.PRIVATE);
179+
builder.returns(TypeName.get(info.getTargetType()));
180+
builder.addParameter(ParameterSpec.builder(TypeName.get(info.getSourceType()), "source").build());
181+
return builder;
182+
}
183+
return MethodSpec.overriding(method);
184+
}
174185
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.devindi.mapper;
2+
3+
import java.util.List;
4+
5+
import javax.lang.model.element.ExecutableElement;
6+
import javax.lang.model.element.Name;
7+
import javax.lang.model.element.VariableElement;
8+
import javax.lang.model.type.TypeKind;
9+
import javax.lang.model.type.TypeMirror;
10+
11+
public class MappingInfo {
12+
13+
private final TypeMirror sourceType;
14+
private final TypeMirror targetType;
15+
private final ExecutableElement method;
16+
private final CharSequence sourceName;
17+
private final CharSequence mappingName;
18+
19+
public MappingInfo(ExecutableElement method) {
20+
targetType = method.getReturnType();
21+
if (targetType.getKind().equals(TypeKind.VOID)) {
22+
throw new IllegalArgumentException("Mapper method should return value. Method will be ignored");
23+
}
24+
List<? extends VariableElement> parameters = method.getParameters();
25+
if (parameters.size() != 1) {
26+
throw new IllegalArgumentException("Mapper method should have only 1 parameter. Method will be ignored");
27+
}
28+
sourceType = parameters.get(0).asType();
29+
sourceName = parameters.get(0).getSimpleName();
30+
mappingName = method.getSimpleName();
31+
this.method = method;
32+
}
33+
34+
public MappingInfo(TypeMirror source, TypeMirror target) {
35+
sourceType = source;
36+
targetType = target;
37+
this.sourceName = "source";
38+
mappingName = "convert";
39+
method = null;
40+
}
41+
42+
public TypeMirror getSourceType() {
43+
return sourceType;
44+
}
45+
46+
public TypeMirror getTargetType() {
47+
return targetType;
48+
}
49+
50+
public ExecutableElement getMethod() {
51+
return method;
52+
}
53+
54+
public CharSequence getMethodName() {
55+
return mappingName;
56+
}
57+
58+
public CharSequence getSourceName() {
59+
return sourceName;
60+
}
61+
62+
public String getSourceFieldName(String defaultName) {
63+
if (method == null) {
64+
return defaultName;
65+
}
66+
com.devindi.mapper.Mapping annotation = method.getAnnotation(com.devindi.mapper.Mapping.class);
67+
if (annotation != null && annotation.target().toLowerCase().equals(defaultName)) {
68+
return annotation.source();
69+
}
70+
Mappings mappings = method.getAnnotation(Mappings.class);
71+
if (mappings != null) {
72+
for (com.devindi.mapper.Mapping fieldMapping : mappings.value()) {
73+
if (fieldMapping != null && fieldMapping.target().toLowerCase().equals(defaultName)) {
74+
return fieldMapping.source();
75+
}
76+
}
77+
}
78+
return defaultName;
79+
}
80+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.example.nested;
2+
3+
import com.devindi.mapper.Mapper;
4+
import com.devindi.mapper.Mapping;
5+
6+
@Mapper
7+
public interface AutoDriverMapper {
8+
@Mapping(source = "license", target = "licenseDto")
9+
DriverDto toDto(Driver d);
10+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.example.nested;
2+
3+
public class Driver {
4+
5+
private final String name;
6+
private final int age;
7+
private final DriverLicense license;
8+
9+
public Driver(int age, String name, DriverLicense license) {
10+
this.name = name;
11+
this.age = age;
12+
this.license = license;
13+
}
14+
15+
public String getName() {
16+
return name;
17+
}
18+
19+
public int getAge() {
20+
return age;
21+
}
22+
23+
public DriverLicense getLicense() {
24+
return license;
25+
}
26+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.example.nested;
2+
3+
public class DriverDto {
4+
5+
private String name;
6+
private int age;
7+
private LicenseDto licenseDto;
8+
9+
public DriverDto(String name, int age, LicenseDto licenseDto) {
10+
this.name = name;
11+
this.age = age;
12+
this.licenseDto = licenseDto;
13+
}
14+
15+
public String getName() {
16+
return name;
17+
}
18+
19+
public void setName(String name) {
20+
this.name = name;
21+
}
22+
23+
public int getAge() {
24+
return age;
25+
}
26+
27+
public void setAge(int age) {
28+
this.age = age;
29+
}
30+
31+
public LicenseDto getLicenseDto() {
32+
return licenseDto;
33+
}
34+
35+
public void setLicenseDto(LicenseDto licenseDto) {
36+
this.licenseDto = licenseDto;
37+
}
38+
39+
@Override
40+
public String toString() {
41+
return "DriverDto{" +
42+
"name='" + name + '\'' +
43+
", age=" + age +
44+
", licenseDto=" + licenseDto +
45+
'}';
46+
}
47+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.example.nested;
2+
3+
import java.util.Date;
4+
5+
public class DriverLicense {
6+
private final String id;
7+
private final String photoUrl;
8+
private final Date validUntil;
9+
10+
public DriverLicense(String id, String photoUrl, Date validUntil) {
11+
this.id = id;
12+
this.photoUrl = photoUrl;
13+
this.validUntil = validUntil;
14+
}
15+
16+
public String getId() {
17+
return id;
18+
}
19+
20+
public String getPhotoUrl() {
21+
return photoUrl;
22+
}
23+
24+
public Date getValidUntil() {
25+
return validUntil;
26+
}
27+
}

0 commit comments

Comments
 (0)