Skip to content

Commit 74f281f

Browse files
sjaakdfiliphr
authored andcommitted
mapstruct#2135 improved messages for not able to select qualified method (mapstruct#2141)
* mapstruct#2135 improved messages for not able to select qualified method
1 parent c0d88f8 commit 74f281f

8 files changed

Lines changed: 302 additions & 10 deletions

File tree

processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
import java.util.List;
1111
import java.util.Set;
1212
import java.util.function.Supplier;
13+
import java.util.stream.Collectors;
1314
import javax.lang.model.element.AnnotationMirror;
15+
import javax.lang.model.element.Element;
1416
import javax.lang.model.element.ExecutableElement;
17+
import javax.lang.model.element.Name;
1518
import javax.lang.model.element.TypeElement;
1619
import javax.lang.model.type.DeclaredType;
1720
import javax.lang.model.type.ExecutableType;
@@ -48,6 +51,7 @@
4851
import org.mapstruct.ap.internal.util.Collections;
4952
import org.mapstruct.ap.internal.util.FormattingMessager;
5053
import org.mapstruct.ap.internal.util.Message;
54+
import org.mapstruct.ap.internal.util.MessageConstants;
5155
import org.mapstruct.ap.internal.util.NativeTypes;
5256
import org.mapstruct.ap.internal.util.Strings;
5357

@@ -260,13 +264,7 @@ && allowDirect( sourceType, targetType ) ) {
260264
}
261265

