#1401 improvements by direct assigning constants

This commit is contained in:
sjaakd 2018-04-01 15:15:42 +02:00
parent 2fe7f6be2b
commit 35f5400e00
17 changed files with 850 additions and 17 deletions

View File

@ -1918,7 +1918,8 @@ This chapter describes several advanced options which allow to fine-tune the beh
[[default-values-and-constants]] [[default-values-and-constants]]
=== Default values and constants === Default values and constants
Default values can be specified to set a predefined value to a target property if the corresponding source property is `null`. Constants can be specified to set such a predefined value in any case. Default values and constants are specified as String values and are subject to type conversion either via built-in conversions or the invocation of other mapping methods in order to match the type required by the target property. Default values can be specified to set a predefined value to a target property if the corresponding source property is `null`. Constants can be specified to set such a predefined value in any case. Default values and constants are specified as String values. When the target type is a primitive or a boxed type, the String value is taken literal. Bit / octal / decimal / hex patterns are allowed in such case as long as they are a valid literal.
In all other cases, constant or default values are subject to type conversion either via built-in conversions or the invocation of other mapping methods in order to match the type required by the target property.
A mapping with a constant must not include a reference to a source property. The following example shows some mappings using default values and constants: A mapping with a constant must not include a reference to a source property. The following example shows some mappings using default values and constants:

View File

@ -496,13 +496,14 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
propertyMapping = new ConstantMappingBuilder() propertyMapping = new ConstantMappingBuilder()
.mappingContext( ctx ) .mappingContext( ctx )
.sourceMethod( method ) .sourceMethod( method )
.constantExpression( "\"" + mapping.getConstant() + "\"" ) .constantExpression( mapping.getConstant() )
.targetProperty( targetProperty ) .targetProperty( targetProperty )
.targetPropertyName( mapping.getTargetName() ) .targetPropertyName( mapping.getTargetName() )
.formattingParameters( mapping.getFormattingParameters() ) .formattingParameters( mapping.getFormattingParameters() )
.selectionParameters( mapping.getSelectionParameters() ) .selectionParameters( mapping.getSelectionParameters() )
.existingVariableNames( existingVariableNames ) .existingVariableNames( existingVariableNames )
.dependsOn( mapping.getDependsOn() ) .dependsOn( mapping.getDependsOn() )
.mirror( mapping.getMirror() )
.build(); .build();
handledTargets.add( mapping.getTargetName() ); handledTargets.add( mapping.getTargetName() );
} }

View File

