#2169 Add support for defining a custom unexpected value mapping exception

Expose definition via:

* `@EnumMapping`
* `@Mapper`
* `@MapperConfig`
* `EnumMappingStrategy` SPI

Rename `EnumNamingStrategy` to `EnumMappingStrategy`
This commit is contained in:
Filip Hrisafov 2020-08-29 13:53:30 +02:00 committed by GitHub
parent a9451b1159
commit 7dcbef349d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1014 additions and 72 deletions

View File

@ -111,7 +111,7 @@ public @interface EnumMapping {
* *
* @return the name transformation strategy * @return the name transformation strategy
*/ */
String nameTransformationStrategy(); String nameTransformationStrategy() default "";
/** /**
* The configuration that should be passed on the appropriate name transformation strategy. * The configuration that should be passed on the appropriate name transformation strategy.
@ -119,5 +119,26 @@ public @interface EnumMapping {
* *
* @return the configuration to use * @return the configuration to use
*/ */
String configuration(); String configuration() default "";
/**
* Exception that should be thrown by the generated code if no mapping matches.
* If no exception is configured, the exception given via {@link MapperConfig#unexpectedValueMappingException()} or
* {@link Mapper#unexpectedValueMappingException()} will be used, using {@link IllegalArgumentException} by default.
*
* <p>
* Note:
* <ul>
* <li>
* The defined exception should at least have a constructor with a {@link String} parameter.
* </li>
* <li>
* If the defined exception is a checked exception then the enum mapping methods should have that exception
* in the throws clause.
* </li>
* </ul>
*
* @return the exception that should be used in the generated code
*/
Class<? extends Exception> unexpectedValueMappingException() default IllegalArgumentException.class;
} }

View File

@ -296,4 +296,27 @@ public @interface Mapper {
* @see org.mapstruct.control.MappingControl * @see org.mapstruct.control.MappingControl
*/ */
Class<? extends Annotation> mappingControl() default MappingControl.class; Class<? extends Annotation> mappingControl() default MappingControl.class;
/**
* Exception that should be thrown by the generated code if no mapping matches for enums.
* If no exception is configured, the exception given via {@link MapperConfig#unexpectedValueMappingException()}
* will be used, using {@link IllegalArgumentException} by default.
*
* <p>
* Note:
* <ul>
* <li>
* The defined exception should at least have a constructor with a {@link String} parameter.
* </li>
* <li>
* If the defined exception is a checked exception then the enum mapping methods should have that exception
* in the throws clause.
* </li>
* </ul>
*
* @return the exception that should be used in the generated code
*
* @since 1.4
*/
Class<? extends Exception> unexpectedValueMappingException() default IllegalArgumentException.class;
} }

View File

@ -273,5 +273,27 @@ public @interface MapperConfig {
*/ */
Class<? extends Annotation> mappingControl() default MappingControl.class; Class<? extends Annotation> mappingControl() default MappingControl.class;
/**
* Exception that should be thrown by the generated code if no mapping matches for enums.
* If no exception is configured, {@link IllegalArgumentException} will be used by default.
*
* <p>
* Note:
* <ul>
* <li>
* The defined exception should at least have a constructor with a {@link String} parameter.
* </li>
* <li>
* If the defined exception is a checked exception then the enum mapping methods should have that exception
* in the throws clause.
* </li>
* </ul>
*
* @return the exception that should be used in the generated code
*
* @since 1.4
*/
Class<? extends Exception> unexpectedValueMappingException() default IllegalArgumentException.class;
} }

View File