262266
if ( hasQualfiers() ) {
263-
messager.printMessage(
264-
mappingMethod.getExecutable(),
265-
positionHint,
266-
Message.GENERAL_NO_QUALIFYING_METHOD,
267-
Strings.join( selectionCriteria.getQualifiers(), ", " ),
268-
Strings.join( selectionCriteria.getQualifiedByNames(), ", " )
269-
);
267+
printQualifierMessage( selectionCriteria );
270268
}
271269
else if ( allowMappingMethod() ) {
272270
// only forge if we would allow mapping method
@@ -281,6 +279,46 @@ private boolean hasQualfiers() {
281279
return selectionCriteria != null && selectionCriteria.hasQualfiers();
282280
}
283281

282+
private void printQualifierMessage(SelectionCriteria selectionCriteria ) {
283+
284+
List<String> annotations = selectionCriteria.getQualifiers().stream()
285+
.filter( DeclaredType.class::isInstance )
286+
.map( DeclaredType.class::cast )
287+
.map( DeclaredType::asElement )
288+
.map( Element::getSimpleName )
289+
.map( Name::toString )
290+
.map( a -> "@" + a )
291+
.collect( Collectors.toList() );
292+
List<String> names = selectionCriteria.getQualifiedByNames();
293+
294+
if ( !annotations.isEmpty() && !names.isEmpty() ) {
295+
messager.printMessage(
296+
mappingMethod.getExecutable(),
297+
positionHint,
298+
Message.GENERAL_NO_QUALIFYING_METHOD_COMBINED,
299+
Strings.join( names, MessageConstants.AND ),
300+
Strings.join( annotations, MessageConstants.AND )
301+
);
302+
}
303+
else if ( !annotations.isEmpty() ) {
304+
messager.printMessage(
305+
mappingMethod.getExecutable(),
306+
positionHint,
307+
Message.GENERAL_NO_QUALIFYING_METHOD_ANNOTATION,
308+
Strings.join( annotations, MessageConstants.AND )
309+
);
310+
}
311+
else {
312+
messager.printMessage(
313+
mappingMethod.getExecutable(),
314+
positionHint,
315+
Message.GENERAL_NO_QUALIFYING_METHOD_NAMED,
316+
Strings.join( names, MessageConstants.AND )
317+
);
318+
}
319+
320+
}
321+
284322
private boolean allowDirect( Type sourceType, Type targetType ) {
285323
if ( selectionCriteria != null && selectionCriteria.isAllowDirect() ) {
286324
return true;

processor/src/main/java/org/mapstruct/ap/internal/util/Message.java

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

88
import javax.tools.Diagnostic;
99

10+
import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_QUALIFIER_URL;
11+
1012
/**
1113
* A message used in warnings/errors raised by the annotation processor.
1214
*
@@ -119,7 +121,9 @@ public enum Message {
119121
GENERAL_JODA_NOT_ON_CLASSPATH( "Cannot validate Joda dateformat, no Joda on classpath. Consider adding Joda to the annotation processorpath.", Diagnostic.Kind.WARNING ),
120122
GENERAL_NOT_ALL_FORGED_CREATED( "Internal Error in creation of Forged Methods, it was expected all Forged Methods to finished with creation, but %s did not" ),
121123
GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible constructor." ),
122-
GENERAL_NO_QUALIFYING_METHOD( "No qualifying method found for qualifiers: %s and / or qualifying names: %s" ),
124+
GENERAL_NO_QUALIFYING_METHOD_ANNOTATION( "Qualifier error. No method found annotated with: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ),
125+
GENERAL_NO_QUALIFYING_METHOD_NAMED( "Qualifier error. No method found annotated with @Named#value: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ),
126+
GENERAL_NO_QUALIFYING_METHOD_COMBINED( "Qualifier error. No method found annotated with @Named#value: [ %s ], annotated with [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ),
123127

124128
BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ),
125129
BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ),
@@ -162,6 +166,7 @@ public enum Message {
162166
VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." );
163167
// CHECKSTYLE:ON
164168

169+
165170
private final String description;
166171
private final Diagnostic.Kind kind;
167172

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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.util;
7+
8+
public final class MessageConstants {
9+
10+
public static final String AND = " and ";
11+
public static final String FAQ_QUALIFIER_URL = "https://mapstruct.org/faq/#qualifier";
12+
13+
private MessageConstants() {
14+
}
15+
}

processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ public void shouldMatchClassAndMethod() {
103103
kind = Kind.ERROR,
104104
line = 28,
105105
message =
106-
"No qualifying method found for qualifiers: org.mapstruct.ap.test.selection.qualifier.annotation" +
107-
".NonQualifierAnnotated and / or qualifying names: "),
106+
"Qualifier error. No method found annotated with: [ @NonQualifierAnnotated ]. " +
107+
"See https://mapstruct.org/faq/#qualifier for more info."),
108108
@Diagnostic(
109109
type = ErroneousMapper.class,
110110
kind = Kind.ERROR,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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.test.selection.qualifier.errors;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
12+
import org.mapstruct.Mapper;
13+
import org.mapstruct.Mapping;
14+
import org.mapstruct.Qualifier;
15+
16+
@Mapper
17+
public interface ErroneousMessageByAnnotationAndNamedMapper {
18+
19+
@Mapping(target = "nested", source = "value", qualifiedBy = {
20+
SelectMe1.class,
21+
SelectMe2.class
22+
}, qualifiedByName = { "selectMe1", "selectMe2" })
23+
Target map(Source source);
24+
25+
default Nested map(String in) {
26+
return null;
27+
}
28+
29+
// CHECKSTYLE:OFF
30+
class Source {
31+
public String value;
32+
}
33+
34+
class Target {
35+
public Nested nested;
36+
}
37+
38+
class Nested {
39+
public String value;
40+
}
41+
// CHECKSTYLE ON
42+
43+
@Qualifier
44+
@java.lang.annotation.Target(ElementType.METHOD)
45+
@Retention(RetentionPolicy.CLASS)
46+
public @interface SelectMe1 {
47+
}
48+
49+
@Qualifier
50+
@java.lang.annotation.Target(ElementType.METHOD)
51+
@Retention(RetentionPolicy.CLASS)
52+
public @interface SelectMe2 {
53+
}
54+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.test.selection.qualifier.errors;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
12+
import org.mapstruct.Mapper;
13+
import org.mapstruct.Mapping;
14+
import org.mapstruct.Qualifier;
15+
16+
@Mapper
17+
public interface ErroneousMessageByAnnotationMapper {
18+
19+
@Mapping(target = "nested", source = "value", qualifiedBy = SelectMe.class)
20+
Target map(Source source);
21+
22+
default Nested map(String in) {
23+
return null;
24+
}
25+
26+
// CHECKSTYLE:OFF
27+
class Source {
28+
public String value;
29+
}
30+
31+
class Target {
32+
public Nested nested;
33+
}
34+
35+
class Nested {
36+
public String value;
37+
}
38+
// CHECKSTYLE ON
39+
40+
@Qualifier
41+
@java.lang.annotation.Target(ElementType.METHOD)
42+
@Retention(RetentionPolicy.CLASS)
43+
public @interface SelectMe {
44+
}
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.test.selection.qualifier.errors;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
12+
import org.mapstruct.Mapper;
13+
import org.mapstruct.Mapping;
14+
import org.mapstruct.Qualifier;
15+
16+
@Mapper
17+
public interface ErroneousMessageByNamedMapper {
18+
19+
@Mapping(target = "nested", source = "value", qualifiedByName = "SelectMe")
20+
Target map(Source source);
21+
22+
default Nested map(String in) {
23+
return null;
24+
}
25+
26+
// CHECKSTYLE:OFF
27+
class Source {
28+
public String value;
29+
}
30+
31+
class Target {
32+
public Nested nested;
33+
}
34+
35+
class Nested {
36+
public String value;
37+
}
38+
// CHECKSTYLE ON
39+
40+
@Qualifier
41+
@java.lang.annotation.Target(ElementType.METHOD)
42+
@Retention(RetentionPolicy.CLASS)
43+
public @interface SelectMe {
44+
}
45+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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.test.selection.qualifier.errors;
7+
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.mapstruct.ap.testutil.IssueKey;
11+
import org.mapstruct.ap.testutil.WithClasses;
12+
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
13+
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
14+
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
15+
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
16+
17+
import static javax.tools.Diagnostic.Kind.ERROR;
18+
19+
@IssueKey("2135")
20+
@RunWith(AnnotationProcessorTestRunner.class)
21+
public class QualfierMessageTest {
22+
23+
@Test
24+
@WithClasses(ErroneousMessageByAnnotationMapper.class)
25+
@ExpectedCompilationOutcome(
26+
value = CompilationResult.FAILED,
27+
diagnostics = {
28+
@Diagnostic(
29+
type = ErroneousMessageByAnnotationMapper.class,
30+
kind = ERROR,
31+
line = 19,
32+
message = "Qualifier error. No method found annotated with: [ @SelectMe ]. " +
33+
"See https://mapstruct.org/faq/#qualifier for more info."),
34+
@Diagnostic(
35+
type = ErroneousMessageByAnnotationMapper.class,
36+
kind = ERROR,
37+
line = 19,
38+
messageRegExp = "Can't map property.*")
39+
40+
}
41+
)
42+
public void testNoQualifyingMethodByAnnotationFound() {
43+
}
44+
45+
@Test
46+
@WithClasses(ErroneousMessageByNamedMapper.class)
47+
@ExpectedCompilationOutcome(
48+
value = CompilationResult.FAILED,
49+
diagnostics = {
50+
@Diagnostic(
51+
type = ErroneousMessageByNamedMapper.class,
52+
kind = ERROR,
53+
line = 19,
54+
message = "Qualifier error. No method found annotated with @Named#value: [ SelectMe ]. " +
55+
"See https://mapstruct.org/faq/#qualifier for more info."),
56+
@Diagnostic(
57+
type = ErroneousMessageByNamedMapper.class,
58+
kind = ERROR,
59+
line = 19,
60+
messageRegExp = "Can't map property.*")
61+
62+
}
63+
)
64+
public void testNoQualifyingMethodByNamedFound() {
65+
}
66+
67+
@Test
68+
@WithClasses(ErroneousMessageByAnnotationAndNamedMapper.class)
69+
@ExpectedCompilationOutcome(
70+
value = CompilationResult.FAILED,
71+
diagnostics = {
72+
@Diagnostic(
73+
type = ErroneousMessageByAnnotationAndNamedMapper.class,
74+
kind = ERROR,
75+
line = 19,
76+
message =
77+
"Qualifier error. No method found annotated with @Named#value: [ selectMe1 and selectMe2 ], " +
78+
"annotated with [ @SelectMe1 and @SelectMe2 ]. " +
79+
"See https://mapstruct.org/faq/#qualifier for more info."),
80+
@Diagnostic(
81+
type = ErroneousMessageByAnnotationAndNamedMapper.class,
82+
kind = ERROR,
83+
line = 19,
84+
messageRegExp = "Can't map property.*")
85+
86+
}
87+
)
88+
public void testNoQualifyingMethodByAnnotationAndNamedFound() {
89+
}
90+
}

0 commit comments

Comments
 (0)