From c4135e68ed8330fbc5180abd828e35b30f606ece Mon Sep 17 00:00:00 2001 From: "jude.niroshan11@gmail.com" Date: Fri, 19 Feb 2021 23:06:42 +0100 Subject: [PATCH] #2339 Support throwing an exception as an Enum Mapping option --- .../java/org/mapstruct/MappingConstants.java | 8 ++ .../main/java/org/mapstruct/ValueMapping.java | 27 +++++- .../ap/internal/gem/MappingConstantsGem.java | 2 + .../ap/internal/model/ValueMappingMethod.java | 82 +++++++++++++--- .../model/source/ValueMappingOptions.java | 3 +- .../mapstruct/ap/internal/util/Message.java | 3 +- .../ap/internal/model/ValueMappingMethod.ftl | 10 +- .../DefaultOrderThrowExceptionMapper.java | 26 ++++++ .../enum2enum/EnumToEnumMappingTest.java | 30 +++--- .../EnumToEnumThrowExceptionMappingTest.java | 93 +++++++++++++++++++ ...OrderMapperThrowExceptionAsSourceType.java | 32 +++++++ .../enum2enum/OrderThrowExceptionMapper.java | 35 +++++++ .../value/enum2enum/SpecialOrderMapper.java | 8 +- .../SpecialThrowExceptionMapper.java | 47 ++++++++++ .../enum2enum/SpecialOrderMapperImpl.java | 4 +- 15 files changed, 368 insertions(+), 42 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index 6e30f08c2..53528f559 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -36,6 +36,14 @@ public final class MappingConstants { */ public static final String ANY_UNMAPPED = ""; + /** + * In an {@link ValueMapping} this represents any target that will be mapped to an + * {@link java.lang.IllegalArgumentException} which will be thrown at runtime. + *

+ * NOTE: The value is only applicable to {@link ValueMapping#target()} and not to {@link ValueMapping#source()}. + */ + public static final String THROW_EXCEPTION = ""; + /** * In an {@link EnumMapping} this represent the enum transformation strategy that adds a suffix to the source enum. * diff --git a/core/src/main/java/org/mapstruct/ValueMapping.java b/core/src/main/java/org/mapstruct/ValueMapping.java index f8f2d3b18..2d4a58636 100644 --- a/core/src/main/java/org/mapstruct/ValueMapping.java +++ b/core/src/main/java/org/mapstruct/ValueMapping.java @@ -23,7 +23,8 @@ import java.lang.annotation.Target; * *

  * 
- * public enum OrderType { RETAIL, B2B, EXTRA, STANDARD, NORMAL }
+ *
+ * public enum OrderType { RETAIL, B2B, C2C, EXTRA, STANDARD, NORMAL }
  *
  * public enum ExternalOrderType { RETAIL, B2B, SPECIAL, DEFAULT }
  *
@@ -45,13 +46,12 @@ import java.lang.annotation.Target;
  * +---------------------+----------------------------+
  * 
* - * MapStruct will WARN on incomplete mappings. However, if for some reason no match is found an - * {@link java.lang.IllegalStateException} will be thrown. *

* Example 2: * *

  * 
+ *
  * @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ),
  * @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ),
  * @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" )
@@ -70,6 +70,26 @@ import java.lang.annotation.Target;
  * +---------------------+----------------------------+
  * 
* + *

+ * Example 3: + *

+ * + *