@ -202,7 +202,7 @@ include::{processor-ap-main}/spi/NoOpBuilderProvider.java[tag=documentation]
[[custom-enum-naming-strategy]] [[custom-enum-naming-strategy]]
=== Custom Enum Naming Strategy === Custom Enum Naming Strategy
MapStruct offers the possibility to override the `EnumNamingStrategy` via the Service Provider Interface (SPI). MapStruct offers the possibility to override the `EnumMappingStrategy` via the Service Provider Interface (SPI).
This can be used when you have certain enums that follow some conventions within your organization. This can be used when you have certain enums that follow some conventions within your organization.
For example all enums which implement an interface named `CustomEnumMarker` are prefixed with `CUSTOM_` For example all enums which implement an interface named `CustomEnumMarker` are prefixed with `CUSTOM_`
and the default value for them when mapping from `null` is `UNSPECIFIED` and the default value for them when mapping from `null` is `UNSPECIFIED`
@ -250,15 +250,15 @@ public interface CheeseTypeMapper {
---- ----
==== ====
This can be achieved with implementing the SPI `org.mapstruct.ap.spi.EnumNamingStrategy` as in the following example. This can be achieved with implementing the SPI `org.mapstruct.ap.spi.EnumMappingStrategy` as in the following example.
Heres an implemented `org.mapstruct.ap.spi.EnumNamingStrategy`: Heres an implemented `org.mapstruct.ap.spi.EnumMappingStrategy`:
.Custom enum naming strategy .Custom enum naming strategy
==== ====
[source, java, linenums] [source, java, linenums]
[subs="verbatim,attributes"] [subs="verbatim,attributes"]
---- ----
public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy { public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy {
@Override @Override
public String getDefaultNullEnumConstant(TypeElement enumType) { public String getDefaultNullEnumConstant(TypeElement enumType) {

View File

@ -30,7 +30,7 @@ import org.mapstruct.ap.internal.option.Options;
import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.AccessorNamingUtils;
import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.Services; import org.mapstruct.ap.internal.util.Services;
import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumMappingStrategy;
import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy;
import org.mapstruct.ap.spi.MappingExclusionProvider; import org.mapstruct.ap.spi.MappingExclusionProvider;
@ -107,7 +107,7 @@ public class MappingBuilderContext {
private final Types typeUtils; private final Types typeUtils;
private final FormattingMessager messager; private final FormattingMessager messager;
private final AccessorNamingUtils accessorNaming; private final AccessorNamingUtils accessorNaming;
private final EnumNamingStrategy enumNamingStrategy; private final EnumMappingStrategy enumMappingStrategy;
private final Map<String, EnumTransformationStrategy> enumTransformationStrategies; private final Map<String, EnumTransformationStrategy> enumTransformationStrategies;
private final Options options; private final Options options;
private final TypeElement mapperTypeElement; private final TypeElement mapperTypeElement;
@ -124,7 +124,7 @@ public class MappingBuilderContext {
Types typeUtils, Types typeUtils,
FormattingMessager messager, FormattingMessager messager,
AccessorNamingUtils accessorNaming, AccessorNamingUtils accessorNaming,
EnumNamingStrategy enumNamingStrategy, EnumMappingStrategy enumMappingStrategy,
Map<String, EnumTransformationStrategy> enumTransformationStrategies, Map<String, EnumTransformationStrategy> enumTransformationStrategies,
Options options, Options options,
MappingResolver mappingResolver, MappingResolver mappingResolver,
@ -136,7 +136,7 @@ public class MappingBuilderContext {
this.typeUtils = typeUtils; this.typeUtils = typeUtils;
this.messager = messager; this.messager = messager;
this.accessorNaming = accessorNaming; this.accessorNaming = accessorNaming;
this.enumNamingStrategy = enumNamingStrategy; this.enumMappingStrategy = enumMappingStrategy;
this.enumTransformationStrategies = enumTransformationStrategies; this.enumTransformationStrategies = enumTransformationStrategies;
this.options = options; this.options = options;
this.mappingResolver = mappingResolver; this.mappingResolver = mappingResolver;
@ -191,8 +191,8 @@ public class MappingBuilderContext {
return accessorNaming; return accessorNaming;
} }
public EnumNamingStrategy getEnumNamingStrategy() { public EnumMappingStrategy getEnumMappingStrategy() {
return enumNamingStrategy; return enumMappingStrategy;
} }
public Map<String, EnumTransformationStrategy> getEnumTransformationStrategies() { public Map<String, EnumTransformationStrategy> getEnumTransformationStrategies() {

View File

@ -43,7 +43,9 @@ public class ValueMappingMethod extends MappingMethod {
private final List<MappingEntry> valueMappings; private final List<MappingEntry> valueMappings;
private final String defaultTarget; private final String defaultTarget;
private final String nullTarget; private final String nullTarget;
private final boolean throwIllegalArgumentException;
private final Type unexpectedValueMappingException;
private final boolean overridden; private final boolean overridden;
public static class Builder { public static class Builder {
@ -90,7 +92,7 @@ public class ValueMappingMethod extends MappingMethod {
if ( targetType.isEnumType() && valueMappings.nullTarget == null ) { if ( targetType.isEnumType() && valueMappings.nullTarget == null ) {
// If null target is not set it means that the user has not explicitly defined a mapping for null // If null target is not set it means that the user has not explicitly defined a mapping for null
valueMappings.nullValueTarget = ctx.getEnumNamingStrategy() valueMappings.nullValueTarget = ctx.getEnumMappingStrategy()
.getDefaultNullEnumConstant( targetType.getTypeElement() ); .getDefaultNullEnumConstant( targetType.getTypeElement() );
} }
@ -118,14 +120,14 @@ public class ValueMappingMethod extends MappingMethod {
mappingEntries, mappingEntries,
valueMappings.nullValueTarget, valueMappings.nullValueTarget,
valueMappings.defaultTargetValue, valueMappings.defaultTargetValue,
!valueMappings.hasDefaultValue, determineUnexpectedValueMappingException(),
beforeMappingMethods, beforeMappingMethods,
afterMappingMethods afterMappingMethods
); );
} }
private void initializeEnumTransformationStrategy() { private void initializeEnumTransformationStrategy() {
if ( !enumMapping.hasAnnotation() ) { if ( !enumMapping.hasNameTransformationStrategy() ) {
enumTransformationInvoker = EnumTransformationStrategyInvoker.DEFAULT; enumTransformationInvoker = EnumTransformationStrategyInvoker.DEFAULT;
} }
else { else {
@ -290,7 +292,7 @@ public class ValueMappingMethod extends MappingMethod {
} }
private String getEnumConstant(TypeElement typeElement, String enumConstant) { private String getEnumConstant(TypeElement typeElement, String enumConstant) {
return ctx.getEnumNamingStrategy().getEnumConstant( typeElement, enumConstant ); return ctx.getEnumMappingStrategy().getEnumConstant( typeElement, enumConstant );
} }
private SelectionParameters getSelectionParameters(Method method, Types typeUtils) { private SelectionParameters getSelectionParameters(Method method, Types typeUtils) {
@ -408,12 +410,26 @@ public class ValueMappingMethod extends MappingMethod {
Message.VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI, Message.VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI,
valueMappings.nullValueTarget, valueMappings.nullValueTarget,
method.getReturnType(), method.getReturnType(),
ctx.getEnumNamingStrategy() ctx.getEnumMappingStrategy()
); );
} }
return !foundIncorrectMapping; return !foundIncorrectMapping;
} }
private Type determineUnexpectedValueMappingException() {
if ( !valueMappings.hasDefaultValue ) {
TypeMirror unexpectedValueMappingException = enumMapping.getUnexpectedValueMappingException();
if ( unexpectedValueMappingException != null ) {
return ctx.getTypeFactory().getType( unexpectedValueMappingException );
}
return ctx.getTypeFactory()
.getType( ctx.getEnumMappingStrategy().getUnexpectedValueMappingExceptionType() );
}
return null;
}
} }
private static class EnumTransformationStrategyInvoker { private static class EnumTransformationStrategyInvoker {
@ -485,16 +501,28 @@ public class ValueMappingMethod extends MappingMethod {
} }
private ValueMappingMethod(Method method, List<MappingEntry> enumMappings, String nullTarget, String defaultTarget, private ValueMappingMethod(Method method, List<MappingEntry> enumMappings, String nullTarget, String defaultTarget,
boolean throwIllegalArgumentException, List<LifecycleCallbackMethodReference> beforeMappingMethods, Type unexpectedValueMappingException,
List<LifecycleCallbackMethodReference> beforeMappingMethods,
List<LifecycleCallbackMethodReference> afterMappingMethods) { List<LifecycleCallbackMethodReference> afterMappingMethods) {
super( method, beforeMappingMethods, afterMappingMethods ); super( method, beforeMappingMethods, afterMappingMethods );
this.valueMappings = enumMappings; this.valueMappings = enumMappings;
this.nullTarget = nullTarget; this.nullTarget = nullTarget;
this.defaultTarget = defaultTarget; this.defaultTarget = defaultTarget;
this.throwIllegalArgumentException = throwIllegalArgumentException; this.unexpectedValueMappingException = unexpectedValueMappingException;
this.overridden = method.overridesMethod(); this.overridden = method.overridesMethod();
} }
@Override
public Set<Type> getImportTypes() {
Set<Type> importTypes = super.getImportTypes();
if ( unexpectedValueMappingException != null && !unexpectedValueMappingException.isJavaLangType() ) {
importTypes.addAll( unexpectedValueMappingException.getImportTypes() );
}
return importTypes;
}
public List<MappingEntry> getValueMappings() { public List<MappingEntry> getValueMappings() {
return valueMappings; return valueMappings;
} }
@ -507,8 +535,8 @@ public class ValueMappingMethod extends MappingMethod {
return nullTarget; return nullTarget;
} }
public boolean isThrowIllegalArgumentException() { public Type getUnexpectedValueMappingException() {
return throwIllegalArgumentException; return unexpectedValueMappingException;
} }
public Parameter getSourceParameter() { public Parameter getSourceParameter() {

View File

@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.model.source;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.option.Options;
@ -127,6 +128,11 @@ public class DefaultOptions extends DelegatingOptions {
return MappingControl.fromTypeMirror( mapper.mappingControl().getDefaultValue(), elementUtils ); return MappingControl.fromTypeMirror( mapper.mappingControl().getDefaultValue(), elementUtils );
} }
@Override
public TypeMirror getUnexpectedValueMappingException() {
return null;
}
@Override @Override
public boolean hasAnnotation() { public boolean hasAnnotation() {
return false; return false;

View File

@ -105,6 +105,10 @@ public abstract class DelegatingOptions {
return next.getMappingControl( elementUtils ); return next.getMappingControl( elementUtils );
} }
public TypeMirror getUnexpectedValueMappingException() {
return next.getUnexpectedValueMappingException();
}
DelegatingOptions next() { DelegatingOptions next() {
return next; return next;
} }

View File

@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.model.source;
import java.util.Map; import java.util.Map;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.internal.gem.EnumMappingGem; import org.mapstruct.ap.internal.gem.EnumMappingGem;
import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.FormattingMessager;
@ -14,6 +15,8 @@ import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy;
import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY; import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY;
import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_MISSING_CONFIGURATION;
import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_NO_ELEMENTS;
/** /**
* @author Filip Hrisafov * @author Filip Hrisafov
@ -40,12 +43,25 @@ public class EnumMappingOptions extends DelegatingOptions {
return valid; return valid;
} }
public boolean hasNameTransformationStrategy() {
return hasAnnotation() && Strings.isNotEmpty( getNameTransformationStrategy() );
}
public String getNameTransformationStrategy() { public String getNameTransformationStrategy() {
return enumMapping.nameTransformationStrategy().get(); return enumMapping.nameTransformationStrategy().getValue();
} }
public String getNameTransformationConfiguration() { public String getNameTransformationConfiguration() {
return enumMapping.configuration().get(); return enumMapping.configuration().getValue();
}
@Override
public TypeMirror getUnexpectedValueMappingException() {
if ( enumMapping != null && enumMapping.unexpectedValueMappingException().hasValue() ) {
return enumMapping.unexpectedValueMappingException().getValue();
}
return next().getUnexpectedValueMappingException();
} }
public boolean isInverse() { public boolean isInverse() {
@ -79,21 +95,47 @@ public class EnumMappingOptions extends DelegatingOptions {
Map<String, EnumTransformationStrategy> enumTransformationStrategies, FormattingMessager messager) { Map<String, EnumTransformationStrategy> enumTransformationStrategies, FormattingMessager messager) {
String strategy = gem.nameTransformationStrategy().getValue(); String strategy = gem.nameTransformationStrategy().getValue();
String configuration = gem.configuration().getValue();
if ( !enumTransformationStrategies.containsKey( strategy ) ) { boolean isConsistent = false;
String registeredStrategies = Strings.join( enumTransformationStrategies.keySet(), ", " );
if ( Strings.isNotEmpty( strategy ) || Strings.isNotEmpty( configuration ) ) {
if ( !enumTransformationStrategies.containsKey( strategy ) ) {
String registeredStrategies = Strings.join( enumTransformationStrategies.keySet(), ", " );
messager.printMessage(
method,
gem.mirror(),
gem.nameTransformationStrategy().getAnnotationValue(),
ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY,
strategy,
registeredStrategies
);
return false;
}
else if ( Strings.isEmpty( configuration ) ) {
messager.printMessage(
method,
gem.mirror(),
gem.configuration().getAnnotationValue(),
ENUMMAPPING_MISSING_CONFIGURATION
);
return false;
}
isConsistent = true;
}
isConsistent = isConsistent || gem.unexpectedValueMappingException().hasValue();
if ( !isConsistent ) {
messager.printMessage( messager.printMessage(
method, method,
gem.mirror(), gem.mirror(),
gem.nameTransformationStrategy().getAnnotationValue(), ENUMMAPPING_NO_ELEMENTS
ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY,
strategy,
registeredStrategies
); );
return false;
} }
return true; return isConsistent;
} }
} }

View File

@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.model.source;
import java.util.Set; import java.util.Set;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.BuilderGem;
@ -137,6 +138,13 @@ public class MapperConfigOptions extends DelegatingOptions {
next().getMappingControl( elementUtils ); next().getMappingControl( elementUtils );
} }
@Override
public TypeMirror getUnexpectedValueMappingException() {
return mapperConfig.unexpectedValueMappingException().hasValue() ?
mapperConfig.unexpectedValueMappingException().get() :
next().getUnexpectedValueMappingException();
}
@Override @Override
public boolean hasAnnotation() { public boolean hasAnnotation() {
return mapperConfig != null; return mapperConfig != null;

View File

@ -11,6 +11,7 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.option.Options;
@ -167,6 +168,13 @@ public class MapperOptions extends DelegatingOptions {
next().getMappingControl( elementUtils ); next().getMappingControl( elementUtils );
} }
@Override
public TypeMirror getUnexpectedValueMappingException() {
return mapper.unexpectedValueMappingException().hasValue() ?
mapper.unexpectedValueMappingException().get() :
next().getUnexpectedValueMappingException();
}
// @Mapper specific // @Mapper specific
public DeclaredType mapperConfigType() { public DeclaredType mapperConfigType() {

View File

@ -26,7 +26,7 @@ import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.RoundContext;
import org.mapstruct.ap.internal.util.workarounds.TypesDecorator; import org.mapstruct.ap.internal.util.workarounds.TypesDecorator;
import org.mapstruct.ap.internal.version.VersionInformation; import org.mapstruct.ap.internal.version.VersionInformation;
import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumMappingStrategy;
import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy;
/** /**
@ -101,8 +101,8 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
} }
@Override @Override
public EnumNamingStrategy getEnumNamingStrategy() { public EnumMappingStrategy getEnumMappingStrategy() {
return roundContext.getAnnotationProcessorContext().getEnumNamingStrategy(); return roundContext.getAnnotationProcessorContext().getEnumMappingStrategy();
} }
@Override @Override

View File

@ -101,7 +101,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
typeUtils, typeUtils,
messager, messager,
accessorNaming, accessorNaming,
context.getEnumNamingStrategy(), context.getEnumMappingStrategy(),
context.getEnumTransformationStrategies(), context.getEnumTransformationStrategies(),
options, options,
new MappingResolverImpl( new MappingResolverImpl(

View File

@ -17,7 +17,7 @@ import org.mapstruct.ap.internal.option.Options;
import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.AccessorNamingUtils;
import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.version.VersionInformation; import org.mapstruct.ap.internal.version.VersionInformation;
import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumMappingStrategy;
import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy;
/** /**
@ -56,7 +56,7 @@ public interface ModelElementProcessor<P, R> {
Map<String, EnumTransformationStrategy> getEnumTransformationStrategies(); Map<String, EnumTransformationStrategy> getEnumTransformationStrategies();
EnumNamingStrategy getEnumNamingStrategy(); EnumMappingStrategy getEnumMappingStrategy();
Options getOptions(); Options getOptions();

View File

@ -24,8 +24,8 @@ import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.BuilderProvider;
import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy; import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy;
import org.mapstruct.ap.spi.DefaultBuilderProvider; import org.mapstruct.ap.spi.DefaultBuilderProvider;
import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; import org.mapstruct.ap.spi.DefaultEnumMappingStrategy;
import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumMappingStrategy;
import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy;
import org.mapstruct.ap.spi.FreeBuilderAccessorNamingStrategy; import org.mapstruct.ap.spi.FreeBuilderAccessorNamingStrategy;
import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy;
@ -43,7 +43,7 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen
private BuilderProvider builderProvider; private BuilderProvider builderProvider;
private AccessorNamingStrategy accessorNamingStrategy; private AccessorNamingStrategy accessorNamingStrategy;
private EnumNamingStrategy enumNamingStrategy; private EnumMappingStrategy enumMappingStrategy;
private boolean initialized; private boolean initialized;
private Map<String, EnumTransformationStrategy> enumTransformationStrategies; private Map<String, EnumTransformationStrategy> enumTransformationStrategies;
@ -113,13 +113,13 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen
} }
this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy ); this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy );
this.enumNamingStrategy = Services.get( EnumNamingStrategy.class, new DefaultEnumNamingStrategy() ); this.enumMappingStrategy = Services.get( EnumMappingStrategy.class, new DefaultEnumMappingStrategy() );
this.enumNamingStrategy.init( this ); this.enumMappingStrategy.init( this );
if ( verbose ) { if ( verbose ) {
messager.printMessage( messager.printMessage(
Diagnostic.Kind.NOTE, Diagnostic.Kind.NOTE,
"MapStruct: Using enum naming strategy: " "MapStruct: Using enum naming strategy: "
+ this.enumNamingStrategy.getClass().getCanonicalName() + this.enumMappingStrategy.getClass().getCanonicalName()
); );
} }
@ -250,9 +250,9 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen
return accessorNamingStrategy; return accessorNamingStrategy;
} }
public EnumNamingStrategy getEnumNamingStrategy() { public EnumMappingStrategy getEnumMappingStrategy() {
initialize(); initialize();
return enumNamingStrategy; return enumMappingStrategy;
} }
public BuilderProvider getBuilderProvider() { public BuilderProvider getBuilderProvider() {

View File

@ -105,6 +105,8 @@ public enum Message {
ENUMMAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ), ENUMMAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ),
ENUMMAPPING_REMOVED( "Mapping of Enums via @Mapping is removed. Please use @ValueMapping instead!" ), ENUMMAPPING_REMOVED( "Mapping of Enums via @Mapping is removed. Please use @ValueMapping instead!" ),
ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY( "There is no registered EnumTransformationStrategy for '%s'. Registered strategies are: %s." ), ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY( "There is no registered EnumTransformationStrategy for '%s'. Registered strategies are: %s." ),
ENUMMAPPING_MISSING_CONFIGURATION( "Configuration has to be defined when strategy is defined." ),
ENUMMAPPING_NO_ELEMENTS( "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are undefined in @EnumMapping, define at least one of them." ),
LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS( "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to name the parameters in the lifecycle and mapping method identical. This lifecycle method will not be used for the mapping method '%s'.", Diagnostic.Kind.WARNING), LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS( "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to name the parameters in the lifecycle and mapping method identical. This lifecycle method will not be used for the mapping method '%s'.", Diagnostic.Kind.WARNING),
@ -169,7 +171,7 @@ public enum Message {
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_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 = \"<ANY_REMAINING>\" can only be used on targets of type enum and not for %s." ), VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM( "Source = \"<ANY_REMAINING>\" can only be used on targets of type enum and not for %s." ),
VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"<ANY_REMAINING>\" or \"<ANY_UNMAPPED>\" is advisable for mapping of type String to an enum type.", Diagnostic.Kind.WARNING ), VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"<ANY_REMAINING>\" or \"<ANY_UNMAPPED>\" 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 EnumNamingStrategy: %s"), 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." );
// CHECKSTYLE:ON // CHECKSTYLE:ON

View File

@ -129,6 +129,10 @@ public class Strings {
return string == null || string.isEmpty(); return string == null || string.isEmpty();
} }
public static boolean isNotEmpty(String string) {
return !isEmpty( string );
}
public static String getSafeVariableName(String name, String... existingVariableNames) { public static String getSafeVariableName(String name, String... existingVariableNames) {
return getSafeVariableName( name, Arrays.asList( existingVariableNames ) ); return getSafeVariableName( name, Arrays.asList( existingVariableNames ) );
} }

View File

@ -14,7 +14,7 @@ import javax.lang.model.util.Types;
* *
* @since 1.4 * @since 1.4
*/ */
public class DefaultEnumNamingStrategy implements EnumNamingStrategy { public class DefaultEnumMappingStrategy implements EnumMappingStrategy {
protected Elements elementUtils; protected Elements elementUtils;
protected Types typeUtils; protected Types typeUtils;
@ -34,4 +34,13 @@ public class DefaultEnumNamingStrategy implements EnumNamingStrategy {
public String getEnumConstant(TypeElement enumType, String enumConstant) { public String getEnumConstant(TypeElement enumType, String enumConstant) {
return enumConstant; return enumConstant;
} }
@Override
public TypeElement getUnexpectedValueMappingExceptionType() {
return elementUtils.getTypeElement( getUnexpectedValueMappingExceptionClass().getCanonicalName() );
}
protected Class<? extends Exception> getUnexpectedValueMappingExceptionClass() {
return IllegalArgumentException.class;
}
} }

View File

@ -13,11 +13,12 @@ import org.mapstruct.util.Experimental;
* A service provider interface for the mapping between different enum constants * A service provider interface for the mapping between different enum constants
* *
* @author Arne Seime * @author Arne Seime
* @author Filip Hrisafov
* *
* @since 1.4 * @since 1.4
*/ */
@Experimental("This SPI can have it's signature changed in subsequent releases") @Experimental("This SPI can have it's signature changed in subsequent releases")
public interface EnumNamingStrategy { public interface EnumMappingStrategy {
/** /**
* Initializes the enum value mapping strategy * Initializes the enum value mapping strategy
@ -47,4 +48,12 @@ public interface EnumNamingStrategy {
* never return null * never return null
*/ */
String getEnumConstant(TypeElement enumType, String enumConstant); String getEnumConstant(TypeElement enumType, String enumConstant);
/**
* Return the type element of the exception that should be used in the generated code
* for an unexpected enum constant.
*
* @return the type element of the exception that should be used, never {@code null}
*/
TypeElement getUnexpectedValueMappingExceptionType();
} }

View File

@ -25,7 +25,7 @@
case <@writeSource source=valueMapping.source/>: ${resultName} = <@writeTarget target=valueMapping.target/>; case <@writeSource source=valueMapping.source/>: ${resultName} = <@writeTarget target=valueMapping.target/>;
break; break;
</#list> </#list>
default: <#if throwIllegalArgumentException>throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/></#if>; default: <#if unexpectedValueMappingException??>throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/></#if>;
} }
<#list beforeMappingReferencesWithMappingTarget as callback> <#list beforeMappingReferencesWithMappingTarget as callback>
<#if callback_index = 0> <#if callback_index = 0>
@ -40,7 +40,7 @@
<@includeModel object=callback targetBeanName=resultName targetType=resultType/> <@includeModel object=callback targetBeanName=resultName targetType=resultType/>
</#list> </#list>
<#if !(valueMappings.empty && throwIllegalArgumentException)> <#if !(valueMappings.empty && unexpectedValueMappingException??)>
return ${resultName}; return ${resultName};
</#if> </#if>
} }

View File

@ -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;
/**
* @author Filip Hrisafov
*/
public class CustomIllegalArgumentException extends RuntimeException {
public CustomIllegalArgumentException(String message) {
super( message );
}
}

View File

@ -0,0 +1,25 @@
/*
* 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.erroneous;
import org.mapstruct.EnumMapping;
import org.mapstruct.Mapper;
import org.mapstruct.ValueMapping;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface EmptyEnumMappingMapper {
@EnumMapping
@ValueMapping(source = "EXTRA", target = "SPECIAL")
@ValueMapping(source = "STANDARD", target = "DEFAULT")
@ValueMapping(source = "NORMAL", target = "DEFAULT")
ExternalOrderType onlyWithMappings(OrderType orderType);
}

View File

@ -0,0 +1,63 @@
/*
* 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.erroneous;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
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;
/**
* @author Filip Hrisafov
*/
@IssueKey("2169")
@RunWith(AnnotationProcessorTestRunner.class)
@WithClasses({
CustomIllegalArgumentException.class,
ExternalOrderType.class,
OrderType.class,
})
public class ErroneousEnumMappingTest {
@Test
@WithClasses({
EmptyEnumMappingMapper.class
})
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = EmptyEnumMappingMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 20,
message = "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are " +
"undefined in @EnumMapping, define at least one of them."
)
}
)
public void shouldCompileErrorWhenEnumMappingIsEmpty() {
}
@Test
@WithClasses({
NoConfigurationEnumMappingMapper.class
})
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = NoConfigurationEnumMappingMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 21,
message = "Configuration has to be defined when strategy is defined.")
}
)
public void shouldCompileErrorWhenEnumMappingHasStrategyButNoConfiguration() {
}
}

View File

@ -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.erroneous;
import org.mapstruct.EnumMapping;
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;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface NoConfigurationEnumMappingMapper {
@EnumMapping(nameTransformationStrategy = MappingConstants.PREFIX_TRANSFORMATION)
@ValueMapping(source = "EXTRA", target = "SPECIAL")
@ValueMapping(source = "STANDARD", target = "DEFAULT")
@ValueMapping(source = "NORMAL", target = "DEFAULT")
ExternalOrderType onlyWithMappings(OrderType orderType);
}

View File

@ -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.exception;
import org.mapstruct.MapperConfig;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
/**
* @author Filip Hrisafov
*/
@MapperConfig(unexpectedValueMappingException = CustomIllegalArgumentException.class)
public interface Config {
}

View File

@ -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.exception;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.ValueMapping;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
/**
* @author Filip Hrisafov
*/
@Mapper(unexpectedValueMappingException = CustomIllegalArgumentException.class)
public interface CustomUnexpectedValueMappingExceptionDefinedInMapper {
@ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT")
ExternalOrderType withAnyUnmapped(OrderType orderType);
@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT")
ExternalOrderType withAnyRemaining(OrderType orderType);
@ValueMapping(source = "EXTRA", target = "SPECIAL")
@ValueMapping(source = "STANDARD", target = "DEFAULT")
@ValueMapping(source = "NORMAL", target = "DEFAULT")
ExternalOrderType onlyWithMappings(OrderType orderType);
@InheritInverseConfiguration(name = "onlyWithMappings")
OrderType inverseOnlyWithMappings(ExternalOrderType orderType);
}

View File

@ -0,0 +1,38 @@
/*
* 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.exception;
import org.mapstruct.EnumMapping;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.ValueMapping;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
/**
* @author Filip Hrisafov
*/
@Mapper(unexpectedValueMappingException = CustomIllegalArgumentException.class)
public interface CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping {
@ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT")
ExternalOrderType withAnyUnmapped(OrderType orderType);
@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT")
ExternalOrderType withAnyRemaining(OrderType orderType);
@ValueMapping(source = "EXTRA", target = "SPECIAL")
@ValueMapping(source = "STANDARD", target = "DEFAULT")
@ValueMapping(source = "NORMAL", target = "DEFAULT")
ExternalOrderType onlyWithMappings(OrderType orderType);
// If unexpectedValueMappingException is explicitly defined then it should be used instead of what is in the SPI
@EnumMapping(unexpectedValueMappingException = IllegalArgumentException.class)
@InheritInverseConfiguration(name = "onlyWithMappings")
OrderType inverseOnlyWithMappings(ExternalOrderType orderType);
}

View File

@ -0,0 +1,34 @@
/*
* 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.exception;
import org.mapstruct.InheritInverseConfiguration;
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;
/**
* @author Filip Hrisafov
*/
@Mapper(config = Config.class)
public interface CustomUnexpectedValueMappingExceptionDefinedInMapperConfig {
@ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT")
ExternalOrderType withAnyUnmapped(OrderType orderType);
@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT")
ExternalOrderType withAnyRemaining(OrderType orderType);
@ValueMapping(source = "EXTRA", target = "SPECIAL")
@ValueMapping(source = "STANDARD", target = "DEFAULT")
@ValueMapping(source = "NORMAL", target = "DEFAULT")
ExternalOrderType onlyWithMappings(OrderType orderType);
@InheritInverseConfiguration(name = "onlyWithMappings")
OrderType inverseOnlyWithMappings(ExternalOrderType orderType);
}

View File

@ -0,0 +1,39 @@
/*
* 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.exception;
import org.mapstruct.EnumMapping;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.ValueMapping;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface CustomUnexpectedValueMappingExceptionMapper {
@EnumMapping(unexpectedValueMappingException = CustomIllegalArgumentException.class)
@ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT" )
ExternalOrderType withAnyUnmapped(OrderType orderType);
@EnumMapping(unexpectedValueMappingException = CustomIllegalArgumentException.class)
@ValueMapping( source = MappingConstants.ANY_REMAINING, target = "DEFAULT" )
ExternalOrderType withAnyRemaining(OrderType orderType);
@EnumMapping(unexpectedValueMappingException = CustomIllegalArgumentException.class)
@ValueMapping(source = "EXTRA", target = "SPECIAL")
@ValueMapping(source = "STANDARD", target = "DEFAULT")
@ValueMapping(source = "NORMAL", target = "DEFAULT")
ExternalOrderType onlyWithMappings(OrderType orderType);
@InheritInverseConfiguration(name = "onlyWithMappings")
OrderType inverseOnlyWithMappings(ExternalOrderType orderType);
}

View File

@ -0,0 +1,48 @@
/*
* 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.exception;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
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.runner.AnnotationProcessorTestRunner;
import org.mapstruct.ap.testutil.runner.GeneratedSource;
/**
* @author Filip Hrisafov
*/
@IssueKey("2169")
@RunWith(AnnotationProcessorTestRunner.class)
@WithClasses({
Config.class,
CustomUnexpectedValueMappingExceptionDefinedInMapper.class,
CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.class,
CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.class,
CustomUnexpectedValueMappingExceptionMapper.class,
CustomIllegalArgumentException.class,
ExternalOrderType.class,
OrderType.class
})
public class CustomUnexpectedValueMappingExceptionTest {
@Rule
public final GeneratedSource generatedSource = new GeneratedSource();
@Test
public void shouldGenerateCustomUnexpectedValueMappingException() {
generatedSource.addComparisonToFixtureFor(
CustomUnexpectedValueMappingExceptionDefinedInMapper.class,
CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.class,
CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.class,
CustomUnexpectedValueMappingExceptionMapper.class
);
}
}

View File

@ -5,6 +5,7 @@
*/ */
package org.mapstruct.ap.test.value.spi; package org.mapstruct.ap.test.value.spi;
import org.mapstruct.EnumMapping;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants; import org.mapstruct.MappingConstants;
import org.mapstruct.ValueMapping; import org.mapstruct.ValueMapping;
@ -24,6 +25,8 @@ public interface CustomCheeseMapper {
String mapToString(CustomCheeseType cheeseType); String mapToString(CustomCheeseType cheeseType);
// If unexpectedValueMappingException is explicitly defined then it should be used instead of what is in the SPI
@EnumMapping(unexpectedValueMappingException = IllegalArgumentException.class)
String mapToString(CheeseType cheeseType); String mapToString(CheeseType cheeseType);
@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "CUSTOM_BRIE") @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "CUSTOM_BRIE")

