mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#598: Errors/Warnings now end up in the @Mapper annotated class. Co-authored-by: Ben Zegveld <Ben.Zegveld@gmail.com>
This commit is contained in:
parent
e32fc8c283
commit
ca2529f862
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||
|
||||
/**
|
||||
* Handles redirection of errors/warnings so that they're shown on the mapper instead of hidden on a superclass.
|
||||
*
|
||||
* @author Ben Zegveld
|
||||
*/
|
||||
public class MapperAnnotatedFormattingMessenger implements FormattingMessager {
|
||||
|
||||
private FormattingMessager delegateMessager;
|
||||
private TypeElement mapperTypeElement;
|
||||
private TypeUtils typeUtils;
|
||||
|
||||
public MapperAnnotatedFormattingMessenger(FormattingMessager delegateMessager, TypeElement mapperTypeElement,
|
||||
TypeUtils typeUtils) {
|
||||
this.delegateMessager = delegateMessager;
|
||||
this.mapperTypeElement = mapperTypeElement;
|
||||
this.typeUtils = typeUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printMessage(Message msg, Object... args) {
|
||||
delegateMessager.printMessage( msg, args );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printMessage(Element e, Message msg, Object... args) {
|
||||
delegateMessager
|
||||
.printMessage(
|
||||
determineDelegationElement( e ),
|
||||
determineDelegationMessage( e, msg ),
|
||||
determineDelegationArguments( e, msg, args ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printMessage(Element e, AnnotationMirror a, Message msg, Object... args) {
|
||||
delegateMessager
|
||||
.printMessage(
|
||||
determineDelegationElement( e ),
|
||||
a,
|
||||
determineDelegationMessage( e, msg ),
|
||||
determineDelegationArguments( e, msg, args ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printMessage(Element e, AnnotationMirror a, AnnotationValue v, Message msg, Object... args) {
|
||||
delegateMessager
|
||||
.printMessage(
|
||||
determineDelegationElement( e ),
|
||||
a,
|
||||
v,
|
||||
determineDelegationMessage( e, msg ),
|
||||
determineDelegationArguments( e, msg, args ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void note(int level, Message log, Object... args) {
|
||||
delegateMessager.note( level, log, args );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErroneous() {
|
||||
return delegateMessager.isErroneous();
|
||||
}
|
||||
|
||||
private Object[] determineDelegationArguments(Element e, Message msg, Object[] args) {
|
||||
if ( methodInMapperClass( e ) ) {
|
||||
return args;
|
||||
}
|
||||
String originalMessage = String.format( msg.getDescription(), args );
|
||||
return new Object[] { originalMessage, constructMethod( e ), e.getEnclosingElement().getSimpleName() };
|
||||
}
|
||||
|
||||
/**
|
||||
* ExecutableElement.toString() has different values depending on the compiler. Constructing the method itself
|
||||
* manually will ensure that the message is always traceable to it's source.
|
||||
*/
|
||||
private String constructMethod(Element e) {
|
||||
if ( e instanceof ExecutableElement ) {
|
||||
ExecutableElement ee = (ExecutableElement) e;
|
||||
StringBuilder method = new StringBuilder();
|
||||
method.append( typeUtils.asElement( ee.getReturnType() ).getSimpleName() );
|
||||
method.append( " " );
|
||||
method.append( ee.getSimpleName() );
|
||||
method.append( "(" );
|
||||
method.append( ee.getParameters()
|
||||
.stream()
|
||||
.map( this::parameterToString )
|
||||
.collect( Collectors.joining( ", " ) ) );
|
||||
method.append( ")" );
|
||||
return method.toString();
|
||||
}
|
||||
return e.toString();
|
||||
}
|
||||
|
||||
private String parameterToString(VariableElement element) {
|
||||
return typeUtils.asElement( element.asType() ).getSimpleName() + " " + element.getSimpleName();
|
||||
}
|
||||
|
||||
private Message determineDelegationMessage(Element e, Message msg) {
|
||||
if ( methodInMapperClass( e ) ) {
|
||||
return msg;
|
||||
}
|
||||
if ( msg.getDiagnosticKind() == Kind.ERROR ) {
|
||||
return Message.MESSAGE_MOVED_TO_MAPPER_ERROR;
|
||||
}
|
||||
return Message.MESSAGE_MOVED_TO_MAPPER_WARNING;
|
||||
}
|
||||
|
||||
private Element determineDelegationElement(Element e) {
|
||||
return methodInMapperClass( e ) ? e : mapperTypeElement;
|
||||
}
|
||||
|
||||
private boolean methodInMapperClass(Element e) {
|
||||
return mapperTypeElement == null || e.equals( mapperTypeElement )
|
||||
|| e.getEnclosingElement().equals( mapperTypeElement );
|
||||
}
|
||||
}
|
@ -96,7 +96,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<SourceMethod> sourceModel) {
|
||||
this.elementUtils = context.getElementUtils();
|
||||
this.typeUtils = context.getTypeUtils();
|
||||
this.messager = context.getMessager();
|
||||
this.messager =
|
||||
new MapperAnnotatedFormattingMessenger( context.getMessager(), mapperTypeElement, context.getTypeUtils() );
|
||||
this.options = context.getOptions();
|
||||
this.versionInformation = context.getVersionInformation();
|
||||
this.typeFactory = context.getTypeFactory();
|
||||
|
@ -20,6 +20,8 @@ public enum Message {
|
||||
// CHECKSTYLE:OFF
|
||||
PROCESSING_NOTE( "processing: %s.", Diagnostic.Kind.NOTE ),
|
||||
CONFIG_NOTE( "applying mapper configuration: %s.", Diagnostic.Kind.NOTE ),
|
||||
MESSAGE_MOVED_TO_MAPPER_ERROR( "%s Occured at '%s' in '%s'." ),
|
||||
MESSAGE_MOVED_TO_MAPPER_WARNING( "%s Occured at '%s' in '%s'.", Diagnostic.Kind.WARNING ),
|
||||
|
||||
BEANMAPPING_CREATE_NOTE( "creating bean mapping method implementation for %s.", Diagnostic.Kind.NOTE ),
|
||||
BEANMAPPING_NO_ELEMENTS( "'nullValueMappingStrategy', 'nullValuePropertyMappingStrategy', 'resultType' and 'qualifiedBy' are undefined in @BeanMapping, define at least one of them." ),
|
||||
|
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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.erroneous.supermappingwithsubclassmapper;
|
||||
|
||||
public interface AbstractMapper<SOURCE, TARGET> {
|
||||
|
||||
TARGET map(SOURCE source);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.erroneous.supermappingwithsubclassmapper;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ErroneousMapper1 extends AbstractMapper<Source, Target> {
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.erroneous.supermappingwithsubclassmapper;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ErroneousMapper2 extends AbstractMapper<Source, Target> {
|
||||
@Override
|
||||
Target map(Source source);
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.erroneous.supermappingwithsubclassmapper;
|
||||
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||
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;
|
||||
|
||||
@IssueKey( "598" )
|
||||
@WithClasses( { Source.class, Target.class, UnmappableClass.class, AbstractMapper.class } )
|
||||
public class ErroneousPropertyMappingTest {
|
||||
|
||||
@ProcessorTest
|
||||
@WithClasses( ErroneousMapper1.class )
|
||||
@IssueKey( "598" )
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousMapper1.class,
|
||||
line = 11,
|
||||
messageRegExp = "Can't map property \"UnmappableClass property\" to \"String property\"\\. " +
|
||||
"Consider to declare/implement a mapping method: \"String map\\(UnmappableClass value\\)\"\\. " +
|
||||
"Occured at 'TARGET map\\(SOURCE source\\)' in 'AbstractMapper'\\.")
|
||||
}
|
||||
)
|
||||
public void testUnmappableSourcePropertyInSuperclass() {
|
||||
}
|
||||
|
||||
@ProcessorTest
|
||||
@WithClasses( ErroneousMapper2.class )
|
||||
@IssueKey( "598" )
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
type = ErroneousMapper2.class,
|
||||
line = 13,
|
||||
message = "Can't map property \"UnmappableClass property\" to \"String property\". " +
|
||||
"Consider to declare/implement a mapping method: \"String map(UnmappableClass value)\".") } )
|
||||
public void testMethodOverride() {
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.erroneous.supermappingwithsubclassmapper;
|
||||
|
||||
public class Source {
|
||||
|
||||
private UnmappableClass property;
|
||||
|
||||
public UnmappableClass getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public void setProperty(UnmappableClass property) {
|
||||
this.property = property;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.erroneous.supermappingwithsubclassmapper;
|
||||
|
||||
public class Target {
|
||||
|
||||
private String property;
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public void setProperty(String property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.erroneous.supermappingwithsubclassmapper;
|
||||
|
||||
public class UnmappableClass {
|
||||
}
|
@ -93,11 +93,12 @@ public class DottedErrorMessageTest {
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = BaseDeepNestingMapper.class,
|
||||
@Diagnostic(type = UnmappableDeepNestingMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 10,
|
||||
line = 14,
|
||||
message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY +
|
||||
" \"Color house.roof.color\" to \"ColorDto house.roof.color\".")
|
||||
" \"Color house.roof.color\" to \"ColorDto house.roof.color\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepNestingMapper'.")
|
||||
}
|
||||
)
|
||||
public void testDeepNestedBeans() {
|
||||
@ -110,11 +111,12 @@ public class DottedErrorMessageTest {
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = BaseDeepListMapper.class,
|
||||
@Diagnostic(type = UnmappableDeepListMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 10,
|
||||
line = 14,
|
||||
message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT +
|
||||
" \"Wheel car.wheels\" to \"WheelDto car.wheels\".")
|
||||
" \"Wheel car.wheels\" to \"WheelDto car.wheels\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepListMapper'.")
|
||||
}
|
||||
)
|
||||
public void testIterables() {
|
||||
@ -127,11 +129,12 @@ public class DottedErrorMessageTest {
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = BaseDeepMapKeyMapper.class,
|
||||
@Diagnostic(type = UnmappableDeepMapKeyMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 10,
|
||||
line = 14,
|
||||
message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY +
|
||||
" \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\".")
|
||||
" \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapKeyMapper'.")
|
||||
}
|
||||
)
|
||||
public void testMapKeys() {
|
||||
@ -144,11 +147,12 @@ public class DottedErrorMessageTest {
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = BaseDeepMapValueMapper.class,
|
||||
@Diagnostic(type = UnmappableDeepMapValueMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 10,
|
||||
line = 14,
|
||||
message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE +
|
||||
" \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\".")
|
||||
" \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapValueMapper'.")
|
||||
}
|
||||
)
|
||||
public void testMapValues() {
|
||||
@ -161,11 +165,12 @@ public class DottedErrorMessageTest {
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = BaseCollectionElementPropertyMapper.class,
|
||||
@Diagnostic(type = UnmappableCollectionElementPropertyMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 10,
|
||||
line = 14,
|
||||
message = "Unmapped target property: \"color\". Mapping from " + PROPERTY +
|
||||
" \"Info computers[].info\" to \"InfoDto computers[].info\".")
|
||||
" \"Info computers[].info\" to \"InfoDto computers[].info\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseCollectionElementPropertyMapper'.")
|
||||
}
|
||||
)
|
||||
public void testCollectionElementProperty() {
|
||||
@ -178,11 +183,12 @@ public class DottedErrorMessageTest {
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = BaseValuePropertyMapper.class,
|
||||
@Diagnostic(type = UnmappableValuePropertyMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 10,
|
||||
line = 14,
|
||||
message = "Unmapped target property: \"color\". Mapping from " + PROPERTY +
|
||||
" \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\".")
|
||||
" \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseValuePropertyMapper'.")
|
||||
}
|
||||
)
|
||||
public void testMapValueProperty() {
|
||||
@ -220,36 +226,42 @@ public class DottedErrorMessageTest {
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.SUCCEEDED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = BaseDeepNestingMapper.class,
|
||||
@Diagnostic(type = UnmappableWarnDeepNestingMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 10,
|
||||
line = 13,
|
||||
message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY +
|
||||
" \"Color house.roof.color\" to \"ColorDto house.roof.color\"."),
|
||||
@Diagnostic(type = BaseDeepListMapper.class,
|
||||
" \"Color house.roof.color\" to \"ColorDto house.roof.color\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepNestingMapper'."),
|
||||
@Diagnostic(type = UnmappableWarnDeepListMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 10,
|
||||
line = 13,
|
||||
message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT +
|
||||
" \"Wheel car.wheels\" to \"WheelDto car.wheels\"."),
|
||||
@Diagnostic(type = BaseDeepMapKeyMapper.class,
|
||||
" \"Wheel car.wheels\" to \"WheelDto car.wheels\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepListMapper'."),
|
||||
@Diagnostic(type = UnmappableWarnDeepMapKeyMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 10,
|
||||
line = 13,
|
||||
message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY +
|
||||
" \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\"."),
|
||||
@Diagnostic(type = BaseDeepMapValueMapper.class,
|
||||
" \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapKeyMapper'."),
|
||||
@Diagnostic(type = UnmappableWarnDeepMapValueMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 10,
|
||||
line = 13,
|
||||
message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE +
|
||||
" \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\"."),
|
||||
@Diagnostic(type = BaseCollectionElementPropertyMapper.class,
|
||||
" \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapValueMapper'."),
|
||||
@Diagnostic(type = UnmappableWarnCollectionElementPropertyMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 10,
|
||||
line = 13,
|
||||
message = "Unmapped target property: \"color\". Mapping from " + PROPERTY +
|
||||
" \"Info computers[].info\" to \"InfoDto computers[].info\"."),
|
||||
@Diagnostic(type = BaseValuePropertyMapper.class,
|
||||
" \"Info computers[].info\" to \"InfoDto computers[].info\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseCollectionElementPropertyMapper'."),
|
||||
@Diagnostic(type = UnmappableWarnValuePropertyMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 10,
|
||||
line = 13,
|
||||
message = "Unmapped target property: \"color\". Mapping from " + PROPERTY +
|
||||
" \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\".")
|
||||
" \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\"." +
|
||||
" Occured at 'UserDto userToUserDto(User user)' in 'BaseValuePropertyMapper'.")
|
||||
}
|
||||
)
|
||||
public void testWarnUnmappedTargetProperties() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user