diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 01ffcd17a..a12aeba70 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -136,7 +136,7 @@ public @interface Mapping { * imported via {@link Mapper#imports()}. *

* This attribute can not be used together with {@link #source()}, {@link #defaultValue()}, - * {@link #defaultExpression()} or {@link #constant()}. + * {@link #defaultExpression()}, {@link #qualifiedBy()}, {@link #qualifiedByName()} or {@link #constant()}. * * @return An expression specifying the value for the designated target property */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java index fabd444fc..789b22126 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java @@ -236,6 +236,10 @@ public class Mapping { else if ( mappingPrism.values.defaultValue() != null && mappingPrism.values.defaultExpression() != null ) { message = Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } + else if ( mappingPrism.values.expression() != null + && ( mappingPrism.values.qualifiedByName() != null || mappingPrism.values.qualifiedBy() != null ) ) { + message = Message.PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED; + } else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null && mappingPrism.values.defaultValue() != null ) { message = Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_NVPMS; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 4ecf757d4..83ad59fcc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -57,6 +57,7 @@ public enum Message { PROPERTYMAPPING_CONSTANT_VALUE_AND_NVPMS( "Constant and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a constant or an nullValuePropertyMappingStrategy." ), PROPERTYMAPPING_DEFAULT_EXPERSSION_AND_NVPMS( "DefaultExpression and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a defaultExpression or an nullValuePropertyMappingStrategy." ), PROPERTYMAPPING_IGNORE_AND_NVPMS( "Ignore and nullValuePropertyMappingStrategy are both defined in @Mapping, either define ignore or an nullValuePropertyMappingStrategy." ), + PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED("Expression and a qualifier both defined in @Mapping, either define an expression or a qualifier."), PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java()\"." ), PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java()\"." ), PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/ErroneousSourceTargetMapperExpressionAndQualifiers.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/ErroneousSourceTargetMapperExpressionAndQualifiers.java new file mode 100644 index 000000000..189fc58b5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/ErroneousSourceTargetMapperExpressionAndQualifiers.java @@ -0,0 +1,29 @@ +/* + * 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.source.expressions.java; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.Mappings; +import org.mapstruct.ReportingPolicy; + +@Mapper( uses = QualifierProvider.class, unmappedTargetPolicy = ReportingPolicy.IGNORE ) +public interface ErroneousSourceTargetMapperExpressionAndQualifiers { + + @Mappings( { + @Mapping( target = "anotherProp", expression = "java( s.getClass().getName() )", qualifiedByName = "toUpper" ), + @Mapping( target = "timeAndFormat", ignore = true ) + } ) + Target sourceToTargetWithExpressionAndNamedQualifier(Source s, @MappingTarget Target t); + + @Mappings( { + @Mapping( target = "anotherProp", expression = "java( s.getClass().getName() )", + qualifiedBy = QualifierProvider.ToUpper.class ), + @Mapping( target = "timeAndFormat", ignore = true ) + } ) + Target sourceToTargetWithExpressionAndQualifier(Source s, @MappingTarget Target t); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java index 3cec82ea9..726690ed8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java @@ -17,6 +17,9 @@ import org.junit.runner.RunWith; import org.mapstruct.ap.test.source.expressions.java.mapper.TimeAndFormat; 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; /** @@ -131,4 +134,33 @@ public class JavaExpressionTest { assertThat( target ).isNotNull(); assertThat( target.getList() ).isEqualTo( Arrays.asList( "test2" ) ); } + + @IssueKey( "1851" ) + @Test + @WithClasses({ + Source.class, + Target.class, + QualifierProvider.class, + TimeAndFormat.class, + ErroneousSourceTargetMapperExpressionAndQualifiers.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousSourceTargetMapperExpressionAndQualifiers.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 18, + messageRegExp = "Expression and a qualifier both defined in @Mapping," + + " either define an expression or a qualifier." + ), + @Diagnostic(type = ErroneousSourceTargetMapperExpressionAndQualifiers.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 24, + messageRegExp = "Expression and a qualifier both defined in @Mapping," + + " either define an expression or a qualifier." + ) + } + ) + public void testExpressionAndQualifiedDoesNotCompile() { + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/QualifierProvider.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/QualifierProvider.java new file mode 100644 index 000000000..a5a508b6f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/QualifierProvider.java @@ -0,0 +1,17 @@ +/* + * 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.source.expressions.java; + +public class QualifierProvider { + + public @interface ToUpper { + } + + @ToUpper + public String toUpper( String string ) { + return string.toUpperCase(); + } +}