mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
* #2135 improved messages for not able to select qualified method
This commit is contained in:
parent
c0d88f86bf
commit
74f281fa3e
@ -10,8 +10,11 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.element.Name;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.lang.model.type.DeclaredType;
|
import javax.lang.model.type.DeclaredType;
|
||||||
import javax.lang.model.type.ExecutableType;
|
import javax.lang.model.type.ExecutableType;
|
||||||
@ -48,6 +51,7 @@ import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
|||||||
import org.mapstruct.ap.internal.util.Collections;
|
import org.mapstruct.ap.internal.util.Collections;
|
||||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
|
import org.mapstruct.ap.internal.util.MessageConstants;
|
||||||
import org.mapstruct.ap.internal.util.NativeTypes;
|
import org.mapstruct.ap.internal.util.NativeTypes;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
|
|
||||||
@ -260,13 +264,7 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( hasQualfiers() ) {
|
if ( hasQualfiers() ) {
|
||||||
messager.printMessage(
|
printQualifierMessage( selectionCriteria );
|
||||||
mappingMethod.getExecutable(),
|
|
||||||
positionHint,
|
|
||||||
Message.GENERAL_NO_QUALIFYING_METHOD,
|
|
||||||
Strings.join( selectionCriteria.getQualifiers(), ", " ),
|
|
||||||
Strings.join( selectionCriteria.getQualifiedByNames(), ", " )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else if ( allowMappingMethod() ) {
|
else if ( allowMappingMethod() ) {
|
||||||
// only forge if we would allow mapping method
|
// only forge if we would allow mapping method
|
||||||
@ -281,6 +279,46 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
return selectionCriteria != null && selectionCriteria.hasQualfiers();
|
return selectionCriteria != null && selectionCriteria.hasQualfiers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void printQualifierMessage(SelectionCriteria selectionCriteria ) {
|
||||||
|
|
||||||
|
List<String> annotations = selectionCriteria.getQualifiers().stream()
|
||||||
|
.filter( DeclaredType.class::isInstance )
|
||||||
|
.map( DeclaredType.class::cast )
|
||||||
|
.map( DeclaredType::asElement )
|
||||||
|
.map( Element::getSimpleName )
|
||||||
|
.map( Name::toString )
|
||||||
|
.map( a -> "@" + a )
|
||||||
|
.collect( Collectors.toList() );
|
||||||
|
List<String> names = selectionCriteria.getQualifiedByNames();
|
||||||
|
|
||||||
|
if ( !annotations.isEmpty() && !names.isEmpty() ) {
|
||||||
|
messager.printMessage(
|
||||||
|
mappingMethod.getExecutable(),
|
||||||
|
positionHint,
|
||||||
|
Message.GENERAL_NO_QUALIFYING_METHOD_COMBINED,
|
||||||
|
Strings.join( names, MessageConstants.AND ),
|
||||||
|
Strings.join( annotations, MessageConstants.AND )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ( !annotations.isEmpty() ) {
|
||||||
|
messager.printMessage(
|
||||||
|
mappingMethod.getExecutable(),
|
||||||
|
positionHint,
|
||||||
|
Message.GENERAL_NO_QUALIFYING_METHOD_ANNOTATION,
|
||||||
|
Strings.join( annotations, MessageConstants.AND )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
messager.printMessage(
|
||||||
|
mappingMethod.getExecutable(),
|
||||||
|
positionHint,
|
||||||
|
Message.GENERAL_NO_QUALIFYING_METHOD_NAMED,
|
||||||
|
Strings.join( names, MessageConstants.AND )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private boolean allowDirect( Type sourceType, Type targetType ) {
|
private boolean allowDirect( Type sourceType, Type targetType ) {
|
||||||
if ( selectionCriteria != null && selectionCriteria.isAllowDirect() ) {
|
if ( selectionCriteria != null && selectionCriteria.isAllowDirect() ) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -7,6 +7,8 @@ package org.mapstruct.ap.internal.util;
|
|||||||
|
|
||||||
import javax.tools.Diagnostic;
|
import javax.tools.Diagnostic;
|
||||||
|
|
||||||
|
import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_QUALIFIER_URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A message used in warnings/errors raised by the annotation processor.
|
* A message used in warnings/errors raised by the annotation processor.
|
||||||
*
|
*
|
||||||
@ -119,7 +121,9 @@ public enum Message {
|
|||||||
GENERAL_JODA_NOT_ON_CLASSPATH( "Cannot validate Joda dateformat, no Joda on classpath. Consider adding Joda to the annotation processorpath.", Diagnostic.Kind.WARNING ),
|
GENERAL_JODA_NOT_ON_CLASSPATH( "Cannot validate Joda dateformat, no Joda on classpath. Consider adding Joda to the annotation processorpath.", Diagnostic.Kind.WARNING ),
|
||||||
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" ),
|
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" ),
|
||||||
GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible constructor." ),
|
GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible constructor." ),
|
||||||
GENERAL_NO_QUALIFYING_METHOD( "No qualifying method found for qualifiers: %s and / or qualifying names: %s" ),
|
GENERAL_NO_QUALIFYING_METHOD_ANNOTATION( "Qualifier error. No method found annotated with: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ),
|
||||||
|
GENERAL_NO_QUALIFYING_METHOD_NAMED( "Qualifier error. No method found annotated with @Named#value: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ),
|
||||||
|
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." ),
|
||||||
|
|
||||||
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 ),
|
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 ),
|
||||||
BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ),
|
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 {
|
|||||||
VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." );
|
VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." );
|
||||||
// CHECKSTYLE:ON
|
// CHECKSTYLE:ON
|
||||||
|
|
||||||
|
|
||||||
private final String description;
|
private final String description;
|
||||||
private final Diagnostic.Kind kind;
|
private final Diagnostic.Kind kind;
|
||||||
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.internal.util;
|
||||||
|
|
||||||
|
public final class MessageConstants {
|
||||||
|
|
||||||
|
public static final String AND = " and ";
|
||||||
|
public static final String FAQ_QUALIFIER_URL = "https://mapstruct.org/faq/#qualifier";
|
||||||
|
|
||||||
|
private MessageConstants() {
|
||||||
|
}
|
||||||
|
}
|
@ -103,8 +103,8 @@ public class QualifierTest {
|
|||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 28,
|
line = 28,
|
||||||
message =
|
message =
|
||||||
"No qualifying method found for qualifiers: org.mapstruct.ap.test.selection.qualifier.annotation" +
|
"Qualifier error. No method found annotated with: [ @NonQualifierAnnotated ]. " +
|
||||||
".NonQualifierAnnotated and / or qualifying names: "),
|
"See https://mapstruct.org/faq/#qualifier for more info."),
|
||||||
@Diagnostic(
|
@Diagnostic(
|
||||||
type = ErroneousMapper.class,
|
type = ErroneousMapper.class,
|
||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.selection.qualifier.errors;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Qualifier;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousMessageByAnnotationAndNamedMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "nested", source = "value", qualifiedBy = {
|
||||||
|
SelectMe1.class,
|
||||||
|
SelectMe2.class
|
||||||
|
}, qualifiedByName = { "selectMe1", "selectMe2" })
|
||||||
|
Target map(Source source);
|
||||||
|
|
||||||
|
default Nested map(String in) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
class Source {
|
||||||
|
public String value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
public Nested nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
public String value;
|
||||||
|
}
|
||||||
|
// CHECKSTYLE ON
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@java.lang.annotation.Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface SelectMe1 {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@java.lang.annotation.Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface SelectMe2 {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.selection.qualifier.errors;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Qualifier;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousMessageByAnnotationMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "nested", source = "value", qualifiedBy = SelectMe.class)
|
||||||
|
Target map(Source source);
|
||||||
|
|
||||||
|
default Nested map(String in) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
class Source {
|
||||||
|
public String value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
public Nested nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
public String value;
|
||||||
|
}
|
||||||
|
// CHECKSTYLE ON
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@java.lang.annotation.Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface SelectMe {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.selection.qualifier.errors;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Qualifier;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousMessageByNamedMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "nested", source = "value", qualifiedByName = "SelectMe")
|
||||||
|
Target map(Source source);
|
||||||
|
|
||||||
|
default Nested map(String in) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
class Source {
|
||||||
|
public String value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
public Nested nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
public String value;
|
||||||
|
}
|
||||||
|
// CHECKSTYLE ON
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@java.lang.annotation.Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface SelectMe {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.selection.qualifier.errors;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
|
||||||
|
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
|
||||||
|
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static javax.tools.Diagnostic.Kind.ERROR;
|
||||||
|
|
||||||
|
@IssueKey("2135")
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
public class QualfierMessageTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses(ErroneousMessageByAnnotationMapper.class)
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByAnnotationMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 19,
|
||||||
|
message = "Qualifier error. No method found annotated with: [ @SelectMe ]. " +
|
||||||
|
"See https://mapstruct.org/faq/#qualifier for more info."),
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByAnnotationMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 19,
|
||||||
|
messageRegExp = "Can't map property.*")
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void testNoQualifyingMethodByAnnotationFound() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses(ErroneousMessageByNamedMapper.class)
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByNamedMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 19,
|
||||||
|
message = "Qualifier error. No method found annotated with @Named#value: [ SelectMe ]. " +
|
||||||
|
"See https://mapstruct.org/faq/#qualifier for more info."),
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByNamedMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 19,
|
||||||
|
messageRegExp = "Can't map property.*")
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void testNoQualifyingMethodByNamedFound() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses(ErroneousMessageByAnnotationAndNamedMapper.class)
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByAnnotationAndNamedMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 19,
|
||||||
|
message =
|
||||||
|
"Qualifier error. No method found annotated with @Named#value: [ selectMe1 and selectMe2 ], " +
|
||||||
|
"annotated with [ @SelectMe1 and @SelectMe2 ]. " +
|
||||||
|
"See https://mapstruct.org/faq/#qualifier for more info."),
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByAnnotationAndNamedMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 19,
|
||||||
|
messageRegExp = "Can't map property.*")
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void testNoQualifyingMethodByAnnotationAndNamedFound() {
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user