diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index 8b0da9a72..af52d58f2 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -23,11 +23,16 @@ public final class MappingConstants { /** * In an {@link ValueMapping} this represents any source that is not already mapped by either a defined mapping or * by means of name based mapping. + * + * NOTE: The value is only applicable to {@link ValueMapping#source()} and not to {@link ValueMapping#target()}. */ public static final String ANY_REMAINING = ""; /** * In an {@link ValueMapping} this represents any source that is not already mapped by a defined mapping. + * + * NOTE: The value is only applicable to {@link ValueMapping#source()} and not to {@link ValueMapping#target()}. + * */ public static final String ANY_UNMAPPED = ""; diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 752c1418a..7bfd6afa4 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -1,7 +1,7 @@ [[mapping-enum-types]] == Mapping Values -=== Mapping enum types +=== Mapping enum to enum types MapStruct supports the generation of methods which map one Java enum type into another. @@ -67,7 +67,7 @@ public class OrderMapperImpl implements OrderMapper { By default an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@ValueMapping`. This ensures that all constants are mapped in a safe and predictable manner. The generated mapping method will throw an IllegalStateException if for some reason an unrecognized source value occurs. -MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings. It comes in two flavors: `` and ``. +MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings and only applies to the source. It comes in two flavors: `` and ``. They cannot be used at the same time. In case of source `` MapStruct will continue to map a source enum constant to a target enum constant with the same name. The remainder of the source enum constants will be mapped to the target specified in the `@ValueMapping` with `` source. @@ -80,7 +80,7 @@ MapStruct is able to handle `null` sources and `null` targets by means of the `< Constants for ``, `` and `` are available in the `MappingConstants` class. ==== -Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. +Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. `` and `` will be ignored in that case. .Enum mapping method, and ==== @@ -140,4 +140,22 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper { [WARNING] ==== The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead. -==== \ No newline at end of file +==== + +=== Mapping enum-to-String or String-to-enum + +MapStruct supports enum to a String mapping along the same lines as is described in <>. There are similarities and differences: + +*enum to `String`* + +1. Similarity: All not explicit defined mappings will result in each source enum constant value being mapped a `String` value with the same constant value. +2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. +3. Difference: `` will result in an error. It acts on the premise that there is name similarity between enum constants in source and target which does not make sense for a String type. +4. Difference: Given 1. and 3. there will never be unmapped values. + +*`String` to enum* + +1. Similarity: All not explicit defined mappings will result in the target enum constant mapped from the `String` value when that maches the target enum constant name. +2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. +3. Similarity: `` will create a mapping for each target enum constant and proceed to the switch/default clause value. +4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `` or ` will result in a warning. 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 7c05ac581..90d5cd38f 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 @@ -5,6 +5,9 @@ */ package org.mapstruct.ap.internal.model; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_REMAINING; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_UNMAPPED; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.NULL; import static org.mapstruct.ap.internal.util.Collections.first; import java.util.ArrayList; @@ -16,11 +19,11 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.ValueMapping; import org.mapstruct.ap.internal.prism.BeanMappingPrism; -import org.mapstruct.ap.internal.prism.MappingConstantsPrism; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -42,10 +45,7 @@ public class ValueMappingMethod extends MappingMethod { private Method method; private MappingBuilderContext ctx; - private final List trueValueMappings = new ArrayList<>(); - private ValueMapping defaultTargetValue = null; - private ValueMapping nullTargetValue = null; - private boolean applyNamebasedMappings = true; + private ValueMappings valueMappings; public Builder mappingContext(MappingBuilderContext mappingContext) { this.ctx = mappingContext; @@ -58,21 +58,7 @@ public class ValueMappingMethod extends MappingMethod { } public Builder valueMappings(List valueMappings) { - for ( ValueMapping valueMapping : valueMappings ) { - if ( MappingConstantsPrism.ANY_REMAINING.equals( valueMapping.getSource() ) ) { - defaultTargetValue = valueMapping; - } - else if ( MappingConstantsPrism.ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { - defaultTargetValue = valueMapping; - applyNamebasedMappings = false; - } - else if ( MappingConstantsPrism.NULL.equals( valueMapping.getSource() ) ) { - nullTargetValue = valueMapping; - } - else { - trueValueMappings.add( valueMapping ); - } - } + this.valueMappings = new ValueMappings( valueMappings ); return this; } @@ -80,27 +66,19 @@ public class ValueMappingMethod extends MappingMethod { // initialize all relevant parameters List mappingEntries = new ArrayList<>(); - String nullTarget = null; - String defaultTarget = null; - boolean throwIllegalArgumentException = false; - // for now, we're only dealing with enum mappings, populate relevant parameters based on enum-2-enum - if ( first( method.getSourceParameters() ).getType().isEnumType() && method.getResultType().isEnumType() ) { - mappingEntries.addAll( enumToEnumMapping( method ) ); - - if ( (nullTargetValue != null) && !MappingConstantsPrism.NULL.equals( nullTargetValue.getTarget() ) ) { - // absense nulltargetvalue reverts to null. Or it could be a deliberate choice to return null - nullTarget = nullTargetValue.getTarget(); - } - if ( defaultTargetValue != null ) { - // If the default target value is NULL then we should map it to null - defaultTarget = MappingConstantsPrism.NULL.equals( defaultTargetValue.getTarget() ) ? null : - defaultTargetValue.getTarget(); - } - else { - throwIllegalArgumentException = true; - } + Type sourceType = first( method.getSourceParameters() ).getType(); + Type targetType = method.getResultType(); + // enum-to-enum + if ( sourceType.isEnumType() && targetType.isEnumType() ) { + mappingEntries.addAll( enumToEnumMapping( method, sourceType, targetType ) ); + } + else if ( sourceType.isEnumType() && targetType.isString() ) { + mappingEntries.addAll( enumToStringMapping( method, sourceType ) ); + } + else if ( sourceType.isString() && targetType.isEnumType() ) { + mappingEntries.addAll( stringToEnumMapping( method, targetType ) ); } // do before / after lifecycle mappings @@ -112,32 +90,36 @@ public class ValueMappingMethod extends MappingMethod { LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariables ); // finally return a mapping - return new ValueMappingMethod( method, mappingEntries, nullTarget, defaultTarget, - throwIllegalArgumentException, beforeMappingMethods, afterMappingMethods ); + return new ValueMappingMethod( method, + mappingEntries, + valueMappings.nullValueTarget, + valueMappings.defaultTargetValue, + !valueMappings.hasDefaultValue, + beforeMappingMethods, + afterMappingMethods + ); } - private List enumToEnumMapping(Method method) { + private List enumToEnumMapping(Method method, Type sourceType, Type targetType ) { List mappings = new ArrayList<>(); - List unmappedSourceConstants - = new ArrayList<>( first( method.getSourceParameters() ).getType().getEnumConstants() ); - - - if ( !reportErrorIfMappedEnumConstantsDontExist( method ) ) { + List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); + boolean sourceErrorOccurred = !reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); + boolean targetErrorOccurred = !reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); + if ( sourceErrorOccurred || targetErrorOccurred ) { return mappings; } - // Start to fill the mappings with the defined valuemappings - for ( ValueMapping valueMapping : trueValueMappings ) { + // Start to fill the mappings with the defined value mappings + for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { String target = - MappingConstantsPrism.NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); unmappedSourceConstants.remove( valueMapping.getSource() ); } - // add mappings based on name - if ( applyNamebasedMappings ) { + if ( !valueMappings.hasMapAnyUnmapped ) { // get all target constants List targetConstants = method.getReturnType().getEnumConstants(); @@ -148,7 +130,7 @@ public class ValueMappingMethod extends MappingMethod { } } - if ( defaultTargetValue == null && !unmappedSourceConstants.isEmpty() ) { + if ( valueMappings.defaultTarget == null && !unmappedSourceConstants.isEmpty() ) { String sourceErrorMessage = "source"; String targetErrorMessage = "target"; if ( method instanceof ForgedMethod && ( (ForgedMethod) method ).getHistory() != null ) { @@ -160,7 +142,7 @@ public class ValueMappingMethod extends MappingMethod { // all sources should now be matched, there's no default to fall back to, so if sources remain, // we have an issue. ctx.getMessager().printMessage( method.getExecutable(), - Message.VALUE_MAPPING_UNMAPPED_SOURCES, + Message.VALUEMAPPING_UNMAPPED_SOURCES, sourceErrorMessage, targetErrorMessage, Strings.join( unmappedSourceConstants, ", " ) @@ -171,6 +153,64 @@ public class ValueMappingMethod extends MappingMethod { return mappings; } + private List enumToStringMapping(Method method, Type sourceType ) { + + List mappings = new ArrayList<>(); + List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); + boolean sourceErrorOccurred = !reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); + boolean anyRemainingUsedError = !reportErrorIfSourceEnumConstantsContainsAnyRemaining( method ); + if ( sourceErrorOccurred || anyRemainingUsedError ) { + return mappings; + } + + // Start to fill the mappings with the defined valuemappings + for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { + String target = + NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + unmappedSourceConstants.remove( valueMapping.getSource() ); + } + + // add mappings based on name + if ( !valueMappings.hasMapAnyUnmapped ) { + + // all remaining constants are mapped + for ( String sourceConstant : unmappedSourceConstants ) { + mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + } + } + return mappings; + } + + private List stringToEnumMapping(Method method, Type targetType ) { + + List mappings = new ArrayList<>(); + List unmappedSourceConstants = new ArrayList<>( targetType.getEnumConstants() ); + boolean sourceErrorOccurred = !reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); + boolean mandatoryMissing = !reportErrorIfAnyRemainingOrAnyUnMappedMissing( method ); + if ( sourceErrorOccurred || mandatoryMissing ) { + return mappings; + } + + // Start to fill the mappings with the defined valuemappings + for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { + String target = + NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + unmappedSourceConstants.remove( valueMapping.getSource() ); + } + + // add mappings based on name + if ( !valueMappings.hasMapAnyUnmapped ) { + + // all remaining constants are mapped + for ( String sourceConstant : unmappedSourceConstants ) { + mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + } + } + return mappings; + } + private SelectionParameters getSelectionParameters(Method method, Types typeUtils) { BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() ); if ( beanMappingPrism != null ) { @@ -182,13 +222,12 @@ public class ValueMappingMethod extends MappingMethod { return null; } - private boolean reportErrorIfMappedEnumConstantsDontExist(Method method) { - List sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants(); - List targetEnumConstants = method.getReturnType().getEnumConstants(); + private boolean reportErrorIfMappedSourceEnumConstantsDontExist(Method method, Type sourceType) { + List sourceEnumConstants = sourceType.getEnumConstants(); boolean foundIncorrectMapping = false; - for ( ValueMapping mappedConstant : trueValueMappings ) { + for ( ValueMapping mappedConstant : valueMappings.regularValueMappings ) { if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -196,11 +235,50 @@ public class ValueMappingMethod extends MappingMethod { mappedConstant.getSourceAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, mappedConstant.getSource(), - first( method.getSourceParameters() ).getType() + sourceType ); foundIncorrectMapping = true; } - if ( !MappingConstantsPrism.NULL.equals( mappedConstant.getTarget() ) + } + return !foundIncorrectMapping; + } + + private boolean reportErrorIfSourceEnumConstantsContainsAnyRemaining(Method method) { + boolean foundIncorrectMapping = false; + + if ( valueMappings.hasMapAnyRemaining ) { + ctx.getMessager().printMessage( + method.getExecutable(), + valueMappings.defaultTarget.getMirror(), + valueMappings.defaultTarget.getSourceAnnotationValue(), + Message.VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM, + method.getResultType() + ); + foundIncorrectMapping = true; + } + return !foundIncorrectMapping; + } + + private boolean reportErrorIfAnyRemainingOrAnyUnMappedMissing(Method method) { + boolean foundIncorrectMapping = false; + + if ( !( valueMappings.hasMapAnyUnmapped || valueMappings.hasMapAnyRemaining ) ) { + ctx.getMessager().printMessage( + method.getExecutable(), + Message.VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING + ); + foundIncorrectMapping = true; + } + return !foundIncorrectMapping; + } + + private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, Type targetType) { + List targetEnumConstants = targetType.getEnumConstants(); + + boolean foundIncorrectMapping = false; + + for ( ValueMapping mappedConstant : valueMappings.regularValueMappings ) { + if ( !NULL.equals( mappedConstant.getTarget() ) && !targetEnumConstants.contains( mappedConstant.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), @@ -213,25 +291,25 @@ public class ValueMappingMethod extends MappingMethod { } } - if ( defaultTargetValue != null && !MappingConstantsPrism.NULL.equals( defaultTargetValue.getTarget() ) - && !targetEnumConstants.contains( defaultTargetValue.getTarget() ) ) { + if ( valueMappings.defaultTarget != null && !NULL.equals( valueMappings.defaultTarget.getTarget() ) + && !targetEnumConstants.contains( valueMappings.defaultTarget.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), - defaultTargetValue.getMirror(), - defaultTargetValue.getTargetAnnotationValue(), + valueMappings.defaultTarget.getMirror(), + valueMappings.defaultTarget.getTargetAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, - defaultTargetValue.getTarget(), + valueMappings.defaultTarget.getTarget(), method.getReturnType() ); foundIncorrectMapping = true; } - if ( nullTargetValue != null && MappingConstantsPrism.NULL.equals( nullTargetValue.getTarget() ) - && !targetEnumConstants.contains( nullTargetValue.getTarget() ) ) { + if ( valueMappings.nullTarget != null && NULL.equals( valueMappings.nullTarget.getTarget() ) + && !targetEnumConstants.contains( valueMappings.nullTarget.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), - nullTargetValue.getMirror(), - nullTargetValue.getTargetAnnotationValue(), + valueMappings.nullTarget.getMirror(), + valueMappings.nullTarget.getTargetAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, - nullTargetValue.getTarget(), + valueMappings.nullTarget.getTarget(), method.getReturnType() ); foundIncorrectMapping = true; @@ -241,6 +319,47 @@ public class ValueMappingMethod extends MappingMethod { } } + private static class ValueMappings { + + List regularValueMappings = new ArrayList<>(); + ValueMapping defaultTarget = null; + String defaultTargetValue = null; + ValueMapping nullTarget = null; + String nullValueTarget = null; + boolean hasMapAnyUnmapped = false; + boolean hasMapAnyRemaining = false; + boolean hasDefaultValue = false; + + ValueMappings(List valueMappings) { + + for ( ValueMapping valueMapping : valueMappings ) { + if ( ANY_REMAINING.equals( valueMapping.getSource() ) ) { + defaultTarget = valueMapping; + defaultTargetValue = getValue( defaultTarget ); + hasDefaultValue = true; + hasMapAnyRemaining = true; + } + else if ( ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { + defaultTarget = valueMapping; + defaultTargetValue = getValue( defaultTarget ); + hasDefaultValue = true; + hasMapAnyUnmapped = true; + } + else if ( NULL.equals( valueMapping.getSource() ) ) { + nullTarget = valueMapping; + nullValueTarget = getValue( nullTarget ); + } + else { + regularValueMappings.add( valueMapping ); + } + } + } + + String getValue(ValueMapping valueMapping) { + return NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + } + } + private ValueMappingMethod(Method method, List enumMappings, String nullTarget, String defaultTarget, boolean throwIllegalArgumentException, List beforeMappingMethods, List afterMappingMethods) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index c21906f68..da2bfc76f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -220,6 +220,10 @@ public class Type extends ModelElement implements Comparable { return typeElement != null && typeElement.getModifiers().contains( Modifier.ABSTRACT ); } + public boolean isString() { + return String.class.getName().equals( getFullyQualifiedName() ); + } + /** * @return this type's enum constants in case it is an enum, an empty list otherwise. */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java index a471e1c6a..50ea5df6e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java @@ -5,6 +5,8 @@ */ package org.mapstruct.ap.internal.model.source; +import org.mapstruct.ap.internal.model.common.Type; + import static org.mapstruct.ap.internal.util.Collections.first; /** @@ -18,18 +20,34 @@ public final class MappingMethodUtils { private MappingMethodUtils() { } - /** * Checks if the provided {@code method} is for enum mapping. A Method is an Enum Mapping method when the - * source parameter and result type are enum types. + *
    + *
  1. source parameter type and result type are enum types
  2. + *
  3. source parameter type is a String and result type is an enum type
  4. + *
  5. source parameter type is a enum type and result type is a String
  6. + *
* * @param method to check * * @return {@code true} if the method is for enum mapping, {@code false} otherwise */ public static boolean isEnumMapping(Method method) { - return method.getSourceParameters().size() == 1 - && first( method.getSourceParameters() ).getType().isEnumType() - && method.getResultType().isEnumType(); + if ( method.getSourceParameters().size() != 1 ) { + return false; + } + + Type source = first( method.getSourceParameters() ).getType(); + Type result = method.getResultType(); + if ( source.isEnumType() && result.isEnumType() ) { + return true; + } + if ( source.isString() && result.isEnumType() ) { + return true; + } + if ( source.isEnumType() && result.isString() ) { + return true; + } + return false; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java index 8902d9e8f..cd0f0a431 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java @@ -7,17 +7,18 @@ package org.mapstruct.ap.internal.model.source; import java.util.List; import java.util.Objects; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; -import org.mapstruct.ap.internal.prism.MappingConstantsPrism; import org.mapstruct.ap.internal.prism.ValueMappingPrism; import org.mapstruct.ap.internal.prism.ValueMappingsPrism; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_REMAINING; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_UNMAPPED; + /** * Represents the mapping between one value constant and another. * @@ -51,8 +52,8 @@ public class ValueMapping { mappingPrism.source() ); } - if ( MappingConstantsPrism.ANY_REMAINING.equals( mapping.source ) - || MappingConstantsPrism.ANY_UNMAPPED.equals( mapping.source ) ) { + if ( ANY_REMAINING.equals( mapping.source ) + || ANY_UNMAPPED.equals( mapping.source ) ) { if ( anyFound ) { messager.printMessage( method, @@ -111,8 +112,7 @@ public class ValueMapping { public ValueMapping inverse() { ValueMapping result; - if ( !MappingConstantsPrism.ANY_REMAINING.equals( source ) - || !MappingConstantsPrism.ANY_UNMAPPED.equals( source ) ) { + if ( !(ANY_REMAINING.equals( source ) || ANY_UNMAPPED.equals( source ) ) ) { result = new ValueMapping( target, source, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index e7e2db260..068aa240f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -461,12 +461,12 @@ public class MethodRetrievalProcessor implements ModelElementProcessor\" or \"\" can only be used once." ), - VALUE_MAPPING_UNMAPPED_SOURCES( "The following constants from the %s enum have no corresponding constant in the %s enum and must be be mapped via adding additional mappings: %s." ), + VALUEMAPPING_UNMAPPED_SOURCES( "The following constants from the %s enum have no corresponding constant in the %s enum and must be be mapped via adding additional mappings: %s." ), + 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( "Constant %s doesn't exist in enum type %s." ); // CHECKSTYLE:ON diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/EnumMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/EnumMappingMethod.ftl deleted file mode 100644 index 3d0c9e074..000000000 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/EnumMappingMethod.ftl +++ /dev/null @@ -1,44 +0,0 @@ -<#-- - - Copyright MapStruct Authors. - - Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - ---> -<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.EnumMappingMethod" --> -@Override -public <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, ) { - <#list beforeMappingReferencesWithoutMappingTarget as callback> - <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - <#if !callback_has_next> - - - - if ( ${sourceParameter.name} == null ) { - return null; - } - - <@includeModel object=resultType/> ${resultName}; - - switch ( ${sourceParameter.name} ) { - <#list enumMappings as enumMapping> - case ${enumMapping.source}: ${resultName} = <@includeModel object=returnType/>.${enumMapping.target}; - break; - - default: throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} ); - } - <#list beforeMappingReferencesWithMappingTarget as callback> - <#if callback_index = 0> - - - <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - - <#list afterMappingReferences as callback> - <#if callback_index = 0> - - - <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - - - return ${resultName}; -} 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 5fa1c0a11..ba16d56a2 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 <#if nullTarget??><@includeModel object=returnType/>.${nullTarget}<#else>null; + return <@writeTarget target=nullTarget/>; } <@includeModel object=resultType/> ${resultName}; switch ( ${sourceParameter.name} ) { <#list valueMappings as valueMapping> - case ${valueMapping.source}: ${resultName} = <#if valueMapping.target??><@includeModel object=returnType/>.${valueMapping.target}<#else>null; + case <@writeSource source=valueMapping.source/>: ${resultName} = <@writeTarget target=valueMapping.target/>; break; - default: <#if throwIllegalArgumentException>throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <#if defaultTarget??><@includeModel object=returnType/>.${defaultTarget}<#else>null; + default: <#if throwIllegalArgumentException>throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; } <#list beforeMappingReferencesWithMappingTarget as callback> <#if callback_index = 0> @@ -40,5 +40,29 @@ <@includeModel object=callback targetBeanName=resultName targetType=resultType/> + <#if !(valueMappings.empty && throwIllegalArgumentException)> return ${resultName}; + } +<#macro writeSource source=""> + <@compress single_line=true> + <#if sourceParameter.type.enumType> + ${source} + <#elseif sourceParameter.type.string> + "${source}" + + + +<#macro writeTarget target=""> + <@compress single_line=true> + <#if target?has_content> + <#if returnType.enumType> + <@includeModel object=returnType/>.${target} + <#elseif returnType.string> + "${target}" + + <#else> + null + + + \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapper.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapper.java index 6228e4b61..4e46a4a02 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapper.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java similarity index 97% rename from processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java index aa1e0ea02..51f867947 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import static org.assertj.core.api.Assertions.assertThat; @@ -12,6 +12,8 @@ import javax.tools.Diagnostic.Kind; import org.junit.Rule; 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; @@ -214,7 +216,7 @@ public class EnumToEnumMappingTest { diagnostics = { @Diagnostic(type = ErroneousOrderMapperMappingSameConstantTwice.class, kind = Kind.ERROR, - line = 25, + line = 27, messageRegExp = "Source value mapping: \"EXTRA\" cannot be mapped more than once\\.") } ) @@ -228,11 +230,11 @@ public class EnumToEnumMappingTest { diagnostics = { @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, kind = Kind.ERROR, - line = 24, + line = 26, messageRegExp = "Constant FOO doesn't exist in enum type org.mapstruct.ap.test.value.OrderType\\."), @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, kind = Kind.ERROR, - line = 25, + line = 27, messageRegExp = "Constant BAR doesn't exist in enum type org.mapstruct.ap.test.value." + "ExternalOrderType\\.") } @@ -247,7 +249,7 @@ public class EnumToEnumMappingTest { diagnostics = { @Diagnostic(type = ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.class, kind = Kind.ERROR, - line = 21, + line = 23, messageRegExp = "The following constants from the source enum have no corresponding constant in the " + "target enum and must be be mapped via adding additional mappings: EXTRA, STANDARD, NORMAL") } @@ -262,7 +264,7 @@ public class EnumToEnumMappingTest { diagnostics = { @Diagnostic(type = ErroneousOrderMapperDuplicateANY.class, kind = Kind.ERROR, - line = 26, + line = 28, messageRegExp = "Source = \"\" or \"\" can only be used once\\." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperDuplicateANY.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperDuplicateANY.java index 72989be31..d6d9076b2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperDuplicateANY.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +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; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperMappingSameConstantTwice.java similarity index 85% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperMappingSameConstantTwice.java index 62c6bc334..cc4341394 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperMappingSameConstantTwice.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +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; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java similarity index 80% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java index d39d57909..767522ffd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.Mapper; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperUsingUnknownEnumConstants.java similarity index 83% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperUsingUnknownEnumConstants.java index 49569f1fa..6dacf98c7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperUsingUnknownEnumConstants.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +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; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderDto.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderDto.java index d0eb104a5..ef756c5de 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderDto.java @@ -3,8 +3,9 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; +import org.mapstruct.ap.test.value.ExternalOrderType; /** * @author Gunnar Morling diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderEntity.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderEntity.java index 62a5f4148..247db83d4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderEntity.java @@ -3,8 +3,9 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; +import org.mapstruct.ap.test.value.OrderType; /** * @author Gunnar Morling diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderMapper.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderMapper.java index 82996f73d..e30d0cfc7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderMapper.java @@ -3,12 +3,14 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.InheritInverseConfiguration; 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; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java similarity index 91% rename from processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java index 29767c1a6..46ffb8b1c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; @@ -12,6 +12,8 @@ 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; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java new file mode 100644 index 000000000..2f1be5312 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java @@ -0,0 +1,60 @@ +/* + * 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.enum2string; + +import org.junit.Test; +import org.junit.runner.RunWith; +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; + +@IssueKey( "1557" ) +@WithClasses({ OrderType.class, OrderMapper.class }) +@RunWith(AnnotationProcessorTestRunner.class) +public class EnumToStringMappingTest { + + @Test + public void testTheNormalStuff() { + assertThat( OrderMapper.INSTANCE.mapNormal( null ) ).isNull(); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.EXTRA ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.STANDARD ) ).isEqualTo( "DEFAULT" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.NORMAL ) ).isEqualTo( "DEFAULT" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.B2B ) ).isEqualTo( "B2B" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.RETAIL ) ).isEqualTo( "RETAIL" ); + } + + @Test + public void testRemainingAndNull() { + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( null ) ).isEqualTo( "DEFAULT" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.STANDARD ) ).isNull(); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.NORMAL ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.B2B ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.RETAIL ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.EXTRA ) ).isEqualTo( "SPECIAL" ); + } + + @Test + @WithClasses(ErroneousOrderMapperUsingAnyRemaining.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperUsingAnyRemaining.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 26, + messageRegExp = "\"\" can only be used on targets of type enum and not for " + + "java\\.lang\\.String\\.") + } + ) + public void shouldRaiseErrorWhenUsingAnyRemaining() { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/ErroneousOrderMapperUsingAnyRemaining.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/ErroneousOrderMapperUsingAnyRemaining.java new file mode 100644 index 000000000..536586529 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/ErroneousOrderMapperUsingAnyRemaining.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.value.enum2string; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ErroneousOrderMapperUsingAnyRemaining { + + ErroneousOrderMapperUsingAnyRemaining INSTANCE = Mappers.getMapper( ErroneousOrderMapperUsingAnyRemaining.class ); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" ) + }) + String mapWithRemainingAndNull(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java new file mode 100644 index 000000000..35d9f57f9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java @@ -0,0 +1,36 @@ +/* + * 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.enum2string; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT") + }) + String mapNormal(OrderType orderType); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "SPECIAL" ) + }) + String withAnyUnmappedAndNull(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.java new file mode 100644 index 000000000..06e9c05c9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.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.value.string2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped { + + ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped INSTANCE = + Mappers.getMapper( ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class ); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "STANDARD" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ) + }) + OrderType map(String orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/OrderMapper.java new file mode 100644 index 000000000..9c43835f4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/OrderMapper.java @@ -0,0 +1,36 @@ +/* + * 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.string2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + @ValueMappings({ + @ValueMapping(source = "SPECIAL", target = "EXTRA" ), + @ValueMapping(source = "DEFAULT", target = "STANDARD"), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "RETAIL" ) + }) + OrderType mapNormal(String orderType); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "STANDARD" ), + @ValueMapping( source = "DEFAULT", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "RETAIL" ) + }) + OrderType mapWithAnyUnmapped(String orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java new file mode 100644 index 000000000..552766d24 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.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.test.value.string2enum; + +import org.junit.Test; +import org.junit.runner.RunWith; +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; + +@IssueKey( "1557" ) +@WithClasses({ OrderType.class, OrderMapper.class }) +@RunWith(AnnotationProcessorTestRunner.class) +public class StringToEnumMappingTest { + + @Test + public void testTheNormalStuff() { + assertThat( OrderMapper.INSTANCE.mapNormal( null ) ).isNull(); + assertThat( OrderMapper.INSTANCE.mapNormal( "SPECIAL" ) ).isEqualTo( OrderType.EXTRA ); + assertThat( OrderMapper.INSTANCE.mapNormal( "DEFAULT" ) ).isEqualTo( OrderType.STANDARD ); + assertThat( OrderMapper.INSTANCE.mapNormal( "RETAIL" ) ).isEqualTo( OrderType.RETAIL ); + assertThat( OrderMapper.INSTANCE.mapNormal( "B2B" ) ).isEqualTo( OrderType.B2B ); + assertThat( OrderMapper.INSTANCE.mapNormal( "STANDARD" ) ).isEqualTo( OrderType.STANDARD ); + assertThat( OrderMapper.INSTANCE.mapNormal( "NORMAL" ) ).isEqualTo( OrderType.NORMAL ); + assertThat( OrderMapper.INSTANCE.mapNormal( "blah" ) ).isEqualTo( OrderType.RETAIL ); + } + + @Test + public void testRemainingAndNull() { + assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( null ) ).isEqualTo( OrderType.STANDARD ); + assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( "DEFAULT" ) ).isNull(); + assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( "BLAH" ) ).isEqualTo( OrderType.RETAIL ); + } + + @Test + @WithClasses(ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 28, + messageRegExp = "Source = \"\" or \"\" is advisable for mapping of " + + "type String to an enum type" ) + } + ) + public void shouldRaiseErrorWhenUsingAnyRemaining() { + } + +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/DefaultOrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java similarity index 88% rename from processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/DefaultOrderMapperImpl.java rename to processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java index 3818038de..fe44bab44 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/DefaultOrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import javax.annotation.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; @Generated( value = "org.mapstruct.ap.MappingProcessor", diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/OrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java similarity index 93% rename from processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/OrderMapperImpl.java rename to processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java index dbcd2cb01..31f6b6989 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/OrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import javax.annotation.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; @Generated( value = "org.mapstruct.ap.MappingProcessor", diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/SpecialOrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java similarity index 94% rename from processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/SpecialOrderMapperImpl.java rename to processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java index 8ff732ae2..f420a63ba 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/SpecialOrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import javax.annotation.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; @Generated( value = "org.mapstruct.ap.MappingProcessor",