From fdf3dcc8efaf0cdc1a2145dd53c1302346e2838a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Bassinello?= Date: Thu, 6 May 2021 15:13:13 -0300 Subject: [PATCH] #2445 Support for case changing enum transformation strategy Available case transformations: upper, lower, capital --- .../main/java/org/mapstruct/EnumMapping.java | 12 ++++ .../java/org/mapstruct/MappingConstants.java | 8 +++ .../chapter-8-mapping-values.asciidoc | 4 ++ .../ap/internal/gem/MappingConstantsGem.java | 2 + .../spi/CaseEnumTransformationStrategy.java | 59 +++++++++++++++ ...apstruct.ap.spi.EnumTransformationStrategy | 1 + .../mapstruct/ap/test/gem/ConstantTest.java | 1 + .../nametransformation/CheeseCaseMapper.java | 57 +++++++++++++++ .../value/nametransformation/CheeseType.java | 3 +- .../nametransformation/CheeseTypeCapital.java | 16 +++++ .../CheeseTypeCustomSuffix.java | 1 + .../nametransformation/CheeseTypeLower.java | 16 +++++ .../CheeseTypePrefixed.java | 1 + .../CheeseTypeSuffixed.java | 1 + .../EnumNameTransformationStrategyTest.java | 72 ++++++++++++++++++- 15 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java diff --git a/core/src/main/java/org/mapstruct/EnumMapping.java b/core/src/main/java/org/mapstruct/EnumMapping.java index 2f908579e..ba3a5e0cb 100644 --- a/core/src/main/java/org/mapstruct/EnumMapping.java +++ b/core/src/main/java/org/mapstruct/EnumMapping.java @@ -104,6 +104,18 @@ public @interface EnumMapping { * prefix to the source enum *
  • {@link MappingConstants#STRIP_PREFIX_TRANSFORMATION} - strips the given {@link #configuration()} from * the start of the source enum
  • + *
  • + * {@link MappingConstants#CASE_TRANSFORMATION} - applies the given {@link #configuration()} case + * transformation to the source enum. Supported configurations are: + * + *
  • * * * It is possible to use custom name transformation strategies by implementing the {@code diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index 53528f559..002f00529 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -74,6 +74,14 @@ public final class MappingConstants { */ public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix"; + /** + * In an {@link EnumMapping} this represent the enum transformation strategy that applies case transformation + * at the source. + * + * @since 1.5 + */ + public static final String CASE_TRANSFORMATION = "case"; + /** * Specifies the component model constants to which the generated mapper should adhere. * It can be used with the annotation {@link Mapper#componentModel()} or {@link MapperConfig#componentModel()} diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 57b9433a2..24c7324f1 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -259,6 +259,10 @@ MapStruct provides the following out of the box enum name transformation strateg * _stripSuffix_ - Strips a suffix from the source enum * _prefix_ - Applies a prefix on the source enum * _stripPrefix_ - Strips a prefix from the source enum +* _case_ - Applies case transformation to the source enum. Supported _case_ transformations are: +** _upper_ - Performs upper case transformation to the source enum +** _lower_ - Performs lower case transformation to the source enum +** _capital_ - Performs capitalisation of the first character of every word in the source enum and everything else to lowercase. A word is split by "_" It is also possible to register custom strategies. For more information on how to do that have a look at <> 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 be8f23b76..b49b9d17e 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 @@ -31,6 +31,8 @@ public final class MappingConstantsGem { public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix"; + public static final String CASE_TRANSFORMATION = "case"; + /** * Gem for the class {@link org.mapstruct.MappingConstants.ComponentModel} * diff --git a/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java new file mode 100644 index 000000000..2f99f56ce --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java @@ -0,0 +1,59 @@ +/* + * 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.spi; + +import java.util.Arrays; +import java.util.Locale; +import java.util.stream.Collectors; + +/** + * Applies case transformation to the source enum + * + * @author jpbassinello + * @since 1.5 + */ +public class CaseEnumTransformationStrategy implements EnumTransformationStrategy { + + private static final String UPPER = "upper"; + private static final String LOWER = "lower"; + private static final String CAPITAL = "capital"; + + @Override + public String getStrategyName() { + return "case"; + } + + @Override + public String transform(String value, String configuration) { + switch ( configuration.toLowerCase() ) { + case UPPER: + return value.toUpperCase( Locale.ROOT ); + case LOWER: + return value.toLowerCase( Locale.ROOT ); + case CAPITAL: + return capitalize( value ); + default: + throw new IllegalArgumentException( + "Unexpected configuration for enum case transformation: " + configuration ); + } + } + + private static String capitalize(String value) { + return Arrays.stream( value.split( "_" ) ) + .map( CaseEnumTransformationStrategy::upperCaseFirst ) + .collect( Collectors.joining( "_" ) ); + } + + private static String upperCaseFirst(String value) { + char[] array = value.toCharArray(); + array[0] = Character.toUpperCase( array[0] ); + for ( int i = 1; i < array.length; i++ ) { + array[i] = Character.toLowerCase( array[i] ); + } + return new String( array ); + } + +} diff --git a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy index 1f52733ad..264b82d74 100644 --- a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy +++ b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy @@ -6,3 +6,4 @@ org.mapstruct.ap.spi.PrefixEnumTransformationStrategy org.mapstruct.ap.spi.StripPrefixEnumTransformationStrategy org.mapstruct.ap.spi.StripSuffixEnumTransformationStrategy org.mapstruct.ap.spi.SuffixEnumTransformationStrategy +org.mapstruct.ap.spi.CaseEnumTransformationStrategy diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index 87a22931c..e3a5b7c26 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -30,6 +30,7 @@ public class ConstantTest { assertThat( MappingConstants.PREFIX_TRANSFORMATION ).isEqualTo( MappingConstantsGem.PREFIX_TRANSFORMATION ); assertThat( MappingConstants.STRIP_PREFIX_TRANSFORMATION ) .isEqualTo( MappingConstantsGem.STRIP_PREFIX_TRANSFORMATION ); + assertThat( MappingConstants.CASE_TRANSFORMATION ).isEqualTo( MappingConstantsGem.CASE_TRANSFORMATION ); } @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java new file mode 100644 index 000000000..f29766be8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java @@ -0,0 +1,57 @@ +/* + * 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.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.factory.Mappers; + +/** + * @author jpbassinello + */ +@Mapper +public interface CheeseCaseMapper { + + CheeseCaseMapper INSTANCE = Mappers.getMapper( CheeseCaseMapper.class ); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "lower") + CheeseTypeLower mapToLower(CheeseType cheese); + + @InheritInverseConfiguration + CheeseType mapToLowerInverse(CheeseTypeLower cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "lower") + CheeseTypeLower mapToLower(CheeseTypeCapital cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "upper") + CheeseType mapToUpper(CheeseTypeLower cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "upper") + CheeseType mapToUpper(CheeseTypeCapital cheese); + + @InheritInverseConfiguration(name = "mapToUpper") + CheeseTypeCapital mapToUpperInverse(CheeseType cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "capital") + CheeseTypeCapital mapToCapital(CheeseTypeLower cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "capital") + CheeseTypeCapital mapToCapital(CheeseType cheese); + + @InheritInverseConfiguration(name = "mapToCapital") + CheeseType mapToCapitalInverse(CheeseTypeCapital cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "lower") + String mapToLowerString(CheeseType cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "upper") + String mapToUpperString(CheeseType cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "capital") + String mapToCapitalString(CheeseType cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java index d7a1d2e28..d3e9d55b1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java @@ -11,5 +11,6 @@ package org.mapstruct.ap.test.value.nametransformation; public enum CheeseType { BRIE, - ROQUEFORT + ROQUEFORT, + COLBY_JACK } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java new file mode 100644 index 000000000..d5d890dce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java @@ -0,0 +1,16 @@ +/* + * 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.nametransformation; + +/** + * @author jpbassinello + */ +public enum CheeseTypeCapital { + + Brie, + Roquefort, + Colby_Jack +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java index b7eccd3d4..7249e8a92 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java @@ -12,4 +12,5 @@ public enum CheeseTypeCustomSuffix { brie_TYPE, roquefort_TYPE, + colby_jack_TYPE } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java new file mode 100644 index 000000000..c6f064ec0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java @@ -0,0 +1,16 @@ +/* + * 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.nametransformation; + +/** + * @author jpbassinello + */ +public enum CheeseTypeLower { + + brie, + roquefort, + colby_jack +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java index 5f5b989c7..171b4e7d3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java @@ -13,4 +13,5 @@ public enum CheeseTypePrefixed { DEFAULT, SWISS_BRIE, SWISS_ROQUEFORT, + SWISS_COLBY_JACK } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java index b2b264b78..06b019c12 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java @@ -13,4 +13,5 @@ public enum CheeseTypeSuffixed { DEFAULT, BRIE_CHEESE_TYPE, ROQUEFORT_CHEESE_TYPE, + COLBY_JACK_CHEESE_TYPE } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java index c27128edb..d5c74dbe8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java @@ -22,6 +22,8 @@ import static org.assertj.core.api.Assertions.assertThat; CheeseTypeSuffixed.class, CheeseTypePrefixed.class, CheeseTypeCustomSuffix.class, + CheeseTypeLower.class, + CheeseTypeCapital.class }) public class EnumNameTransformationStrategyTest { @@ -92,7 +94,7 @@ public class EnumNameTransformationStrategyTest { kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, message = "There is no registered EnumTransformationStrategy for 'custom'. Registered strategies are:" + - " prefix, stripPrefix, stripSuffix, suffix." + " prefix, stripPrefix, stripSuffix, suffix, case." ) } ) @@ -109,4 +111,72 @@ public class EnumNameTransformationStrategyTest { .isEqualTo( CheeseTypeCustomSuffix.brie_TYPE ); } + @ProcessorTest + @WithClasses({ + CheeseCaseMapper.class + }) + public void shouldConvertCaseOnEnumToEnumMapping() { + CheeseCaseMapper mapper = CheeseCaseMapper.INSTANCE; + + assertThat( mapper.mapToLower( CheeseType.BRIE ) ) + .isEqualTo( CheeseTypeLower.brie ); + + assertThat( mapper.mapToLowerInverse( CheeseTypeLower.brie ) ) + .isEqualTo( CheeseType.BRIE ); + + assertThat( mapper.mapToLower( CheeseTypeCapital.Colby_Jack ) ) + .isEqualTo( CheeseTypeLower.colby_jack ); + + assertThat( mapper.mapToUpper( CheeseTypeLower.roquefort ) ) + .isEqualTo( CheeseType.ROQUEFORT ); + + assertThat( mapper.mapToUpper( CheeseTypeCapital.Colby_Jack ) ) + .isEqualTo( CheeseType.COLBY_JACK ); + + assertThat( mapper.mapToUpperInverse( CheeseType.COLBY_JACK ) ) + .isEqualTo( CheeseTypeCapital.Colby_Jack ); + + assertThat( mapper.mapToCapital( CheeseTypeLower.brie ) ) + .isEqualTo( CheeseTypeCapital.Brie ); + + assertThat( mapper.mapToCapital( CheeseType.ROQUEFORT ) ) + .isEqualTo( CheeseTypeCapital.Roquefort ); + + assertThat( mapper.mapToCapital( CheeseType.COLBY_JACK ) ) + .isEqualTo( CheeseTypeCapital.Colby_Jack ); + + assertThat( mapper.mapToCapitalInverse( CheeseTypeCapital.Roquefort ) ) + .isEqualTo( CheeseType.ROQUEFORT ); + + assertThat( mapper.mapToCapitalInverse( CheeseTypeCapital.Colby_Jack ) ) + .isEqualTo( CheeseType.COLBY_JACK ); + + assertThat( mapper.mapToCapital( CheeseTypeLower.colby_jack ) ) + .isEqualTo( CheeseTypeCapital.Colby_Jack ); + + } + + @ProcessorTest + @WithClasses({ + CheeseCaseMapper.class + }) + public void shouldConvertCaseOnEnumToStringMapping() { + CheeseCaseMapper mapper = CheeseCaseMapper.INSTANCE; + + assertThat( mapper.mapToLowerString( CheeseType.BRIE ) ) + .isEqualTo( "brie" ); + assertThat( mapper.mapToLowerString( CheeseType.COLBY_JACK ) ) + .isEqualTo( "colby_jack" ); + + assertThat( mapper.mapToUpperString( CheeseType.ROQUEFORT ) ) + .isEqualTo( "ROQUEFORT" ); + assertThat( mapper.mapToUpperString( CheeseType.COLBY_JACK ) ) + .isEqualTo( "COLBY_JACK" ); + + assertThat( mapper.mapToCapitalString( CheeseType.ROQUEFORT ) ) + .isEqualTo( "Roquefort" ); + assertThat( mapper.mapToCapitalString( CheeseType.COLBY_JACK ) ) + .isEqualTo( "Colby_Jack" ); + + } }