MapStruct will WARN on incomplete mappings. However, if for some reason no match is found, an + * {@link java.lang.IllegalStateException} will be thrown. This compile-time error can be avoided by + * using {@link MappingConstants#THROW_EXCEPTION} for {@link ValueMapping#target()}. It will result an + * {@link java.lang.IllegalArgumentException} at runtime. + *
+ * 
+ *
+ * @ValueMapping( source = "STANDARD", target = "DEFAULT" ),
+ * @ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION )
+ * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
+ * 
+ * Mapping result:
+ * {@link java.lang.IllegalArgumentException} with the error message:
+ * Unexpected enum constant: C2C
+ * 
+ * * @author Sjaak Derksen */ @Repeatable(ValueMappings.class) @@ -104,6 +124,7 @@ public @interface ValueMapping { *
    *
  1. enum constant name
  2. *
  3. {@link MappingConstants#NULL}
  4. + *
  5. {@link MappingConstants#THROW_EXCEPTION}
  6. *
* * @return The target value. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index 5df7fd3f9..be8f23b76 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -21,6 +21,8 @@ public final class MappingConstantsGem { public static final String ANY_UNMAPPED = ""; + public static final String THROW_EXCEPTION = ""; + public static final String SUFFIX_TRANSFORMATION = "suffix"; public static final String STRIP_SUFFIX_TRANSFORMATION = "stripSuffix"; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 627a4b728..e292d38e3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -14,7 +14,6 @@ import java.util.Map; import java.util.Set; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.gem.BeanMappingGem; import org.mapstruct.ap.internal.model.common.Parameter; @@ -25,11 +24,13 @@ import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.ValueMappingOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.EnumTransformationStrategy; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_REMAINING; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_UNMAPPED; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.NULL; +import static org.mapstruct.ap.internal.gem.MappingConstantsGem.THROW_EXCEPTION; import static org.mapstruct.ap.internal.util.Collections.first; /** @@ -43,6 +44,8 @@ public class ValueMappingMethod extends MappingMethod { private final List valueMappings; private final String defaultTarget; private final String nullTarget; + private boolean nullAsException; + private boolean defaultAsException; private final Type unexpectedValueMappingException; @@ -119,10 +122,12 @@ public class ValueMappingMethod extends MappingMethod { return new ValueMappingMethod( method, mappingEntries, valueMappings.nullValueTarget, + valueMappings.hasNullValueAsException, valueMappings.defaultTargetValue, determineUnexpectedValueMappingException(), beforeMappingMethods, - afterMappingMethods + afterMappingMethods, + determineExceptionMappingForDefaultCase() ); } @@ -313,7 +318,17 @@ public class ValueMappingMethod extends MappingMethod { for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { - if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { + if ( !enumMapping.isInverse() && THROW_EXCEPTION.equals( mappedConstant.getSource() ) ) { + ctx.getMessager().printMessage( + method.getExecutable(), + mappedConstant.getMirror(), + mappedConstant.getSourceAnnotationValue(), + Message.VALUEMAPPING_THROW_EXCEPTION_SOURCE + ); + foundIncorrectMapping = true; + } + else if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { + ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), @@ -361,6 +376,7 @@ public class ValueMappingMethod extends MappingMethod { for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { if ( !NULL.equals( mappedConstant.getTarget() ) + && !THROW_EXCEPTION.equals( mappedConstant.getTarget() ) && !targetEnumConstants.contains( mappedConstant.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -374,7 +390,9 @@ public class ValueMappingMethod extends MappingMethod { } } - if ( valueMappings.defaultTarget != null && !NULL.equals( valueMappings.defaultTarget.getTarget() ) + if ( valueMappings.defaultTarget != null + && !THROW_EXCEPTION.equals( valueMappings.defaultTarget.getTarget() ) + && !NULL.equals( valueMappings.defaultTarget.getTarget() ) && !targetEnumConstants.contains( valueMappings.defaultTarget.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -415,7 +433,9 @@ public class ValueMappingMethod extends MappingMethod { } private Type determineUnexpectedValueMappingException() { - if ( !valueMappings.hasDefaultValue ) { + boolean noDefaultValueForSwitchCase = !valueMappings.hasDefaultValue; + if ( noDefaultValueForSwitchCase || valueMappings.hasAtLeastOneExceptionValue + || valueMappings.hasNullValueAsException ) { TypeMirror unexpectedValueMappingException = enumMapping.getUnexpectedValueMappingException(); if ( unexpectedValueMappingException != null ) { return ctx.getTypeFactory().getType( unexpectedValueMappingException ); @@ -427,6 +447,15 @@ public class ValueMappingMethod extends MappingMethod { return null; } + + private boolean determineExceptionMappingForDefaultCase() { + if ( valueMappings.hasDefaultValue ) { + return THROW_EXCEPTION.equals( valueMappings.defaultTargetValue ); + } + else { + return true; + } + } } private static class EnumTransformationStrategyInvoker { @@ -464,7 +493,8 @@ public class ValueMappingMethod extends MappingMethod { boolean hasMapAnyUnmapped = false; boolean hasMapAnyRemaining = false; boolean hasDefaultValue = false; - boolean hasNullValue = false; + boolean hasNullValueAsException = false; + boolean hasAtLeastOneExceptionValue = false; ValueMappings(List valueMappings) { @@ -484,11 +514,17 @@ public class ValueMappingMethod extends MappingMethod { else if ( NULL.equals( valueMapping.getSource() ) ) { nullTarget = valueMapping; nullValueTarget = getValue( nullTarget ); - hasNullValue = true; + if ( THROW_EXCEPTION.equals( nullValueTarget ) ) { + hasNullValueAsException = true; + } } else { regularValueMappings.add( valueMapping ); } + + if ( THROW_EXCEPTION.equals( valueMapping.getTarget() ) ) { + hasAtLeastOneExceptionValue = true; + } } } @@ -497,14 +533,20 @@ public class ValueMappingMethod extends MappingMethod { } } - private ValueMappingMethod(Method method, List enumMappings, String nullTarget, String defaultTarget, - Type unexpectedValueMappingException, - List beforeMappingMethods, - List afterMappingMethods) { + private ValueMappingMethod(Method method, + List enumMappings, + String nullTarget, + boolean hasNullTargetAsException, + String defaultTarget, + Type unexpectedValueMappingException, + List beforeMappingMethods, + List afterMappingMethods, boolean defaultAsException) { super( method, beforeMappingMethods, afterMappingMethods ); this.valueMappings = enumMappings; this.nullTarget = nullTarget; + this.nullAsException = hasNullTargetAsException; this.defaultTarget = defaultTarget; + this.defaultAsException = defaultAsException; this.unexpectedValueMappingException = unexpectedValueMappingException; this.overridden = method.overridesMethod(); } @@ -532,10 +574,18 @@ public class ValueMappingMethod extends MappingMethod { return nullTarget; } + public boolean isNullAsException() { + return nullAsException; + } + public Type getUnexpectedValueMappingException() { return unexpectedValueMappingException; } + public boolean isDefaultAsException() { + return defaultAsException; + } + public Parameter getSourceParameter() { return first( getSourceParameters() ); } @@ -547,17 +597,25 @@ public class ValueMappingMethod extends MappingMethod { public static class MappingEntry { private final String source; private final String target; + private boolean targetAsException = false; - MappingEntry( String source, String target ) { + MappingEntry(String source, String target) { this.source = source; if ( !NULL.equals( target ) ) { this.target = target; + if ( THROW_EXCEPTION.equals( target ) ) { + this.targetAsException = true; + } } else { this.target = null; } } + public boolean isTargetAsException() { + return targetAsException; + } + public String getSource() { return source; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java index 10aef4bfa..963683b5c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java @@ -18,6 +18,7 @@ import org.mapstruct.ap.internal.util.Message; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_REMAINING; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_UNMAPPED; +import static org.mapstruct.ap.internal.gem.MappingConstantsGem.THROW_EXCEPTION; /** * Represents the mapping between one value constant and another. @@ -112,7 +113,7 @@ public class ValueMappingOptions { public ValueMappingOptions inverse() { ValueMappingOptions result; - if ( !(ANY_REMAINING.equals( source ) || ANY_UNMAPPED.equals( source ) ) ) { + if ( !(ANY_REMAINING.equals( source ) || ANY_UNMAPPED.equals( source ) || THROW_EXCEPTION.equals( target ) ) ) { result = new ValueMappingOptions( target, source, 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 378395d4d..e30407ea2 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 @@ -175,7 +175,8 @@ public enum Message { VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM( "Source = \"\" can only be used on targets of type enum and not for %s." ), VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"\" or \"\" is advisable for mapping of type String to an enum type.", Diagnostic.Kind.WARNING ), VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI( "Constant %s doesn't exist in enum type %s. Constant was returned from EnumMappingStrategy: %s"), - 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." ), + VALUEMAPPING_THROW_EXCEPTION_SOURCE( "Source = \"\" is not allowed. Target = \"\" can only be used." ); // CHECKSTYLE:ON diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 530d5643e..2844b43e3 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -15,17 +15,17 @@ if ( ${sourceParameter.name} == null ) { - return <@writeTarget target=nullTarget/>; + <#if nullAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>return <@writeTarget target=nullTarget/>; } <@includeModel object=resultType/> ${resultName}; switch ( ${sourceParameter.name} ) { <#list valueMappings as valueMapping> - case <@writeSource source=valueMapping.source/>: ${resultName} = <@writeTarget target=valueMapping.target/>; - break; + case <@writeSource source=valueMapping.source/>: <#if valueMapping.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>${resultName} = <@writeTarget target=valueMapping.target/>; + break; - default: <#if unexpectedValueMappingException??>throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; + default: <#if defaultAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; } <#list beforeMappingReferencesWithMappingTarget as callback> <#if callback_index = 0> @@ -65,4 +65,4 @@ null - \ No newline at end of file + diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java new file mode 100644 index 000000000..ab1ca9782 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java @@ -0,0 +1,26 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.Named; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface DefaultOrderThrowExceptionMapper { + DefaultOrderThrowExceptionMapper INSTANCE = Mappers.getMapper( DefaultOrderThrowExceptionMapper.class ); + + @Named("orderTypeToExternalOrderTypeAnyUnmappedToException") + @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = MappingConstants.THROW_EXCEPTION) + ExternalOrderType orderTypeToExternalOrderTypeAnyUnmappedToException(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java index 43b621f8c..1e93f4b00 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.value.enum2enum; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; import org.junit.Rule; @@ -22,14 +20,18 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutco import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for the generation and invocation of enum mapping methods. * * @author Gunnar Morling, Sjaak Derksen */ @IssueKey("128") -@WithClasses({ OrderMapper.class, SpecialOrderMapper.class, DefaultOrderMapper.class, OrderEntity.class, - OrderType.class, OrderDto.class, ExternalOrderType.class }) +@WithClasses({ + OrderMapper.class, SpecialOrderMapper.class, DefaultOrderMapper.class, OrderEntity.class, + OrderType.class, OrderDto.class, ExternalOrderType.class +}) @RunWith(AnnotationProcessorTestRunner.class) public class EnumToEnumMappingTest { @@ -74,16 +76,16 @@ public class EnumToEnumMappingTest { @Test public void shouldApplyReverseMappings() { - OrderType result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); + OrderType result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); assertThat( result ).isEqualTo( OrderType.EXTRA ); - result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); assertThat( result ).isEqualTo( OrderType.STANDARD ); - result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); assertThat( result ).isEqualTo( OrderType.RETAIL ); - result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); assertThat( result ).isEqualTo( OrderType.B2B ); } @@ -141,16 +143,16 @@ public class EnumToEnumMappingTest { @Test public void shouldApplyDefaultReverseMappings() { - OrderType result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); + OrderType result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); assertThat( result ).isEqualTo( OrderType.EXTRA ); - result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); assertThat( result ).isNull(); - result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); assertThat( result ).isEqualTo( OrderType.RETAIL ); - result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); assertThat( result ).isEqualTo( OrderType.B2B ); } @@ -186,7 +188,7 @@ public class EnumToEnumMappingTest { } - @IssueKey( "1091" ) + @IssueKey("1091") @Test public void shouldMapAnyRemainingToNullCorrectly() { ExternalOrderType externalOrderType = SpecialOrderMapper.INSTANCE.anyRemainingToNull( OrderType.RETAIL ); @@ -265,7 +267,7 @@ public class EnumToEnumMappingTest { @Diagnostic(type = ErroneousOrderMapperDuplicateANY.class, kind = Kind.ERROR, line = 28, - message = "Source = \"\" or \"\" can only be used once." ) + message = "Source = \"\" or \"\" can only be used once.") } ) public void shouldRaiseErrorIfMappingsContainDuplicateANY() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java new file mode 100644 index 000000000..15f0d7964 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java @@ -0,0 +1,93 @@ +/* + * 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.value.enum2enum; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +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 org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author Jude Niroshan + */ +@IssueKey("2339") +@WithClasses({ + OrderEntity.class, + OrderType.class, ExternalOrderType.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class EnumToEnumThrowExceptionMappingTest { + + @IssueKey("2339") + @Test + @WithClasses(DefaultOrderThrowExceptionMapper.class) + public void shouldBeAbleToMapAnyUnmappedToThrowException() { + + assertThatThrownBy( () -> + DefaultOrderThrowExceptionMapper.INSTANCE + .orderTypeToExternalOrderTypeAnyUnmappedToException( OrderType.EXTRA ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: EXTRA" ); + } + + @IssueKey("2339") + @Test + @WithClasses({ DefaultOrderThrowExceptionMapper.class, ErroneousOrderMapperThrowExceptionAsSourceType.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperThrowExceptionAsSourceType.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 29, + message = "Source = \"\" is not allowed. " + + "Target = \"\" can only be used.") + } + ) + public void shouldRaiseErrorWhenThrowExceptionUsedAsSourceType() { + } + + @IssueKey("2339") + @Test + @WithClasses({OrderThrowExceptionMapper.class, OrderDto.class}) + public void shouldIgnoreThrowExceptionWhenInverseValueMappings() { + + OrderType target = OrderThrowExceptionMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + assertThat( target ).isEqualTo( OrderType.B2B ); + } + + @IssueKey("2339") + @Test + @WithClasses({SpecialThrowExceptionMapper.class, OrderDto.class}) + public void shouldBeAbleToMapAnyRemainingToThrowException() { + + assertThatThrownBy( () -> + SpecialThrowExceptionMapper.INSTANCE + .orderTypeToExternalOrderType( OrderType.EXTRA ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: EXTRA" ); + } + + @IssueKey("2339") + @Test + @WithClasses({SpecialThrowExceptionMapper.class, OrderDto.class}) + public void shouldBeAbleToMapNullToThrowException() { + + assertThatThrownBy( () -> + SpecialThrowExceptionMapper.INSTANCE + .anyRemainingToNullToException( null ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: null" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java new file mode 100644 index 000000000..5c872c2de --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java @@ -0,0 +1,32 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface ErroneousOrderMapperThrowExceptionAsSourceType { + + ErroneousOrderMapperThrowExceptionAsSourceType INSTANCE = Mappers.getMapper( + ErroneousOrderMapperThrowExceptionAsSourceType.class ); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT"), + @ValueMapping(source = "", target = "DEFAULT"), + @ValueMapping(source = "", target = "DEFAULT") + }) + ExternalOrderType orderTypeToExternalOrderTypeWithErroneousSourceMapping(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java new file mode 100644 index 000000000..c189b22d5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java @@ -0,0 +1,35 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface OrderThrowExceptionMapper { + OrderThrowExceptionMapper INSTANCE = Mappers.getMapper( OrderThrowExceptionMapper.class ); + + OrderDto orderEntityToDto(OrderEntity order); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = MappingConstants.THROW_EXCEPTION) + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @InheritInverseConfiguration + OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java index 46ffb8b1c..60d2d5f67 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java @@ -29,14 +29,14 @@ public interface SpecialOrderMapper { @Named("orderTypeToExternalOrderType") @ValueMappings({ - @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), - @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), - @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" ) + @ValueMapping(source = MappingConstants.NULL, target = "DEFAULT"), + @ValueMapping(source = "STANDARD", target = MappingConstants.NULL), + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "SPECIAL") }) ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); @InheritInverseConfiguration(name = "orderTypeToExternalOrderType") - @ValueMapping( target = "EXTRA", source = "SPECIAL" ) + @ValueMapping(target = "EXTRA", source = "SPECIAL") OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); @ValueMappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java new file mode 100644 index 000000000..0c6d4740c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java @@ -0,0 +1,47 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.Named; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface SpecialThrowExceptionMapper { + SpecialThrowExceptionMapper INSTANCE = Mappers.getMapper( SpecialThrowExceptionMapper.class ); + + @Mapping(target = "orderType", source = "orderType", qualifiedByName = "orderTypeToExternalOrderType") + OrderDto orderEntityToDto(OrderEntity order); + + @Named("orderTypeToExternalOrderType") + @ValueMappings({ + @ValueMapping(source = MappingConstants.NULL, target = "DEFAULT"), + @ValueMapping(source = "STANDARD", target = MappingConstants.NULL), + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.THROW_EXCEPTION) + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @InheritInverseConfiguration(name = "orderTypeToExternalOrderType") + @ValueMapping(target = "EXTRA", source = "SPECIAL") + OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); + + @ValueMappings({ + @ValueMapping(source = MappingConstants.NULL, target = MappingConstants.THROW_EXCEPTION), + @ValueMapping(source = "STANDARD", target = MappingConstants.NULL), + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.NULL) + }) + ExternalOrderType anyRemainingToNullToException(OrderType orderType); +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java index f420a63ba..7b5f6f288 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java @@ -11,8 +11,8 @@ import org.mapstruct.ap.test.value.OrderType; @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2017-02-20T21:25:45+0100", - comments = "version: , compiler: javac, environment: Java 1.8.0_112 (Oracle Corporation)" + date = "2021-02-19T21:20:19+0100", + comments = "version: , compiler: javac, environment: Java 1.8.0_191 (Oracle Corporation)" ) public class SpecialOrderMapperImpl implements SpecialOrderMapper {