@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
@ -116,6 +117,7 @@ public class PropertyMapping extends ModelElement {
protected List<String> dependsOn; protected List<String> dependsOn;
protected Set<String> existingVariableNames; protected Set<String> existingVariableNames;
protected AnnotationMirror mirror;
MappingBuilderBase(Class<T> selfType) { MappingBuilderBase(Class<T> selfType) {
super( selfType ); super( selfType );
@ -146,6 +148,11 @@ public class PropertyMapping extends ModelElement {
return (T) this; return (T) this;
} }
T mirror(AnnotationMirror mirror) {
this.mirror = mirror;
return (T) this;
}
private Type determineTargetType() { private Type determineTargetType() {
// This is a bean mapping method, so we know the result is a declared type // This is a bean mapping method, so we know the result is a declared type
DeclaredType resultType = (DeclaredType) method.getResultType().getEffectiveType().getTypeMirror(); DeclaredType resultType = (DeclaredType) method.getResultType().getEffectiveType().getTypeMirror();
@ -359,7 +366,7 @@ public class PropertyMapping extends ModelElement {
&& ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null) ) { && ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null) ) {
// cannot check on null source if source is primitive unless it has a presence checker // cannot check on null source if source is primitive unless it has a presence checker
PropertyMapping build = new ConstantMappingBuilder() PropertyMapping build = new ConstantMappingBuilder()
.constantExpression( '"' + defaultValue + '"' ) .constantExpression( defaultValue )
.formattingParameters( formattingParameters ) .formattingParameters( formattingParameters )
.selectionParameters( selectionParameters ) .selectionParameters( selectionParameters )
.dependsOn( dependsOn ) .dependsOn( dependsOn )
@ -749,7 +756,12 @@ public class PropertyMapping extends ModelElement {
public PropertyMapping build() { public PropertyMapping build() {
// source // source
String sourceErrorMessagePart = "constant '" + constantExpression + "'"; String sourceErrorMessagePart = "constant '" + constantExpression + "'";
Type sourceType = ctx.getTypeFactory().getType( String.class );
Type sourceType = ctx.getTypeFactory().getTypeForConstant( targetType, constantExpression );
if ( String.class.getCanonicalName().equals( sourceType.getFullyQualifiedName() ) ) {
// convert to string
constantExpression = "\"" + constantExpression + "\"";
}
Assignment assignment = null; Assignment assignment = null;
if ( !targetType.isEnumType() ) { if ( !targetType.isEnumType() ) {
@ -808,6 +820,7 @@ public class PropertyMapping extends ModelElement {
else { else {
ctx.getMessager().printMessage( ctx.getMessager().printMessage(
method.getExecutable(), method.getExecutable(),
mirror,
Message.CONSTANTMAPPING_MAPPING_NOT_FOUND, Message.CONSTANTMAPPING_MAPPING_NOT_FOUND,
sourceType, sourceType,
constantExpression, constantExpression,

View File

@ -89,6 +89,8 @@ public class Type extends ModelElement implements Comparable<Type> {
private final boolean isImported; private final boolean isImported;
private final boolean isVoid; private final boolean isVoid;
private final boolean isStream; private final boolean isStream;
private final boolean isBoxed;
private final boolean isOriginatedFromConstant;
private final List<String> enumConstants; private final List<String> enumConstants;
@ -111,7 +113,8 @@ public class Type extends ModelElement implements Comparable<Type> {
BuilderInfo builderInfo, BuilderInfo builderInfo,
String packageName, String name, String qualifiedName, String packageName, String name, String qualifiedName,
boolean isInterface, boolean isEnumType, boolean isIterableType, boolean isInterface, boolean isEnumType, boolean isIterableType,
boolean isCollectionType, boolean isMapType, boolean isStreamType, boolean isImported) { boolean isCollectionType, boolean isMapType, boolean isStreamType, boolean isImported,
boolean isBoxed, boolean isOriginatedFromConstant ) {
this.typeUtils = typeUtils; this.typeUtils = typeUtils;
this.elementUtils = elementUtils; this.elementUtils = elementUtils;
@ -135,6 +138,8 @@ public class Type extends ModelElement implements Comparable<Type> {
this.isStream = isStreamType; this.isStream = isStreamType;
this.isImported = isImported; this.isImported = isImported;
this.isVoid = typeMirror.getKind() == TypeKind.VOID; this.isVoid = typeMirror.getKind() == TypeKind.VOID;
this.isBoxed = isBoxed;
this.isOriginatedFromConstant = isOriginatedFromConstant;
if ( isEnumType ) { if ( isEnumType ) {
enumConstants = new ArrayList<String>(); enumConstants = new ArrayList<String>();
@ -384,7 +389,9 @@ public class Type extends ModelElement implements Comparable<Type> {
isCollectionType, isCollectionType,
isMapType, isMapType,
isStream, isStream,
isImported isImported,
isBoxed,
isOriginatedFromConstant
); );
} }
@ -913,4 +920,13 @@ public class Type extends ModelElement implements Comparable<Type> {
return null; return null;
} }
public boolean isBoxed() {
return isBoxed;
}
public boolean hasOriginatedFromConstant() {
return isOriginatedFromConstant;
}
} }

View File

@ -64,6 +64,7 @@ import org.mapstruct.ap.spi.BuilderInfo;
import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.BuilderProvider;
import org.mapstruct.ap.spi.DefaultBuilderProvider; import org.mapstruct.ap.spi.DefaultBuilderProvider;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException; import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
import org.mapstruct.ap.internal.util.NativeTypes;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withDefaultConstructor; import static org.mapstruct.ap.internal.model.common.ImplementationType.withDefaultConstructor;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withInitialCapacity; import static org.mapstruct.ap.internal.model.common.ImplementationType.withInitialCapacity;
@ -131,6 +132,10 @@ public class TypeFactory {
} }
public Type getType(String canonicalName) { public Type getType(String canonicalName) {
return getType( canonicalName, false );
}
private Type getType(String canonicalName, boolean isOriginatedFromConstant) {
TypeElement typeElement = elementUtils.getTypeElement( canonicalName ); TypeElement typeElement = elementUtils.getTypeElement( canonicalName );
if ( typeElement == null ) { if ( typeElement == null ) {
@ -139,7 +144,31 @@ public class TypeFactory {
); );
} }
return getType( typeElement ); return getType( typeElement, isOriginatedFromConstant );
}
public Type getTypeForConstant(Type targetType, String literal) {
Type result = null;
if ( targetType.isPrimitive() ) {
TypeKind kind = targetType.getTypeMirror().getKind();
boolean assignable = NativeTypes.isStringAssignable( kind, true, literal );
if ( assignable ) {
result = getType( targetType.getTypeMirror(), true );
}
}
else {
TypeKind boxedTypeKind = NativeTypes.getWrapperKind( targetType.getFullyQualifiedName() );
if ( boxedTypeKind != null ) {
boolean assignable = NativeTypes.isStringAssignable( boxedTypeKind, false, literal );
if ( assignable ) {
result = getType( targetType.getTypeMirror(), true );
}
}
}
if ( result == null ) {
result = getType( String.class.getCanonicalName(), true );
}
return result;
} }
/** /**
@ -165,7 +194,15 @@ public class TypeFactory {
return getType( typeElement.asType() ); return getType( typeElement.asType() );
} }
private Type getType(TypeElement typeElement, boolean originatedFromConstant) {
return getType( typeElement.asType(), originatedFromConstant );
}
public Type getType(TypeMirror mirror) { public Type getType(TypeMirror mirror) {
return getType( mirror, false );
}
private Type getType(TypeMirror mirror, boolean originatedFromConstant) {
if ( !canBeProcessed( mirror ) ) { if ( !canBeProcessed( mirror ) ) {
throw new TypeHierarchyErroneousException( mirror ); throw new TypeHierarchyErroneousException( mirror );
} }
@ -186,6 +223,7 @@ public class TypeFactory {
TypeElement typeElement; TypeElement typeElement;
Type componentType; Type componentType;
boolean isImported; boolean isImported;
boolean isBoxed = false;
if ( mirror.getKind() == TypeKind.DECLARED ) { if ( mirror.getKind() == TypeKind.DECLARED ) {
DeclaredType declaredType = (DeclaredType) mirror; DeclaredType declaredType = (DeclaredType) mirror;
@ -207,6 +245,7 @@ public class TypeFactory {
componentType = null; componentType = null;
isImported = isImported( name, qualifiedName ); isImported = isImported( name, qualifiedName );
isBoxed = NativeTypes.isWrapped( qualifiedName );
} }
else if ( mirror.getKind() == TypeKind.ARRAY ) { else if ( mirror.getKind() == TypeKind.ARRAY ) {
TypeMirror componentTypeMirror = getComponentType( mirror ); TypeMirror componentTypeMirror = getComponentType( mirror );
@ -267,7 +306,9 @@ public class TypeFactory {
isCollectionType, isCollectionType,
isMapType, isMapType,
isStreamType, isStreamType,
isImported isImported,
isBoxed,
originatedFromConstant
); );
} }
@ -315,8 +356,8 @@ public class TypeFactory {
} }
/** /**
* Get the Type for given method as part of usedMapper. Possibly parameterized types in method declaration * Get the Type for given method as part of usedMapper. Possibly parameterized types in method declaration will be
* will be evaluated to concrete types then. * evaluated to concrete types then.
* *
* @param includingType the type on which's scope the method type shall be evaluated * @param includingType the type on which's scope the method type shall be evaluated
* @param method the method * @param method the method
@ -479,7 +520,9 @@ public class TypeFactory {
implementationType.isCollectionType(), implementationType.isCollectionType(),
implementationType.isMapType(), implementationType.isMapType(),
implementationType.isStreamType(), implementationType.isStreamType(),
isImported( implementationType.getName(), implementationType.getFullyQualifiedName() ) isImported( implementationType.getName(), implementationType.getFullyQualifiedName() ),
implementationType.isBoxed(),
implementationType.hasOriginatedFromConstant()
); );
return implementation.createNew( replacement ); return implementation.createNew( replacement );
} }

View File

@ -253,6 +253,18 @@ public class MappingResolverImpl implements MappingResolver {
return simpleAssignment; return simpleAssignment;
} }
// At this point the SourceType will either
// 1. be a String
// 2. or when its a primitive / wrapped type and analysis successful equal to its TargetType. But in that
// case it should have been direct assignable.
// In case of 1. and the target type is still a wrapped or primitive type we must assume that the check
// in NativeType is not successful. We don't want to go through type conversion, double mappings etc.
// with something that we already know to be wrong.
if ( sourceType.hasOriginatedFromConstant() && ( targetType.isPrimitive() || targetType.isBoxed() ) ) {
// TODO: convey some error message
return null;
}
// then type conversion // then type conversion
Assignment conversion = resolveViaConversion( sourceType, targetType ); Assignment conversion = resolveViaConversion( sourceType, targetType );
if ( conversion != null ) { if ( conversion != null ) {

View File

@ -25,6 +25,8 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern;
import javax.lang.model.type.TypeKind;
/** /**
* Provides functionality around the Java primitive data types and their wrapper * Provides functionality around the Java primitive data types and their wrapper
@ -37,6 +39,195 @@ public class NativeTypes {
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES; private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES;
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPES; private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPES;
private static final Set<Class<?>> NUMBER_TYPES = new HashSet<Class<?>>(); private static final Set<Class<?>> NUMBER_TYPES = new HashSet<Class<?>>();
private static final Map<String, TypeKind> WRAPPER_NAME_TO_PRIMITIVE_TYPES;
private static final Pattern PTRN_HEX = Pattern.compile( "^0[x|X].*" );
private static final Pattern PTRN_OCT = Pattern.compile( "^0_*[0-7].*" );
private static final Pattern PTRN_BIN = Pattern.compile( "^0[b|B].*" );
private static final Pattern PTRN_FLOAT_DEC_ZERO = Pattern.compile( "^[^eE]*[1-9].*[eE]?.*" );
private static final Pattern PTRN_FLOAT_HEX_ZERO = Pattern.compile( "^[^pP]*[1-9a-fA-F].*[pP]?.*" );
private static final Pattern PTRN_SIGN = Pattern.compile( "^[\\+|-]" );
private static final Pattern PTRN_LONG = Pattern.compile( "[l|L]$" );
private static final Pattern PTRN_FLOAT = Pattern.compile( "[f|F]$" );
private static final Pattern PTRN_DOUBLE = Pattern.compile( "[d|D]$" );
private static final Pattern PTRN_FAULTY_UNDERSCORE_INT = Pattern.compile( "^_|_$|-_|_-|\\+_|_\\+" );
private static final Pattern PTRN_FAULTY_UNDERSCORE_FLOAT = Pattern.compile( "^_|_$|-_|_-|\\+_|_\\+|\\._|_\\." );
private static final Pattern PTRN_FAULTY_DEC_UNDERSCORE_FLOAT = Pattern.compile( "_e|_E|e_|E_" );
private static final Pattern PTRN_FAULTY_HEX_UNDERSCORE_FLOAT = Pattern.compile( "_p|_P|p_|P_" );
private static final Map<TypeKind, NumberFormatValidator> VALIDATORS = initValidators();
private interface NumberFormatValidator {
boolean validate(boolean isPrimitive, String s);
}
private abstract static class NumberRepresentation {
int radix;
String val;
boolean isIntegralType;
boolean isLong;
boolean isFloat;
boolean isPrimitive;
NumberRepresentation(String in, boolean isIntegralType, boolean isLong, boolean isFloat, boolean isPrimitive) {
this.isLong = isLong;
this.isFloat = isFloat;
this.isIntegralType = isIntegralType;
this.isPrimitive = isPrimitive;
String valWithoutSign;
boolean isNegative = in.startsWith( "-" );
boolean hasSign = PTRN_SIGN.matcher( in ).find();
if ( hasSign ) {
valWithoutSign = in.substring( 1 );
}
else {
valWithoutSign = in;
}
if ( PTRN_HEX.matcher( valWithoutSign ).matches() ) {
// hex
radix = 16;
val = (isNegative ? "-" : "") + valWithoutSign.substring( 2 );
}
else if ( PTRN_BIN.matcher( valWithoutSign ).matches() ) {
// binary
radix = 2;
val = (isNegative ? "-" : "") + valWithoutSign.substring( 2 );
}
else if ( PTRN_OCT.matcher( valWithoutSign ).matches() ) {
// octal
radix = 8;
val = (isNegative ? "-" : "") + valWithoutSign.substring( 1 );
}
else {
// decimal
radix = 10;
val = (isNegative ? "-" : "") + valWithoutSign;
}
}
abstract boolean parse(String val, int radix);
boolean validate() {
try {
strip();
return parse( val, radix );
}
catch ( NumberFormatException ex ) {
return false;
}
}
void strip() {
if ( isIntegralType ) {
removeAndValidateIntegerLiteralSuffix();
removeAndValidateIntegerLiteralUnderscore();
}
else {
removeAndValidateFloatingPointLiteralSuffix();
removeAndValidateFloatingPointLiteralUnderscore();
}
}
/**
* remove java7+ underscores from the input
*/
void removeAndValidateIntegerLiteralUnderscore() {
if ( PTRN_FAULTY_UNDERSCORE_INT.matcher( val ).find() ) {
throw new NumberFormatException("Improperly placed underscores");
}
else {
val = val.replace( "_", "" );
}
}
/**
* remove java7+ underscores from the input
*/
void removeAndValidateFloatingPointLiteralUnderscore() {
boolean isHex = radix == 16;
if ( PTRN_FAULTY_UNDERSCORE_FLOAT.matcher( val ).find()
|| !isHex && PTRN_FAULTY_DEC_UNDERSCORE_FLOAT.matcher( val ).find()
|| isHex && PTRN_FAULTY_HEX_UNDERSCORE_FLOAT.matcher( val ).find() ) {
throw new NumberFormatException("Improperly placed underscores");
}
else {
val = val.replace( "_", "" );
}
}
/**
*
*/
void removeAndValidateIntegerLiteralSuffix() {
boolean endsWithLSuffix = PTRN_LONG.matcher( val ).find();
// error handling
if (endsWithLSuffix && !isLong) {
throw new NumberFormatException("L/l not allowed for non-long types");
}
if (!isPrimitive && !endsWithLSuffix && isLong) {
throw new NumberFormatException("L/l mandatory for boxed long");
}
// remove suffix
if ( endsWithLSuffix ) {
val = val.substring( 0, val.length() - 1 );
}
}
/**
* Double suffix forbidden for float.
*
* @param isFloat
*/
void removeAndValidateFloatingPointLiteralSuffix() {
boolean endsWithLSuffix = PTRN_LONG.matcher( val ).find();
boolean endsWithFSuffix = PTRN_FLOAT.matcher( val ).find();
boolean endsWithDSuffix = PTRN_DOUBLE.matcher( val ).find();
// error handling
if ( isFloat && endsWithDSuffix ) {
throw new NumberFormatException("Assiging double to a float");
}
// remove suffix
if ( endsWithLSuffix || endsWithFSuffix || endsWithDSuffix ) {
val = val.substring( 0, val.length() - 1 );
}
}
boolean floatHasBecomeZero(float parsed) {
if ( parsed == 0f ) {
return floatHasBecomeZero();
}
else {
return false;
}
}
boolean doubleHasBecomeZero(double parsed) {
if ( parsed == 0d ) {
return floatHasBecomeZero();
}
else {
return false;
}
}
private boolean floatHasBecomeZero() {
if ( radix == 10 ) {
// decimal, should be at least some number before exponent (eE) unequal to 0.
return PTRN_FLOAT_DEC_ZERO.matcher( val ).matches();
}
else {
// hex, should be at least some number before exponent (pP) unequal to 0.
return PTRN_FLOAT_HEX_ZERO.matcher( val ).matches();
}
}
}
private NativeTypes() { private NativeTypes() {
} }
@ -80,6 +271,19 @@ public class NativeTypes {
NUMBER_TYPES.add( Double.class ); NUMBER_TYPES.add( Double.class );
NUMBER_TYPES.add( BigInteger.class ); NUMBER_TYPES.add( BigInteger.class );
NUMBER_TYPES.add( BigDecimal.class ); NUMBER_TYPES.add( BigDecimal.class );
Map<String, TypeKind> tmp2 = new HashMap<String, TypeKind>();
tmp2.put( Boolean.class.getName(), TypeKind.BOOLEAN );
tmp2.put( Byte.class.getName(), TypeKind.BYTE );
tmp2.put( Character.class.getName(), TypeKind.CHAR );
tmp2.put( Double.class.getName(), TypeKind.DOUBLE );
tmp2.put( Float.class.getName(), TypeKind.FLOAT );
tmp2.put( Integer.class.getName(), TypeKind.INT );
tmp2.put( Long.class.getName(), TypeKind.LONG );
tmp2.put( Short.class.getName(), TypeKind.SHORT );
WRAPPER_NAME_TO_PRIMITIVE_TYPES = Collections.unmodifiableMap( tmp2 );
} }
public static Class<?> getWrapperType(Class<?> clazz) { public static Class<?> getWrapperType(Class<?> clazz) {
@ -98,6 +302,14 @@ public class NativeTypes {
return WRAPPER_TO_PRIMITIVE_TYPES.get( clazz ); return WRAPPER_TO_PRIMITIVE_TYPES.get( clazz );
} }
public static boolean isWrapped(String fullyQualifiedName) {
return WRAPPER_NAME_TO_PRIMITIVE_TYPES.containsKey( fullyQualifiedName );
}
public static TypeKind getWrapperKind(String fullyQualifiedName) {
return WRAPPER_NAME_TO_PRIMITIVE_TYPES.get( fullyQualifiedName );
}
public static boolean isNumber(Class<?> clazz) { public static boolean isNumber(Class<?> clazz) {
if ( clazz == null ) { if ( clazz == null ) {
return false; return false;
@ -106,4 +318,125 @@ public class NativeTypes {
return NUMBER_TYPES.contains( clazz ); return NUMBER_TYPES.contains( clazz );
} }
} }
public static boolean isStringAssignable(TypeKind kind, boolean isPrimitive, String in) {
NumberFormatValidator validator = VALIDATORS.get( kind );
return validator != null && validator.validate( isPrimitive, in );
}
private static Map<TypeKind, NumberFormatValidator> initValidators() {
Map<TypeKind, NumberFormatValidator> result = new HashMap<TypeKind, NumberFormatValidator>();
result.put( TypeKind.BOOLEAN, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
return "true".equals( s ) || "false".equals( s );
}
} );
result.put( TypeKind.CHAR, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
return s.length() == 3 && s.startsWith( "'" ) && s.endsWith( "'" );
}
} );
result.put( TypeKind.BYTE, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
NumberRepresentation br = new NumberRepresentation( s, true, false, false, isPrimitive ) {
@Override
boolean parse(String val, int radix) {
Byte.parseByte( val, radix );
return true;
}
};
return br.validate();
}
} );
result.put( TypeKind.DOUBLE, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
NumberRepresentation br = new NumberRepresentation( s, false, false, false, isPrimitive ) {
@Override
boolean parse(String val, int radix) {
Double d = Double.parseDouble( radix == 16 ? "0x" + val : val );
return !d.isInfinite() && !doubleHasBecomeZero( d );
}
};
return br.validate();
}
} );
result.put( TypeKind.FLOAT, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
NumberRepresentation br = new NumberRepresentation( s, false, false, true, isPrimitive ) {
@Override
boolean parse(String val, int radix) {
Float f = Float.parseFloat( radix == 16 ? "0x" + val : val );
return !f.isInfinite() && !floatHasBecomeZero( f );
}
};
return br.validate();
}
} );
result.put( TypeKind.INT, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
NumberRepresentation br = new NumberRepresentation( s, true, false, false, isPrimitive ) {
@Override
boolean parse(String val, int radix) {
if ( radix == 10 ) {
// when decimal: treat like signed
Integer.parseInt( val, radix );
return true;
}
else {
// when binary, octal or hex: treat like unsigned
return new BigInteger( val, radix ).bitLength() <= 32;
}
}
};
return br.validate();
}
} );
result.put( TypeKind.LONG, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
NumberRepresentation br = new NumberRepresentation( s, true, true, false, isPrimitive ) {
@Override
boolean parse(String val, int radix) {
if ( radix == 10 ) {
// when decimal: treat like signed
Long.parseLong( val, radix );
return true;
}
else {
// when binary, octal or hex: treat like unsigned
return new BigInteger( val, radix ).bitLength() <= 64;
}
}
};
return br.validate();
}
} );
result.put( TypeKind.SHORT, new NumberFormatValidator() {
@Override
public boolean validate(boolean isPrimitive, String s) {
NumberRepresentation br = new NumberRepresentation( s, true, false, false, isPrimitive ) {
@Override
boolean parse(String val, int radix) {
Short.parseShort( val, radix );
return true;
}
};
return br.validate();
}
} );
return result;
}
} }