View File

@ -9,13 +9,14 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; import org.mapstruct.ap.spi.DefaultEnumMappingStrategy;
import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumMappingStrategy;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
/** /**
* @author Filip Hrisafov * @author Filip Hrisafov
*/ */
public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy implements EnumNamingStrategy { public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy implements EnumMappingStrategy {
@Override @Override
public String getDefaultNullEnumConstant(TypeElement enumType) { public String getDefaultNullEnumConstant(TypeElement enumType) {
@ -51,4 +52,9 @@ public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy implemen
return false; return false;
} }
@Override
protected Class<? extends Exception> getUnexpectedValueMappingExceptionClass() {
return CustomIllegalArgumentException.class;
}
} }

View File

@ -8,6 +8,7 @@ package org.mapstruct.ap.test.value.spi;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.WithServiceImplementation;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
@ -23,9 +24,10 @@ import static org.assertj.core.api.Assertions.assertThat;
CheeseType.class, CheeseType.class,
CustomCheeseType.class, CustomCheeseType.class,
CustomEnumMarker.class, CustomEnumMarker.class,
CustomIllegalArgumentException.class,
}) })
@WithServiceImplementation(CustomEnumNamingStrategy.class) @WithServiceImplementation(CustomEnumMappingStrategy.class)
public class CustomEnumNamingStrategyTest { public class CustomEnumMappingStrategyTest {
@Rule @Rule
public final GeneratedSource generatedSource = new GeneratedSource(); public final GeneratedSource generatedSource = new GeneratedSource();
@ -34,7 +36,7 @@ public class CustomEnumNamingStrategyTest {
@WithClasses({ @WithClasses({
CustomCheeseMapper.class CustomCheeseMapper.class
}) })
public void shouldApplyCustomEnumNamingStrategy() { public void shouldApplyCustomEnumMappingStrategy() {
generatedSource.addComparisonToFixtureFor( CustomCheeseMapper.class ); generatedSource.addComparisonToFixtureFor( CustomCheeseMapper.class );
CustomCheeseMapper mapper = CustomCheeseMapper.INSTANCE; CustomCheeseMapper mapper = CustomCheeseMapper.INSTANCE;
@ -80,7 +82,7 @@ public class CustomEnumNamingStrategyTest {
@WithClasses({ @WithClasses({
OverridesCustomCheeseMapper.class OverridesCustomCheeseMapper.class
}) })
public void shouldApplyDefinedMappingsInsteadOfCustomEnumNamingStrategy() { public void shouldApplyDefinedMappingsInsteadOfCustomEnumMappingStrategy() {
OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE; OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE;
// CheeseType -> CustomCheeseType // CheeseType -> CustomCheeseType

View File

@ -9,13 +9,13 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; import org.mapstruct.ap.spi.DefaultEnumMappingStrategy;
import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumMappingStrategy;
/** /**
* @author Filip Hrisafov * @author Filip Hrisafov
*/ */
public class CustomErroneousEnumNamingStrategy extends DefaultEnumNamingStrategy implements EnumNamingStrategy { public class CustomErroneousEnumMappingStrategy extends DefaultEnumMappingStrategy implements EnumMappingStrategy {
@Override @Override
public String getDefaultNullEnumConstant(TypeElement enumType) { public String getDefaultNullEnumConstant(TypeElement enumType) {

View File

@ -25,8 +25,8 @@ import static org.assertj.core.api.Assertions.assertThat;
CustomCheeseType.class, CustomCheeseType.class,
CustomEnumMarker.class, CustomEnumMarker.class,
}) })
@WithServiceImplementation(CustomErroneousEnumNamingStrategy.class) @WithServiceImplementation(CustomErroneousEnumMappingStrategy.class)
public class CustomErroneousEnumNamingStrategyTest { public class CustomErroneousEnumMappingStrategyTest {
@Test @Test
@WithClasses({ @WithClasses({
@ -37,18 +37,18 @@ public class CustomErroneousEnumNamingStrategyTest {
@Diagnostic( @Diagnostic(
type = CustomCheeseMapper.class, type = CustomCheeseMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR, kind = javax.tools.Diagnostic.Kind.ERROR,
line = 23, line = 24,
messageRegExp = "Constant INCORRECT doesn't exist in enum type " + messageRegExp = "Constant INCORRECT doesn't exist in enum type " +
"org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." + "org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." +
" Constant was returned from EnumNamingStrategy: .*CustomErroneousEnumNamingStrategy@.*" " Constant was returned from EnumMappingStrategy: .*CustomErroneousEnumMappingStrategy@.*"
), ),
@Diagnostic( @Diagnostic(
type = CustomCheeseMapper.class, type = CustomCheeseMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR, kind = javax.tools.Diagnostic.Kind.ERROR,
line = 30, line = 33,
messageRegExp = "Constant INCORRECT doesn't exist in enum type " + messageRegExp = "Constant INCORRECT doesn't exist in enum type " +
"org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." + "org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." +
" Constant was returned from EnumNamingStrategy: .*CustomErroneousEnumNamingStrategy@.*" " Constant was returned from EnumMappingStrategy: .*CustomErroneousEnumMappingStrategy@.*"
) )
} }
) )
@ -59,7 +59,7 @@ public class CustomErroneousEnumNamingStrategyTest {
@WithClasses({ @WithClasses({
OverridesCustomCheeseMapper.class OverridesCustomCheeseMapper.class
}) })
public void shouldApplyDefinedMappingsInsteadOfCustomEnumNamingStrategy() { public void shouldApplyDefinedMappingsInsteadOfCustomEnumMappingStrategy() {
OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE; OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE;
// CheeseType -> CustomCheeseType // CheeseType -> CustomCheeseType

View File

@ -0,0 +1,101 @@
/*
* 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.exception;
import javax.annotation.Generated;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-08-29T09:36:57+0200",
comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)"
)
public class CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping {
@Override
public ExternalOrderType withAnyUnmapped(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType withAnyRemaining(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType onlyWithMappings(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case EXTRA: externalOrderType = ExternalOrderType.SPECIAL;
break;
case STANDARD: externalOrderType = ExternalOrderType.DEFAULT;
break;
case NORMAL: externalOrderType = ExternalOrderType.DEFAULT;
break;
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return externalOrderType;
}
@Override
public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) {
if ( orderType == null ) {
return null;
}
OrderType orderType1;
switch ( orderType ) {
case SPECIAL: orderType1 = OrderType.EXTRA;
break;
case DEFAULT: orderType1 = OrderType.STANDARD;
break;
case RETAIL: orderType1 = OrderType.RETAIL;
break;
case B2B: orderType1 = OrderType.B2B;
break;
default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return orderType1;
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.exception;
import javax.annotation.Generated;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-08-29T09:36:57+0200",
comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)"
)
public class CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapperConfig {
@Override
public ExternalOrderType withAnyUnmapped(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType withAnyRemaining(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType onlyWithMappings(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case EXTRA: externalOrderType = ExternalOrderType.SPECIAL;
break;
case STANDARD: externalOrderType = ExternalOrderType.DEFAULT;
break;
case NORMAL: externalOrderType = ExternalOrderType.DEFAULT;
break;
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return externalOrderType;
}
@Override
public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) {
if ( orderType == null ) {
return null;
}
OrderType orderType1;
switch ( orderType ) {
case SPECIAL: orderType1 = OrderType.EXTRA;
break;
case DEFAULT: orderType1 = OrderType.STANDARD;
break;
case RETAIL: orderType1 = OrderType.RETAIL;
break;
case B2B: orderType1 = OrderType.B2B;
break;
default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return orderType1;
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.exception;
import javax.annotation.Generated;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-08-29T09:36:57+0200",
comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)"
)
public class CustomUnexpectedValueMappingExceptionDefinedInMapperImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapper {
@Override
public ExternalOrderType withAnyUnmapped(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType withAnyRemaining(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType onlyWithMappings(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case EXTRA: externalOrderType = ExternalOrderType.SPECIAL;
break;
case STANDARD: externalOrderType = ExternalOrderType.DEFAULT;
break;
case NORMAL: externalOrderType = ExternalOrderType.DEFAULT;
break;
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return externalOrderType;
}
@Override
public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) {
if ( orderType == null ) {
return null;
}
OrderType orderType1;
switch ( orderType ) {
case SPECIAL: orderType1 = OrderType.EXTRA;
break;
case DEFAULT: orderType1 = OrderType.STANDARD;
break;
case RETAIL: orderType1 = OrderType.RETAIL;
break;
case B2B: orderType1 = OrderType.B2B;
break;
default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return orderType1;
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.exception;
import javax.annotation.Generated;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
import org.mapstruct.ap.test.value.ExternalOrderType;
import org.mapstruct.ap.test.value.OrderType;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-08-02T09:36:57+0200",
comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)"
)
public class CustomUnexpectedValueMappingExceptionMapperImpl implements CustomUnexpectedValueMappingExceptionMapper {
@Override
public ExternalOrderType withAnyUnmapped(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType withAnyRemaining(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: externalOrderType = ExternalOrderType.DEFAULT;
}
return externalOrderType;
}
@Override
public ExternalOrderType onlyWithMappings(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case EXTRA: externalOrderType = ExternalOrderType.SPECIAL;
break;
case STANDARD: externalOrderType = ExternalOrderType.DEFAULT;
break;
case NORMAL: externalOrderType = ExternalOrderType.DEFAULT;
break;
case RETAIL: externalOrderType = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType = ExternalOrderType.B2B;
break;
default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return externalOrderType;
}
@Override
public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) {
if ( orderType == null ) {
return null;
}
OrderType orderType1;
switch ( orderType ) {
case SPECIAL: orderType1 = OrderType.EXTRA;
break;
case DEFAULT: orderType1 = OrderType.STANDARD;
break;
case RETAIL: orderType1 = OrderType.RETAIL;
break;
case B2B: orderType1 = OrderType.B2B;
break;
default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return orderType1;
}
}

View File

@ -6,6 +6,7 @@
package org.mapstruct.ap.test.value.spi; package org.mapstruct.ap.test.value.spi;
import javax.annotation.processing.Generated; import javax.annotation.processing.Generated;
import org.mapstruct.ap.test.value.CustomIllegalArgumentException;
@Generated( @Generated(
value = "org.mapstruct.ap.MappingProcessor", value = "org.mapstruct.ap.MappingProcessor",
@ -31,7 +32,7 @@ public class CustomCheeseMapperImpl implements CustomCheeseMapper {
break; break;
case UNRECOGNIZED: cheeseType = null; case UNRECOGNIZED: cheeseType = null;
break; break;
default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + cheese );
} }
return cheeseType; return cheeseType;
@ -50,7 +51,7 @@ public class CustomCheeseMapperImpl implements CustomCheeseMapper {
break; break;
case ROQUEFORT: customCheeseType = CustomCheeseType.CUSTOM_ROQUEFORT; case ROQUEFORT: customCheeseType = CustomCheeseType.CUSTOM_ROQUEFORT;
break; break;
default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + cheese );
} }
return customCheeseType; return customCheeseType;
@ -73,7 +74,7 @@ public class CustomCheeseMapperImpl implements CustomCheeseMapper {
break; break;
case UNRECOGNIZED: string = null; case UNRECOGNIZED: string = null;
break; break;
default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheeseType ); default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + cheeseType );
} }
return string; return string;