Skip to content

Commit a2e1404

Browse files
committed
Refactor presence checks to object in order to simplify the conditional mapping
1 parent 1c8fff1 commit a2e1404

16 files changed

Lines changed: 309 additions & 29 deletions

processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.mapstruct.ap.internal.model.common.ModelElement;
2020
import org.mapstruct.ap.internal.model.common.Parameter;
2121
import org.mapstruct.ap.internal.model.common.ParameterBinding;
22+
import org.mapstruct.ap.internal.model.common.PresenceCheck;
2223
import org.mapstruct.ap.internal.model.common.Type;
2324
import org.mapstruct.ap.internal.model.source.Method;
2425
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod;
@@ -197,7 +198,7 @@ public String getSourceReference() {
197198
}
198199

199200
@Override
200-
public String getSourcePresenceCheckerReference() {
201+
public PresenceCheck getSourcePresenceCheckerReference() {
201202
return assignment.getSourcePresenceCheckerReference();
202203
}
203204

processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
import java.util.Set;
1212

1313
import org.mapstruct.ap.internal.model.common.Parameter;
14+
import org.mapstruct.ap.internal.model.common.PresenceCheck;
1415
import org.mapstruct.ap.internal.model.common.Type;
1516
import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry;
17+
import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck;
1618
import org.mapstruct.ap.internal.util.Strings;
1719
import org.mapstruct.ap.internal.util.ValueProvider;
1820

@@ -52,17 +54,27 @@ public Builder mappingContext(MappingBuilderContext mappingContext) {
5254

5355
public NestedPropertyMappingMethod build() {
5456
List<String> existingVariableNames = new ArrayList<>();
57+
Parameter sourceParameter = null;
5558
for ( Parameter parameter : method.getSourceParameters() ) {
5659
existingVariableNames.add( parameter.getName() );
60+
if ( sourceParameter == null && !parameter.isMappingTarget() && !parameter.isMappingContext() ) {
61+
sourceParameter = parameter;
62+
}
5763
}
5864
final List<Type> thrownTypes = new ArrayList<>();
5965
List<SafePropertyEntry> safePropertyEntries = new ArrayList<>();
66+
if ( sourceParameter == null ) {
67+
throw new IllegalStateException( "Method " + method + " has no source parameter." );
68+
}
69+
70+
String previousPropertyName = sourceParameter.getName();
6071
for ( PropertyEntry propertyEntry : propertyEntries ) {
6172
String safeName = Strings.getSafeVariableName( propertyEntry.getName(), existingVariableNames );
62-
safePropertyEntries.add( new SafePropertyEntry( propertyEntry, safeName ) );
73+
safePropertyEntries.add( new SafePropertyEntry( propertyEntry, safeName, previousPropertyName ) );
6374
existingVariableNames.add( safeName );
6475
thrownTypes.addAll( ctx.getTypeFactory().getThrownTypes(
6576
propertyEntry.getReadAccessor() ) );
77+
previousPropertyName = safeName;
6678
}
6779
method.addThrownTypes( thrownTypes );
6880
return new NestedPropertyMappingMethod( method, safePropertyEntries );
@@ -92,6 +104,9 @@ public Set<Type> getImportTypes() {
92104
Set<Type> types = super.getImportTypes();
93105
for ( SafePropertyEntry propertyEntry : safePropertyEntries) {
94106
types.add( propertyEntry.getType() );
107+
if ( propertyEntry.getPresenceChecker() != null ) {
108+
types.addAll( propertyEntry.getPresenceChecker().getImportTypes() );
109+
}
95110
}
96111
return types;
97112
}
@@ -143,18 +158,23 @@ public static class SafePropertyEntry {
143158

144159
private final String safeName;
145160
private final String readAccessorName;
146-
private final String presenceCheckerName;
161+
private final PresenceCheck presenceChecker;
162+
private final String previousPropertyName;
147163
private final Type type;
148164

149-
public SafePropertyEntry(PropertyEntry entry, String safeName) {
165+
public SafePropertyEntry(PropertyEntry entry, String safeName, String previousPropertyName) {
150166
this.safeName = safeName;
151167
this.readAccessorName = ValueProvider.of( entry.getReadAccessor() ).getValue();
152168
if ( entry.getPresenceChecker() != null ) {
153-
this.presenceCheckerName = entry.getPresenceChecker().getSimpleName();
169+
this.presenceChecker = new SourceReferenceMethodPresenceCheck(
170+
previousPropertyName,
171+
entry.getPresenceChecker().getSimpleName()
172+
);
154173
}
155174
else {
156-
this.presenceCheckerName = null;
175+
this.presenceChecker = null;
157176
}
177+
this.previousPropertyName = previousPropertyName;
158178
this.type = entry.getType();
159179
}
160180

@@ -166,8 +186,12 @@ public String getAccessorName() {
166186
return readAccessorName;
167187
}
168188

169-
public String getPresenceCheckerName() {
170-
return presenceCheckerName;
189+
public PresenceCheck getPresenceChecker() {
190+
return presenceChecker;
191+
}
192+
193+
public String getPreviousPropertyName() {
194+
return previousPropertyName;
171195
}
172196

173197
public Type getType() {
@@ -189,7 +213,11 @@ public boolean equals(Object o) {
189213
return false;
190214
}
191215

192-
if ( !Objects.equals( presenceCheckerName, that.presenceCheckerName ) ) {
216+
if ( !Objects.equals( presenceChecker, that.presenceChecker ) ) {
217+
return false;
218+
}
219+
220+
if ( !Objects.equals( previousPropertyName, that.previousPropertyName ) ) {
193221
return false;
194222
}
195223

@@ -203,7 +231,7 @@ public boolean equals(Object o) {
203231
@Override
204232
public int hashCode() {
205233
int result = readAccessorName != null ? readAccessorName.hashCode() : 0;
206-
result = 31 * result + ( presenceCheckerName != null ? presenceCheckerName.hashCode() : 0 );
234+
result = 31 * result + ( presenceChecker != null ? presenceChecker.hashCode() : 0 );
207235
result = 31 * result + ( type != null ? type.hashCode() : 0 );
208236
return result;
209237
}

processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@
2828
import org.mapstruct.ap.internal.model.common.FormattingParameters;
2929
import org.mapstruct.ap.internal.model.common.ModelElement;
3030
import org.mapstruct.ap.internal.model.common.Parameter;
31+
import org.mapstruct.ap.internal.model.common.PresenceCheck;
3132
import org.mapstruct.ap.internal.model.common.SourceRHS;
3233
import org.mapstruct.ap.internal.model.common.Type;
34+
import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck;
35+
import org.mapstruct.ap.internal.model.presence.NullPresenceCheck;
36+
import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck;
3337
import org.mapstruct.ap.internal.model.source.DelegatingOptions;
3438
import org.mapstruct.ap.internal.model.source.MappingControl;
3539
import org.mapstruct.ap.internal.model.source.MappingOptions;
@@ -609,30 +613,43 @@ else if ( !sourceReference.isNested() ) {
609613
}
610614
}
611615

612-
private String getSourcePresenceCheckerRef( SourceReference sourceReference ) {
613-
String sourcePresenceChecker = null;
616+
private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReference ) {
617+
PresenceCheck sourcePresenceChecker = null;
614618
if ( !sourceReference.getPropertyEntries().isEmpty() ) {
615619
Parameter sourceParam = sourceReference.getParameter();
616620
// TODO is first correct here?? shouldn't it be last since the remainer is checked
617621
// in the forged method?
618622
PropertyEntry propertyEntry = sourceReference.getShallowestProperty();
619623
if ( propertyEntry.getPresenceChecker() != null ) {
620-
sourcePresenceChecker = sourceParam.getName()
621-
+ "." + propertyEntry.getPresenceChecker().getSimpleName() + "()";
624+
List<PresenceCheck> presenceChecks = new ArrayList<>();
625+
presenceChecks.add( new SourceReferenceMethodPresenceCheck(
626+
sourceParam.getName(),
627+
propertyEntry.getPresenceChecker().getSimpleName()
628+
) );
622629

623630
String variableName = sourceParam.getName() + "."
624631
+ propertyEntry.getReadAccessor().getSimpleName() + "()";
625632
for (int i = 1; i < sourceReference.getPropertyEntries().size(); i++) {
626633
PropertyEntry entry = sourceReference.getPropertyEntries().get( i );
627634
if (entry.getPresenceChecker() != null && entry.getReadAccessor() != null) {
628-
sourcePresenceChecker += " && " + variableName + " != null && "
629-
+ variableName + "." + entry.getPresenceChecker().getSimpleName() + "()";
635+
presenceChecks.add( new NullPresenceCheck( variableName ) );
636+
presenceChecks.add( new SourceReferenceMethodPresenceCheck(
637+
variableName,
638+
entry.getPresenceChecker().getSimpleName()
639+
) );
630640
variableName = variableName + "." + entry.getReadAccessor().getSimpleName() + "()";
631641
}
632642
else {
633643
break;
634644
}
635645
}
646+
647+
if ( presenceChecks.size() == 1 ) {
648+
sourcePresenceChecker = presenceChecks.get( 0 );
649+
}
650+
else {
651+
sourcePresenceChecker = new AllPresenceChecksPresenceCheck( presenceChecks );
652+
}
636653
}
637654
}
638655
return sourcePresenceChecker;

processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.mapstruct.ap.internal.model.common.Assignment;
1313
import org.mapstruct.ap.internal.model.common.ModelElement;
14+
import org.mapstruct.ap.internal.model.common.PresenceCheck;
1415
import org.mapstruct.ap.internal.model.common.Type;
1516

1617
/**
@@ -79,7 +80,7 @@ public boolean isSourceReferenceParameter() {
7980
}
8081

8182
@Override
82-
public String getSourcePresenceCheckerReference() {
83+
public PresenceCheck getSourcePresenceCheckerReference() {
8384
return assignment.getSourcePresenceCheckerReference();
8485
}
8586

processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import org.mapstruct.ap.internal.model.common.Assignment;
1212
import org.mapstruct.ap.internal.model.common.ModelElement;
13+
import org.mapstruct.ap.internal.model.common.PresenceCheck;
1314
import org.mapstruct.ap.internal.model.common.Type;
1415

1516
/**
@@ -57,7 +58,7 @@ public boolean isSourceReferenceParameter() {
5758
}
5859

5960
@Override
60-
public String getSourcePresenceCheckerReference() {
61+
public PresenceCheck getSourcePresenceCheckerReference() {
6162
return decoratedAssignment.getSourcePresenceCheckerReference();
6263
}
6364

processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public boolean isConverted() {
9090
*
9191
* @return source reference
9292
*/
93-
String getSourcePresenceCheckerReference();
93+
PresenceCheck getSourcePresenceCheckerReference();
9494

9595
/**
9696
* the source type used in the matching process
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.internal.model.common;
7+
8+
import java.util.Set;
9+
10+
/**
11+
* Marker interface for presence checks.
12+
*
13+
* @author Filip Hrisafov
14+
*/
15+
public interface PresenceCheck {
16+
17+
/**
18+
* returns all types required as import by the presence check.
19+
*
20+
* @return imported types
21+
*/
22+
Set<Type> getImportTypes();
23+
24+
}

processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.Collection;
99
import java.util.Collections;
10+
import java.util.HashSet;
1011
import java.util.List;
1112
import java.util.Set;
1213
import java.util.stream.Stream;
@@ -30,7 +31,7 @@ public class SourceRHS extends ModelElement implements Assignment {
3031
private String sourceLoopVarName;
3132
private final Set<String> existingVariableNames;
3233
private final String sourceErrorMessagePart;
33-
private final String sourcePresenceCheckerReference;
34+
private final PresenceCheck sourcePresenceCheckerReference;
3435
private boolean useElementAsSourceTypeForMatching = false;
3536
private final String sourceParameterName;
3637

@@ -39,7 +40,7 @@ public SourceRHS(String sourceReference, Type sourceType, Set<String> existingVa
3940
this( sourceReference, sourceReference, null, sourceType, existingVariableNames, sourceErrorMessagePart );
4041
}
4142

42-
public SourceRHS(String sourceParameterName, String sourceReference, String sourcePresenceCheckerReference,
43+
public SourceRHS(String sourceParameterName, String sourceReference, PresenceCheck sourcePresenceCheckerReference,
4344
Type sourceType, Set<String> existingVariableNames, String sourceErrorMessagePart ) {
4445
this.sourceReference = sourceReference;
4546
this.sourceType = sourceType;
@@ -60,7 +61,7 @@ public boolean isSourceReferenceParameter() {
6061
}
6162

6263
@Override
63-
public String getSourcePresenceCheckerReference() {
64+
public PresenceCheck getSourcePresenceCheckerReference() {
6465
return sourcePresenceCheckerReference;
6566
}
6667

@@ -96,6 +97,10 @@ public void setSourceLoopVarName(String sourceLoopVarName) {
9697

9798
@Override
9899
public Set<Type> getImportTypes() {
100+
if ( sourcePresenceCheckerReference != null ) {
101+
return sourcePresenceCheckerReference.getImportTypes();
102+
}
103+
99104
return Collections.emptySet();
100105
}
101106

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.internal.model.presence;
7+
8+
import java.util.Collection;
9+
import java.util.Collections;
10+
import java.util.HashSet;
11+
import java.util.Objects;
12+
import java.util.Set;
13+
14+
import org.mapstruct.ap.internal.model.common.ModelElement;
15+
import org.mapstruct.ap.internal.model.common.PresenceCheck;
16+
import org.mapstruct.ap.internal.model.common.Type;
17+
18+
/**
19+
* @author Filip Hrisafov
20+
*/
21+
public class AllPresenceChecksPresenceCheck extends ModelElement implements PresenceCheck {
22+
23+
private final Collection<PresenceCheck> presenceChecks;
24+
25+
public AllPresenceChecksPresenceCheck(Collection<PresenceCheck> presenceChecks) {
26+
this.presenceChecks = presenceChecks;
27+
}
28+
29+
public Collection<PresenceCheck> getPresenceChecks() {
30+
return presenceChecks;
31+
}
32+
33+
@Override
34+
public Set<Type> getImportTypes() {
35+
Set<Type> importTypes = new HashSet<>();
36+
for ( PresenceCheck presenceCheck : presenceChecks ) {
37+
importTypes.addAll( presenceCheck.getImportTypes() );
38+
}
39+
40+
return importTypes;
41+
}
42+
43+
@Override
44+
public boolean equals(Object o) {
45+
if ( this == o ) {
46+
return true;
47+
}
48+
if ( o == null || getClass() != o.getClass() ) {
49+
return false;
50+
}
51+
AllPresenceChecksPresenceCheck that = (AllPresenceChecksPresenceCheck) o;
52+
return Objects.equals( presenceChecks, that.presenceChecks );
53+
}
54+
55+
@Override
56+
public int hashCode() {
57+
return Objects.hash( presenceChecks );
58+
}
59+
}

0 commit comments

Comments
 (0)