View File

@ -183,6 +183,8 @@ public class DateFormatValidatorFactoryTest {
false, false,
false, false,
false, false,
false,
false,
false ); false );
} }

View File

@ -134,6 +134,8 @@ public class DefaultConversionContextTest {
false, false,
false, false,
false, false,
false,
false,
false ); false );
} }

View File

@ -0,0 +1,342 @@
/**
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.source.constants;
import javax.lang.model.type.TypeKind;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mapstruct.ap.internal.util.NativeTypes.isStringAssignable;
/**
*
* @author Sjaak Derksen
*/
public class ConstantOptimizingTest {
/**
* checkout https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
*
* The following example shows other ways you can use the underscore in numeric literals:
*/
@Test
public void testUnderscorePlacement1() {
assertThat( isStringAssignable( TypeKind.LONG, true, "1234_5678_9012_3456L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "999_99_9999L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3.14_15F" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0xFF_EC_DE_5EL" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0xCAFE_BABEL" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0x7fff_ffff_ffff_ffffL" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.BYTE, true, "0b0010_0101" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0b11010010_01101001_10010100_10010010L" ) ).isTrue();
}
/**
* checkout https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
*
* You can place underscores only between digits; you cannot place underscores in the following places:
* <ol>
* <li>At the beginning or end of a number</li>
* <li>Adjacent to a decimal point in a floating point literal</li>
* <li>Prior to an F or L suffix</li>
* <li>In positions where a string of digits is expected</li>
* </ol>
* The following examples demonstrate valid and invalid underscore placements (which are highlighted) in numeric
* literals:
*/
@Test
public void testUnderscorePlacement2() {
// Invalid: cannot put underscores
// adjacent to a decimal point
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3_.1415F" ) ).isFalse();
// Invalid: cannot put underscores
// adjacent to a decimal point
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3._1415F" ) ).isFalse();
// Invalid: cannot put underscores
// prior to an L suffix
assertThat( isStringAssignable( TypeKind.LONG, true, "999_99_9999_L" ) ).isFalse();
// OK (decimal literal)
assertThat( isStringAssignable( TypeKind.INT, true, "5_2" ) ).isTrue();
// Invalid: cannot put underscores
// At the end of a literal
assertThat( isStringAssignable( TypeKind.INT, true, "52_" ) ).isFalse();
// OK (decimal literal)
assertThat( isStringAssignable( TypeKind.INT, true, "5_______2" ) ).isTrue();
// Invalid: cannot put underscores
// in the 0x radix prefix
assertThat( isStringAssignable( TypeKind.INT, true, "0_x52" ) ).isFalse();
// Invalid: cannot put underscores
// at the beginning of a number
assertThat( isStringAssignable( TypeKind.INT, true, "0x_52" ) ).isFalse();
// OK (hexadecimal literal)
assertThat( isStringAssignable( TypeKind.INT, true, "0x5_2" ) ).isTrue();
// Invalid: cannot put underscores
// at the end of a number
assertThat( isStringAssignable( TypeKind.INT, true, "0x52_" ) ).isFalse();
}
/**
* checkout https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
*
* The following example shows other ways you can use the underscore in numeric literals:
*/
@Test
public void testIntegerLiteralFromJLS() {
// largest positive int: dec / octal / int / binary
assertThat( isStringAssignable( TypeKind.INT, true, "2147483647" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0x7fff_ffff" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0177_7777_7777" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0b0111_1111_1111_1111_1111_1111_1111_1111" ) ).isTrue();
// most negative int: dec / octal / int / binary
// NOTE parseInt should be changed to parseUnsignedInt in Java, than the - sign can disssapear (java8)
// and the function will be true to what the compiler shows.
assertThat( isStringAssignable( TypeKind.INT, true, "-2147483648" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0x8000_0000" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0200_0000_0000" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0b1000_0000_0000_0000_0000_0000_0000_0000" ) ).isTrue();
// -1 representation int: dec / octal / int / binary
assertThat( isStringAssignable( TypeKind.INT, true, "-1" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0xffff_ffff" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0377_7777_7777" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0b1111_1111_1111_1111_1111_1111_1111_1111" ) ).isTrue();
// largest positive long: dec / octal / int / binary
assertThat( isStringAssignable( TypeKind.LONG, true, "9223372036854775807L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0x7fff_ffff_ffff_ffffL" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "07_7777_7777_7777_7777_7777L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_"
+ "1111_1111_1111_1111_1111_1111L" ) ).isTrue();
// most negative long: dec / octal / int / binary
assertThat( isStringAssignable( TypeKind.LONG, true, "-9223372036854775808L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0x8000_0000_0000_0000L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "010_0000_0000_0000_0000_0000L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_"
+ "0000_0000_0000_0000_0000L" ) ).isTrue();
// -1 representation long: dec / octal / int / binary
assertThat( isStringAssignable( TypeKind.LONG, true, "-1L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0xffff_ffff_ffff_ffffL" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "017_7777_7777_7777_7777_7777L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_"
+ "1111_1111_1111_1111_1111L" ) ).isTrue();
// some examples of ints
assertThat( isStringAssignable( TypeKind.INT, true, "0" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "2" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0372" ) ).isTrue();
//assertThat( isStringAssignable( TypeKind.INT, true, "0xDada_Cafe" ) ).isTrue(); java8
assertThat( isStringAssignable( TypeKind.INT, true, "1996" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "0x00_FF__00_FF" ) ).isTrue();
// some examples of longs
assertThat( isStringAssignable( TypeKind.LONG, true, "0777l" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0x100000000L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "2_147_483_648L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "0xC0B0L" ) ).isTrue();
}
/**
* checkout https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
*
* The following example shows other ways you can use the underscore in numeric literals:
*/
@Test
public void testFloatingPoingLiteralFromJLS() {
// The largest positive finite literal of type float is 3.4028235e38f.
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3.4028235e38f" ) ).isTrue();
// The smallest positive finite non-zero literal of type float is 1.40e-45f.
assertThat( isStringAssignable( TypeKind.FLOAT, true, "1.40e-45f" ) ).isTrue();
// The largest positive finite literal of type double is 1.7976931348623157e308.
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "1.7976931348623157e308" ) ).isTrue();
// The smallest positive finite non-zero literal of type double is 4.9e-324
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9e-324" ) ).isTrue();
// some floats
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3.1e1F" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "2.f" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, ".3f" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "0f" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3.14f" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "6.022137e+23f" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "-3.14f" ) ).isTrue();
// some doubles
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "1e1" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "1e+1" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "2." ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, ".3" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "0.0" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "3.14" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "-3.14" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "1e-9D" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "1e137" ) ).isTrue();
// too large (infinitve)
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3.4028235e38f" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "1.7976931348623157e308" ) ).isTrue();
// too large (infinitve)
assertThat( isStringAssignable( TypeKind.FLOAT, true, "3.4028235e39f" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "1.7976931348623159e308" ) ).isFalse();
// small
assertThat( isStringAssignable( TypeKind.FLOAT, true, "1.40e-45f" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "0x1.0p-149" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9e-324" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "0x0.001P-1062d" ) ).isTrue();
// too small
assertThat( isStringAssignable( TypeKind.FLOAT, true, "1.40e-46f" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "0x1.0p-150" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9e-325" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "0x0.001p-1063d" ) ).isFalse();
}
/**
* checkout https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
*
* The following example shows other ways you can use the underscore in numeric literals:
*/
@Test
public void testBooleanLiteralFromJLS() {
assertThat( isStringAssignable( TypeKind.BOOLEAN, true, "true" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.BOOLEAN, true, "false" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.BOOLEAN, true, "FALSE" ) ).isFalse();
}
/**
* checkout https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
*
* The following example shows other ways you can use the underscore in numeric literals:
*/
@Test
public void testCharLiteralFromJLS() {
assertThat( isStringAssignable( TypeKind.CHAR, true, "'a'" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'%'" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'\t'" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'\\'" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'\''" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'\u03a9'" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'\uFFFF'" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'\177'" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'Ω'" ) ).isTrue();
}
@Test
public void testShortAndByte() {
assertThat( isStringAssignable( TypeKind.SHORT, true, "0xFE" ) ).isTrue();
// some examples of ints
assertThat( isStringAssignable( TypeKind.BYTE, true, "0" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.BYTE, true, "2" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.BYTE, true, "127" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.BYTE, true, "-128" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.SHORT, true, "1996" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.SHORT, true, "-1996" ) ).isTrue();
}
@Test
public void testMiscellaneousErroneousPatterns() {
assertThat( isStringAssignable( TypeKind.INT, true, "1F" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "1D" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.INT, true, "_1" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.INT, true, "1_" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.INT, true, "0x_1" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.INT, true, "0_x1" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9e_-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9_e-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4._9e-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4_.9e-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "_4.9e-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9E-3_" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9E_-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9E-_3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9E+-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9E+_3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "4.9_E-3" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "0x0.001_P-10d" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "0x0.001P_-10d" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "0x0.001_p-10d" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.DOUBLE, true, "0x0.001p_-10d" ) ).isFalse();
}
@Test
public void testNegatives() {
assertThat( isStringAssignable( TypeKind.INT, true, "-0xffaa" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "-0377_7777" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.INT, true, "-0b1111_1111" ) ).isTrue();
}
@Test
public void testFaultyChar() {
assertThat( isStringAssignable( TypeKind.CHAR, true, "''" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'a" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'aa" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.CHAR, true, "a'" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.CHAR, true, "aa'" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.CHAR, true, "'" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.CHAR, true, "a" ) ).isFalse();
}
@Test
public void testFloatWithLongLiteral() {
assertThat( isStringAssignable( TypeKind.FLOAT, true, "156L" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.FLOAT, true, "156l" ) ).isTrue();
}
@Test
public void testLongPrimitivesAndNonRequiredLongSuffix() {
assertThat( isStringAssignable( TypeKind.LONG, true, "156" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "156l" ) ).isTrue();
assertThat( isStringAssignable( TypeKind.LONG, true, "156L" ) ).isTrue();
}
@Test
public void testIntPrimitiveWithLongSuffix() {
assertThat( isStringAssignable( TypeKind.INT, true, "156l" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.INT, true, "156L" ) ).isFalse();
}
@Test
public void testTooBigIntegersAndBigLongs() {
assertThat( isStringAssignable( TypeKind.INT, true, "0xFFFF_FFFF_FFFF" ) ).isFalse();
assertThat( isStringAssignable( TypeKind.LONG, true, "0xFFFF_FFFF_FFFF_FFFF_FFFF" ) ).isFalse();
}
@Test
public void testNonSupportedPrimitiveType() {
assertThat( isStringAssignable( TypeKind.VOID, true, "0xFFFF_FFFF_FFFF" ) ).isFalse();
}
}

View File

@ -35,7 +35,7 @@ public interface ErroneousMapper1 {
@Mapping(target = "stringConstant", constant = "stringConstant"), @Mapping(target = "stringConstant", constant = "stringConstant"),
@Mapping(target = "emptyStringConstant", constant = ""), @Mapping(target = "emptyStringConstant", constant = ""),
@Mapping(source = "test", target = "integerConstant", constant = "14"), @Mapping(source = "test", target = "integerConstant", constant = "14"),
@Mapping(target = "longWrapperConstant", constant = "3001"), @Mapping(target = "longWrapperConstant", constant = "3001L"),
@Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"), @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"),
@Mapping(target = "nameConstants", constant = "jack-jill-tom"), @Mapping(target = "nameConstants", constant = "jack-jill-tom"),
@Mapping(target = "country", constant = "THE_NETHERLANDS") @Mapping(target = "country", constant = "THE_NETHERLANDS")

View File

@ -35,7 +35,7 @@ public interface ErroneousMapper3 {
@Mapping(target = "stringConstant", constant = "stringConstant"), @Mapping(target = "stringConstant", constant = "stringConstant"),
@Mapping(target = "emptyStringConstant", constant = ""), @Mapping(target = "emptyStringConstant", constant = ""),
@Mapping(target = "integerConstant", expression = "java('test')", constant = "14"), @Mapping(target = "integerConstant", expression = "java('test')", constant = "14"),
@Mapping(target = "longWrapperConstant", constant = "3001"), @Mapping(target = "longWrapperConstant", constant = "3001L"),
@Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"), @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"),
@Mapping(target = "nameConstants", constant = "jack-jill-tom"), @Mapping(target = "nameConstants", constant = "jack-jill-tom"),
@Mapping(target = "country", constant = "THE_NETHERLANDS") @Mapping(target = "country", constant = "THE_NETHERLANDS")

View File

@ -35,7 +35,7 @@ public interface ErroneousMapper4 {
@Mapping(target = "stringConstant", constant = "stringConstant"), @Mapping(target = "stringConstant", constant = "stringConstant"),
@Mapping(target = "emptyStringConstant", constant = ""), @Mapping(target = "emptyStringConstant", constant = ""),
@Mapping(source = "test", target = "integerConstant", expression = "java('test')"), @Mapping(source = "test", target = "integerConstant", expression = "java('test')"),
@Mapping(target = "longWrapperConstant", constant = "3001"), @Mapping(target = "longWrapperConstant", constant = "3001L"),
@Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"), @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"),
@Mapping(target = "nameConstants", constant = "jack-jill-tom"), @Mapping(target = "nameConstants", constant = "jack-jill-tom"),
@Mapping(target = "country", constant = "THE_NETHERLANDS") @Mapping(target = "country", constant = "THE_NETHERLANDS")

View File

@ -35,7 +35,7 @@ public interface ErroneousMapper5 {
@Mapping(target = "stringConstant", constant = "stringConstant"), @Mapping(target = "stringConstant", constant = "stringConstant"),
@Mapping(target = "emptyStringConstant", constant = ""), @Mapping(target = "emptyStringConstant", constant = ""),
@Mapping(target = "integerConstant", constant = "14"), @Mapping(target = "integerConstant", constant = "14"),
@Mapping(target = "longWrapperConstant", constant = "3001"), @Mapping(target = "longWrapperConstant", constant = "3001L"),
@Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"), @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"),
@Mapping(target = "nameConstants", constant = "jack-jill-tom"), @Mapping(target = "nameConstants", constant = "jack-jill-tom"),
@Mapping(target = "country", constant = "DENMARK") @Mapping(target = "country", constant = "DENMARK")

View File

@ -0,0 +1,46 @@
/**
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.source.constants;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper(uses = StringListMapper.class)
public interface ErroneousMapper6 {
ErroneousMapper6 INSTANCE = Mappers.getMapper( ErroneousMapper6.class );
@Mappings({
@Mapping(target = "stringConstant", constant = "stringConstant"),
@Mapping(target = "emptyStringConstant", constant = ""),
@Mapping(target = "integerConstant", constant = "14"),
@Mapping(target = "longWrapperConstant", constant = "3001"),
@Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"),
@Mapping(target = "nameConstants", constant = "jack-jill-tom"),
@Mapping(target = "country", constant = "THE_NETHERLANDS")
})
Target sourceToTarget(Source s);
Source targetToSource(Target t);
}

View File

@ -209,7 +209,7 @@ public class SourceConstantsTest {
+ "constants.CountryEnum for property \"country\".$"), + "constants.CountryEnum for property \"country\".$"),
@Diagnostic(type = ErroneousMapper5.class, @Diagnostic(type = ErroneousMapper5.class,
kind = Kind.ERROR, kind = Kind.ERROR,
line = 43, line = 41,
messageRegExp = "^Can't map \"java.lang.String \"DENMARK\"\" to \"org.mapstruct.ap.test.source." messageRegExp = "^Can't map \"java.lang.String \"DENMARK\"\" to \"org.mapstruct.ap.test.source."
+ "constants.CountryEnum country\".$") + "constants.CountryEnum country\".$")
} }
@ -217,6 +217,28 @@ public class SourceConstantsTest {
public void errorOnNonExistingEnumConstant() throws ParseException { public void errorOnNonExistingEnumConstant() throws ParseException {
} }
@Test
@IssueKey("1401")
@WithClasses({
Source.class,
Target.class,
CountryEnum.class,
ErroneousMapper6.class,
StringListMapper.class
})
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousMapper6.class,
kind = Kind.ERROR,
line = 38,
messageRegExp = "^.*Can't map \"java.lang.String \"3001\"\" to \"java.lang.Long "
+ "longWrapperConstant\".*$")
}
)
public void cannotMapIntConstantToLong() throws ParseException {
}
private Date getDate(String format, String date) throws ParseException { private Date getDate(String format, String date) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat( format ); SimpleDateFormat dateFormat = new SimpleDateFormat( format );
Date result = dateFormat.parse( date ); Date result = dateFormat.parse( date );

View File

@ -35,7 +35,7 @@ public interface SourceTargetMapper {
@Mapping(target = "stringConstant", constant = "stringConstant"), @Mapping(target = "stringConstant", constant = "stringConstant"),
@Mapping(target = "emptyStringConstant", constant = ""), @Mapping(target = "emptyStringConstant", constant = ""),
@Mapping(target = "integerConstant", constant = "14"), @Mapping(target = "integerConstant", constant = "14"),
@Mapping(target = "longWrapperConstant", constant = "3001"), @Mapping(target = "longWrapperConstant", constant = "3001L"),
@Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"), @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"),
@Mapping(target = "nameConstants", constant = "jack-jill-tom"), @Mapping(target = "nameConstants", constant = "jack-jill-tom"),
@Mapping(target = "country", constant = "THE_NETHERLANDS") @Mapping(target = "country", constant = "THE_NETHERLANDS")