Implemented Feature from Issue #679: Support of numberFormat for Number to String mapping

This commit is contained in:
cliedeman 2015-12-22 00:18:46 +02:00 committed by sjaakd
parent d74f73aa2d
commit 607d0fd6f0
25 changed files with 818 additions and 53 deletions

View File

@ -46,6 +46,14 @@ public @interface IterableMapping {
*/
String dateFormat() default "";
/**
* A format string as processable by {@link java.text.DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types.
*
* @return A decimal format string as processable by {@link java.text.DecimalFormat}.
*/
String numberFormat() default "";
/**
* A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
* mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error.

View File

@ -84,6 +84,14 @@ public @interface Mapping {
*/
String dateFormat() default "";
/**
* A format string as processable by {@link java.text.DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types.
*
* @return A decimal format string as processable by {@link java.text.DecimalFormat}.
*/
String numberFormat() default "";
/**
* A constant {@link String} based on which the specified target property is to be set. If the designated target
* property is not of type {@code String}, the value will be converted by applying a matching conversion method or

View File

@ -82,6 +82,14 @@ public @interface Mapping {
*/
String dateFormat() default "";
/**
* A format string as processable by {@link java.text.DecimalFormat} if the annotated method maps from a
* {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types.
*
* @return A decimal format string as processable by {@link java.text.DecimalFormat}.
*/
String numberFormat() default "";
/**
* A constant {@link String} based on which the specified target property is to be set. If the designated target
* property is not of type {@code String}, the value will be converted by applying a matching conversion method or

View File

@ -18,33 +18,39 @@
*/
package org.mapstruct.ap.internal.conversion;
import static org.mapstruct.ap.internal.util.Collections.asSet;
import java.math.BigDecimal;
import java.util.Set;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
/**
* Conversion between {@link BigDecimal} and {@link String}.
*
* @author Gunnar Morling
*/
public class BigDecimalToStringConversion extends SimpleConversion {
public class BigDecimalToStringConversion extends PossibleNumberToStringConversion {
public BigDecimalToStringConversion() {
super( BigDecimal.class );
}
@Override
public String getToExpression(ConversionContext conversionContext) {
protected String getFallbackToExpression(ConversionContext conversionContext) {
return "<SOURCE>.toString()";
}
@Override
public String getFromExpression(ConversionContext conversionContext) {
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return "new BigDecimal( <SOURCE> )";
}
@Override
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
return asSet( conversionContext.getTypeFactory().getType( BigDecimal.class ) );
Set<Type> importTypes = new HashSet<Type>();
importTypes.addAll( super.getFromConversionImportTypes( conversionContext ) );
importTypes.add( conversionContext.getTypeFactory().getType( BigDecimal.class ) );
return importTypes;
}
}

View File

@ -18,33 +18,39 @@
*/
package org.mapstruct.ap.internal.conversion;
import static org.mapstruct.ap.internal.util.Collections.asSet;
import java.math.BigInteger;
import java.util.Set;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
/**
* Conversion between {@link BigInteger} and {@link String}.
*
* @author Gunnar Morling
*/
public class BigIntegerToStringConversion extends SimpleConversion {
public class BigIntegerToStringConversion extends PossibleNumberToStringConversion {
public BigIntegerToStringConversion() {
super( BigInteger.class );
}
@Override
public String getToExpression(ConversionContext conversionContext) {
protected String getFallbackToExpression(ConversionContext conversionContext) {
return "<SOURCE>.toString()";
}
@Override
public String getFromExpression(ConversionContext conversionContext) {
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return "new BigInteger( <SOURCE> )";
}
@Override
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
return asSet( conversionContext.getTypeFactory().getType( BigInteger.class ) );
Set<Type> importTypes = new HashSet<Type>();
importTypes.addAll( super.getFromConversionImportTypes( conversionContext ) );
importTypes.add( conversionContext.getTypeFactory().getType( BigInteger.class ) );
return importTypes;
}
}

View File

@ -0,0 +1,168 @@
/**
* Copyright 2012-2015 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.internal.conversion;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.util.NativeTypes;
import org.mapstruct.ap.internal.util.Strings;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Conversion hooks for {@link Number} to {@link String}. The idea being that <em>if</em>
* we have a conversion to string and the source type is a {@code Number} and the mapping
* has a {@link org.mapstruct.Mapping#numberFormat()} we will replace the standard conversion
* with a {@link DecimalFormat} version
*
* @author Ciaran Liedeman
*/
public abstract class PossibleNumberToStringConversion extends SimpleConversion {
protected final Class<?> primitiveType;
protected final Class<?> wrapperType;
private final Class<?> sourceType;
private boolean sourceTypeNumberSubclass;
public PossibleNumberToStringConversion(final Class<?> sourceType) {
this.sourceType = sourceType;
if ( sourceType.isPrimitive() ) {
wrapperType = NativeTypes.getWrapperType( sourceType );
primitiveType = sourceType;
}
else {
wrapperType = sourceType;
primitiveType = NativeTypes.getPrimitiveType( sourceType );
}
sourceTypeNumberSubclass = NativeTypes.isNumber( primitiveType )
|| NativeTypes.isNumber( sourceType );
}
@Override
protected String getToExpression(ConversionContext conversionContext) {
if ( isDecimalFormatConversion( conversionContext ) ) {
return getFormatterToConversionExpression( conversionContext );
}
else {
return getFallbackToExpression( conversionContext );
}
}
@Override
protected String getFromExpression(ConversionContext conversionContext) {
if ( isDecimalFormatConversion( conversionContext ) ) {
return getFormatterFromConversionExpression( conversionContext );
}
else {
return getFallbackFromExpression( conversionContext );
}
}
protected abstract String getFallbackToExpression(ConversionContext conversionContext);
protected abstract String getFallbackFromExpression(ConversionContext conversionContext);
public Set<Type> getToConversionImportTypes(ConversionContext conversionContext) {
if ( isDecimalFormatConversion( conversionContext ) ) {
return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) );
}
else {
return super.getToConversionImportTypes( conversionContext );
}
}
private boolean isDecimalFormatConversion(ConversionContext conversionContext) {
return sourceTypeNumberSubclass && conversionContext.getNumberFormat() != null;
}
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
if ( isDecimalFormatConversion( conversionContext ) ) {
return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) );
}
else {
return super.getFromConversionImportTypes( conversionContext );
}
}
protected List<Type> getFromConversionExceptionTypes(ConversionContext conversionContext) {
if ( isDecimalFormatConversion( conversionContext ) ) {
return Collections.singletonList( conversionContext.getTypeFactory().getType( ParseException.class ) );
}
else {
return super.getFromConversionExceptionTypes( conversionContext );
}
}
private String getFormatterToConversionExpression(ConversionContext conversionContext) {
StringBuilder sb = new StringBuilder();
appendDecimalFormatter( sb, conversionContext );
sb.append( ".format" );
sb.append( "( <SOURCE> )" );
return sb.toString();
}
private String getFormatterFromConversionExpression(ConversionContext conversionContext) {
StringBuilder sb = new StringBuilder();
appendDecimalFormatter( sb, conversionContext );
sb.append( ".parse" );
sb.append( "( <SOURCE> ).toString()" );
return getParseTypeExpression( sb.toString() );
}
private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversionContext) {
sb.append( "new DecimalFormat( " );
if ( conversionContext.getNumberFormat() != null ) {
sb.append( "\"" );
sb.append( conversionContext.getNumberFormat() );
sb.append( "\"" );
}
sb.append( " )" );
}
protected String getParseTypeExpression(String input) {
if ( BigDecimal.class == sourceType ) {
// TODO this step should be avoided, we return a BigDecimal from the formatter
return "new BigDecimal(" + input + ")";
}
else if ( BigInteger.class == sourceType ) {
return "new BigInteger(" + input + ")";
}
else {
return wrapperType.getSimpleName() + ".parse" +
Strings.capitalize( primitiveType.getSimpleName() ) + "( " + input + " )";
}
}
}

View File

@ -19,8 +19,6 @@
package org.mapstruct.ap.internal.conversion;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.util.NativeTypes;
import org.mapstruct.ap.internal.util.Strings;
/**
* Conversion between primitive types such as {@code byte} or {@code long} and
@ -28,28 +26,23 @@ import org.mapstruct.ap.internal.util.Strings;
*
* @author Gunnar Morling
*/
public class PrimitiveToStringConversion extends SimpleConversion {
private final Class<?> sourceType;
private final Class<?> wrapperType;
public class PrimitiveToStringConversion extends PossibleNumberToStringConversion {
public PrimitiveToStringConversion(Class<?> sourceType) {
super( sourceType );
if ( !sourceType.isPrimitive() ) {
throw new IllegalArgumentException( sourceType + " is no primitive type." );
}
this.sourceType = sourceType;
this.wrapperType = NativeTypes.getWrapperType( sourceType );
}
@Override
public String getToExpression(ConversionContext conversionContext) {
protected String getFallbackToExpression(ConversionContext conversionContext) {
return "String.valueOf( <SOURCE> )";
}
@Override
public String getFromExpression(ConversionContext conversionContext) {
return wrapperType.getSimpleName() + ".parse" +
Strings.capitalize( sourceType.getSimpleName() ) + "( <SOURCE> )";
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return getParseTypeExpression( "<SOURCE>" );
}
}

View File

@ -19,6 +19,7 @@
package org.mapstruct.ap.internal.conversion;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.mapstruct.ap.internal.model.AssignmentFactory;
@ -39,7 +40,7 @@ public abstract class SimpleConversion implements ConversionProvider {
String toExpression = getToExpression( conversionContext );
return AssignmentFactory.createTypeConversion(
getToConversionImportTypes( conversionContext ),
Collections.<Type>emptyList(),
getToConversionExceptionTypes( conversionContext ),
toExpression
);
}
@ -49,7 +50,7 @@ public abstract class SimpleConversion implements ConversionProvider {
String fromExpression = getFromExpression( conversionContext );
return AssignmentFactory.createTypeConversion(
getFromConversionImportTypes( conversionContext ),
Collections.<Type>emptyList(),
getFromConversionExceptionTypes( conversionContext ),
fromExpression
);
}
@ -83,7 +84,7 @@ public abstract class SimpleConversion implements ConversionProvider {
* @return conversion types required in the "from" conversion
*/
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
return Collections.<Type>emptySet();
return Collections.emptySet();
}
/**
@ -95,6 +96,14 @@ public abstract class SimpleConversion implements ConversionProvider {
* @return conversion types required in the "to" conversion
*/
protected Set<Type> getToConversionImportTypes(ConversionContext conversionContext) {
return Collections.<Type>emptySet();
return Collections.emptySet();
}
protected List<Type> getToConversionExceptionTypes(ConversionContext conversionContext) {
return Collections.emptyList();
}
protected List<Type> getFromConversionExceptionTypes(ConversionContext conversionContext) {
return Collections.emptyList();
}
}

View File

@ -19,7 +19,6 @@
package org.mapstruct.ap.internal.conversion;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.util.NativeTypes;
import org.mapstruct.ap.internal.util.Strings;
/**
@ -27,28 +26,24 @@ import org.mapstruct.ap.internal.util.Strings;
*
* @author Gunnar Morling
*/
public class WrapperToStringConversion extends SimpleConversion {
public class WrapperToStringConversion extends PossibleNumberToStringConversion {
private final Class<?> sourceType;
private final Class<?> primitiveType;
public WrapperToStringConversion(Class<?> sourceType) {
if ( sourceType.isPrimitive() ) {
throw new IllegalArgumentException( sourceType + " is no wrapper type." );
}
super( sourceType );
this.sourceType = sourceType;
this.primitiveType = NativeTypes.getPrimitiveType( sourceType );
}
@Override
public String getToExpression(ConversionContext conversionContext) {
protected String getFallbackToExpression(ConversionContext conversionContext) {
return "String.valueOf( <SOURCE> )";
}
@Override
public String getFromExpression(ConversionContext conversionContext) {
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return sourceType.getSimpleName() + ".parse" +
Strings.capitalize( primitiveType.getSimpleName() ) + "( <SOURCE> )";
Strings.capitalize( primitiveType.getSimpleName() ) + "( <SOURCE> )";
}
}

View File

@ -294,6 +294,7 @@ public class BeanMappingMethod extends MappingMethod {
.sourceReference( sourceRef )
.selectionParameters( mapping.getSelectionParameters() )
.dateFormat( mapping.getDateFormat() )
.numberFormat( mapping.getNumberFormat() )
.existingVariableNames( existingVariableNames )
.dependsOn( mapping.getDependsOn() )
.defaultValue( mapping.getDefaultValue() )
@ -416,6 +417,7 @@ public class BeanMappingMethod extends MappingMethod {
.sourceReference( sourceRef )
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
.dateFormat( mapping != null ? mapping.getDateFormat() : null )
.numberFormat( mapping != null ? mapping.getNumberFormat() : null )
.defaultValue( mapping != null ? mapping.getDefaultValue() : null )
.existingVariableNames( existingVariableNames )
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
@ -480,6 +482,7 @@ public class BeanMappingMethod extends MappingMethod {
.sourceReference( sourceRef )
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
.dateFormat( mapping != null ? mapping.getDateFormat() : null )
.numberFormat( mapping != null ? mapping.getNumberFormat() : null )
.existingVariableNames( existingVariableNames )
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
.build();

View File

@ -51,6 +51,7 @@ public class MapMappingMethod extends MappingMethod {
public static class Builder {
private String keyDateFormat;
private String keyNumberFormat;
private String valueDateFormat;
private Method method;
private MappingBuilderContext ctx;

View File

@ -91,6 +91,7 @@ public class MappingBuilderContext {
* <li>null, no assignment found</li>
* </ol>
*/
@SuppressWarnings("checkstyle:parameternumber")
Assignment getTargetAssignment(Method mappingMethod, String mappedElement, Type sourceType, Type targetType,
String targetPropertyName, String dateFormat,
SelectionParameters selectionParameters, String sourceReference,

View File

@ -167,6 +167,7 @@ public class PropertyMapping extends ModelElement {
// initial properties
private String dateFormat;
private String numberFormat;
private String defaultValue;
private SourceReference sourceReference;
private SelectionParameters selectionParameters;
@ -186,6 +187,11 @@ public class PropertyMapping extends ModelElement {
return this;
}
public PropertyMappingBuilder numberFormat(String numberFormat) {
this.numberFormat = numberFormat;
return this;
}
public PropertyMappingBuilder defaultValue(String defaultValue) {
this.defaultValue = defaultValue;
return this;

View File

@ -43,6 +43,8 @@ public interface ConversionContext {
*/
String getDateFormat();
String getNumberFormat();
TypeFactory getTypeFactory();
}

View File

@ -32,15 +32,17 @@ public class DefaultConversionContext implements ConversionContext {
private final Type sourceType;
private final Type targetType;
private final String dateFormat;
private final String numberFormat;
private final TypeFactory typeFactory;
public DefaultConversionContext(TypeFactory typeFactory, FormattingMessager messager, Type sourceType,
Type targetType, String dateFormat) {
Type targetType, String dateFormat, String numberFormat) {
this.typeFactory = typeFactory;
this.messager = messager;
this.sourceType = sourceType;
this.targetType = targetType;
this.dateFormat = dateFormat;
this.numberFormat = numberFormat;
validateDateFormat();
}
@ -63,6 +65,11 @@ public class DefaultConversionContext implements ConversionContext {
return targetType;
}
@Override
public String getNumberFormat() {
return numberFormat;
}
@Override
public String getDateFormat() {
return dateFormat;

View File

@ -39,6 +39,7 @@ public class IterableMapping {
private final SelectionParameters selectionParameters;
private final AnnotationMirror mirror;
private final AnnotationValue dateFormatAnnotationValue;
private final AnnotationValue numberFormatAnnotationValue;
private final NullValueMappingStrategyPrism nullValueMappingStrategy;
public static IterableMapping fromPrism(IterableMappingPrism iterableMapping, ExecutableElement method,
@ -72,6 +73,7 @@ public class IterableMapping {
selection,
iterableMapping.mirror,
iterableMapping.values.dateFormat(),
iterableMapping.values.numberFormat(),
nullValueMappingStrategy
);
}
@ -83,6 +85,7 @@ public class IterableMapping {
this.selectionParameters = selectionParameters;
this.mirror = mirror;
this.dateFormatAnnotationValue = dateFormatAnnotationValue;
this.numberFormatAnnotationValue = numberFormatAnnotationValue;
this.nullValueMappingStrategy = nvms;
}
@ -102,6 +105,14 @@ public class IterableMapping {
return dateFormatAnnotationValue;
}
public String getNumberFormat() {
return numberFormat;
}
public AnnotationValue getNumberFormatAnnotationValue() {
return numberFormatAnnotationValue;
}
public NullValueMappingStrategyPrism getNullValueMappingStrategy() {
return nullValueMappingStrategy;
}

View File

@ -55,6 +55,7 @@ public class Mapping {
private final String javaExpression;
private final String targetName;
private final String dateFormat;
private final String numberFormat;
private final String defaultValue;
private final SelectionParameters selectionParameters;
@ -132,6 +133,7 @@ public class Mapping {
String constant = mappingPrism.values.constant() == null ? null : mappingPrism.constant();
String expression = getExpression( mappingPrism, element, messager );
String dateFormat = mappingPrism.values.dateFormat() == null ? null : mappingPrism.dateFormat();
String numberFormat = mappingPrism.values.numberFormat() == null ? null : mappingPrism.numberFormat();
String defaultValue = mappingPrism.values.defaultValue() == null ? null : mappingPrism.defaultValue();
boolean resultTypeIsDefined = mappingPrism.values.resultType() != null;
@ -149,6 +151,7 @@ public class Mapping {
expression,
mappingPrism.target(),
dateFormat,
numberFormat,
defaultValue,
mappingPrism.ignore(),
mappingPrism.mirror,
@ -171,6 +174,7 @@ public class Mapping {
this.javaExpression = javaExpression;
this.targetName = targetName;
this.dateFormat = dateFormat;
this.numberFormat = numberFormat;
this.defaultValue = defaultValue;
this.isIgnored = isIgnored;
this.mirror = mirror;
@ -243,6 +247,10 @@ public class Mapping {
return dateFormat;
}
public String getNumberFormat() {
return numberFormat;
}
public String getDefaultValue() {
return defaultValue;
}
@ -320,6 +328,7 @@ public class Mapping {
null, // expression
sourceName != null ? sourceName : targetName,
dateFormat,
numberFormat,
null,
isIgnored,
mirror,
@ -347,6 +356,7 @@ public class Mapping {
javaExpression,
targetName,
dateFormat,
numberFormat,
defaultValue,
isIgnored,
mirror,

View File

@ -103,6 +103,7 @@ public class MappingResolverImpl implements MappingResolver {
}
@Override
@SuppressWarnings("checkstyle:parameternumber")
public Assignment getTargetAssignment(Method mappingMethod, String mappedElement, Type sourceType,
Type targetType, String targetPropertyName, String dateFormat, SelectionParameters selectionParameters,
String sourceReference, boolean preferUpdateMapping) {
@ -115,6 +116,7 @@ public class MappingResolverImpl implements MappingResolver {
mappingMethod,
mappedElement,
dateFormat,
numberFormat,
sourceReference,
criteria
);
@ -139,6 +141,7 @@ public class MappingResolverImpl implements MappingResolver {
null,
null,
null,
null,
criteria
);
@ -157,6 +160,7 @@ public class MappingResolverImpl implements MappingResolver {
private final String mappedElement;
private final List<SourceMethod> methods;
private final String dateFormat;
private final String numberFormat;
private final SelectionCriteria selectionCriteria;
private final String sourceReference;
private final boolean savedPreferUpdateMapping;
@ -167,12 +171,13 @@ public class MappingResolverImpl implements MappingResolver {
private final Set<VirtualMappingMethod> virtualMethodCandidates;
private ResolvingAttempt(List<SourceMethod> sourceModel, Method mappingMethod, String mappedElement,
String dateFormat, String sourceReference, SelectionCriteria criteria) {
String dateFormat, String numberFormat, String sourceReference, SelectionCriteria criteria) {
this.mappingMethod = mappingMethod;
this.mappedElement = mappedElement;
this.methods = filterPossibleCandidateMethods( sourceModel );
this.dateFormat = dateFormat;
this.numberFormat = numberFormat;
this.sourceReference = sourceReference;
this.virtualMethodCandidates = new HashSet<VirtualMappingMethod>();
this.selectionCriteria = criteria;
@ -257,7 +262,7 @@ public class MappingResolverImpl implements MappingResolver {
}
ConversionContext ctx =
new DefaultConversionContext( typeFactory, messager, sourceType, targetType, dateFormat );
new DefaultConversionContext( typeFactory, messager, sourceType, targetType, dateFormat, numberFormat);
return conversionProvider.to( ctx );
}
@ -289,7 +294,7 @@ public class MappingResolverImpl implements MappingResolver {
virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod ) );
ConversionContext ctx = new DefaultConversionContext( typeFactory, messager,
sourceType,
targetType, dateFormat );
targetType, dateFormat, numberFormat);
Assignment methodReference = AssignmentFactory.createMethodReference( matchingBuiltInMethod, ctx );
methodReference.setAssignment( AssignmentFactory.createDirect( sourceReference ) );
return methodReference;

View File

@ -18,9 +18,13 @@
*/
package org.mapstruct.ap.internal.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Provides functionality around the Java primitive data types and their wrapper
@ -32,6 +36,7 @@ public class NativeTypes {
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES;
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPES;
private static final Set<Class<?>> NUMBER_TYPES = new HashSet<Class<?>>();
private NativeTypes() {
}
@ -60,6 +65,21 @@ public class NativeTypes {
tmp.put( char.class, Character.class );
PRIMITIVE_TO_WRAPPER_TYPES = Collections.unmodifiableMap( tmp );
NUMBER_TYPES.add( byte.class );
NUMBER_TYPES.add( short.class );
NUMBER_TYPES.add( int.class );
NUMBER_TYPES.add( long.class );
NUMBER_TYPES.add( float.class );
NUMBER_TYPES.add( double.class );
NUMBER_TYPES.add( Byte.class );
NUMBER_TYPES.add( Short.class );
NUMBER_TYPES.add( Integer.class );
NUMBER_TYPES.add( Long.class );
NUMBER_TYPES.add( Float.class );
NUMBER_TYPES.add( Double.class );
NUMBER_TYPES.add( BigInteger.class );
NUMBER_TYPES.add( BigDecimal.class );
}
public static Class<?> getWrapperType(Class<?> clazz) {
@ -77,4 +97,13 @@ public class NativeTypes {
return WRAPPER_TO_PRIMITIVE_TYPES.get( clazz );
}
public static boolean isNumber(Class<?> clazz) {
if ( clazz == null ) {
return false;
}
else {
return NUMBER_TYPES.contains( clazz );
}
}
}

View File

@ -78,7 +78,7 @@ public class DefaultConversionContextTest {
Type type = typeWithFQN( JavaTimeConstants.ZONED_DATE_TIME_FQN );
StatefulMessagerMock statefulMessagerMock = new StatefulMessagerMock();
new DefaultConversionContext(
null, statefulMessagerMock, type, type, "qwertz" );
null, statefulMessagerMock, type, type, "qwertz", null);
assertThat( statefulMessagerMock.getLastKindPrinted() ).isEqualTo( Diagnostic.Kind.ERROR );
}
@ -87,7 +87,7 @@ public class DefaultConversionContextTest {
Type type = typeWithFQN( JavaTimeConstants.ZONED_DATE_TIME_FQN );
StatefulMessagerMock statefulMessagerMock = new StatefulMessagerMock();
new DefaultConversionContext(
null, statefulMessagerMock, type, type, null );
null, statefulMessagerMock, type, type, null, null);
assertThat( statefulMessagerMock.getLastKindPrinted() ).isNull();
}
@ -96,7 +96,7 @@ public class DefaultConversionContextTest {
Type type = typeWithFQN( "java.lang.String" );
StatefulMessagerMock statefulMessagerMock = new StatefulMessagerMock();
new DefaultConversionContext(
null, statefulMessagerMock, type, type, "qwertz" );
null, statefulMessagerMock, type, type, "qwertz", null);
assertThat( statefulMessagerMock.getLastKindPrinted() ).isNull();
}

View File

@ -0,0 +1,47 @@
/**
* Copyright 2012-2015 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.internal.util;
import org.junit.Test;
import java.math.BigDecimal;
import java.math.BigInteger;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Ciaran Liedeman
*/
public class NativeTypesTest {
@Test
public void testIsNumber() throws Exception {
assertFalse( NativeTypes.isNumber( null ) );
assertFalse( NativeTypes.isNumber( Object.class ) );
assertFalse( NativeTypes.isNumber( String.class ) );
assertTrue( NativeTypes.isNumber( double.class ) );
assertTrue( NativeTypes.isNumber( Double.class ) );
assertTrue( NativeTypes.isNumber( long.class ) );
assertTrue( NativeTypes.isNumber( Long.class ) );
assertTrue( NativeTypes.isNumber( BigDecimal.class ) );
assertTrue( NativeTypes.isNumber( BigInteger.class ) );
}
}

View File

@ -0,0 +1,120 @@
/**
* Copyright 2012-2015 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.conversion.numbers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import static org.fest.assertions.Assertions.assertThat;
@WithClasses({
Source.class,
Target.class,
SourceTargetMapper.class
})
@RunWith(AnnotationProcessorTestRunner.class)
public class NumberFormatConversionTest {
@Before
public void setDefaultLocale() {
Locale.setDefault( Locale.ENGLISH );
}
@Test
public void shouldApplyStringConversions() {
Source source = new Source();
source.setI( 1 );
source.setIi( 2 );
source.setD( 3.0 );
source.setDd( 4.0 );
source.setL( 5L );
source.setLl( 6L );
source.setB( (byte) 7 );
source.setBb( (byte) 8 );
source.setComplex1( 345346.456756 );
source.setComplex2( 5007034.3 );
source.setBigDecimal1( new BigDecimal( "987E-20" ) );
source.setBigInteger1( new BigInteger( "1234567890000" ) );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getI() ).isEqualTo( "1.00" );
assertThat( target.getIi() ).isEqualTo( "2.00" );
assertThat( target.getD() ).isEqualTo( "3.00" );
assertThat( target.getDd() ).isEqualTo( "4.00" );
assertThat( target.getL() ).isEqualTo( "5.00" );
assertThat( target.getLl() ).isEqualTo( "6.00" );
assertThat( target.getB() ).isEqualTo( "7.00" );
assertThat( target.getBb() ).isEqualTo( "8.00" );
assertThat( target.getComplex1() ).isEqualTo( "345.35E3" );
assertThat( target.getComplex2() ).isEqualTo( "$5007034.30" );
assertThat( target.getBigDecimal1() ).isEqualTo( "9.87E-18" );
assertThat( target.getBigInteger1() ).isEqualTo( "1.23456789E12" );
}
@Test
public void shouldApplyReverseStringConversions() {
Target target = new Target();
target.setI( "1.00" );
target.setIi( "2.00" );
target.setD( "3.00" );
target.setDd( "4.00" );
target.setL( "5.00" );
target.setLl( "6.00" );
target.setB( "7.00" );
target.setBb( "8.00" );
target.setComplex1( "345.35E3" );
target.setComplex2( "$5007034.30" );
target.setBigDecimal1( "9.87E-18" );
target.setBigInteger1( "1.23456789E12" );
Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
assertThat( source ).isNotNull();
assertThat( source.getI() ).isEqualTo( 1 );
assertThat( source.getIi() ).isEqualTo( Integer.valueOf( 2 ) );
assertThat( source.getD() ).isEqualTo( 3.0 );
assertThat( source.getDd() ).isEqualTo( Double.valueOf( 4.0 ) );
assertThat( source.getL() ).isEqualTo( 5L );
assertThat( source.getLl() ).isEqualTo( Long.valueOf( 6L ) );
assertThat( source.getB() ).isEqualTo( (byte) 7 );
assertThat( source.getBb() ).isEqualTo( (byte) 8 );
assertThat( source.getComplex1() ).isEqualTo( 345350.0 );
assertThat( source.getComplex2() ).isEqualTo( 5007034.3 );
assertThat( source.getBigDecimal1() ).isEqualTo( new BigDecimal( "987E-20" ) );
assertThat( source.getBigInteger1() ).isEqualTo( new BigInteger( "1234567890000" ) );
}
}

View File

@ -0,0 +1,136 @@
/**
* Copyright 2012-2015 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.conversion.numbers;
import java.math.BigDecimal;
import java.math.BigInteger;
public class Source {
private int i;
private Integer ii;
private double d;
private Double dd;
private long l;
private Long ll;
private byte b;
private Byte bb;
private double complex1;
private double complex2;
private BigDecimal bigDecimal1;
private BigInteger bigInteger1;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public Integer getIi() {
return ii;
}
public void setIi(Integer ii) {
this.ii = ii;
}
public double getD() {
return d;
}
public void setD(double d) {
this.d = d;
}
public Double getDd() {
return dd;
}
public void setDd(Double dd) {
this.dd = dd;
}
public long getL() {
return l;
}
public void setL(long l) {
this.l = l;
}
public Long getLl() {
return ll;
}
public void setLl(Long ll) {
this.ll = ll;
}
public byte getB() {
return b;
}
public void setB(byte b) {
this.b = b;
}
public Byte getBb() {
return bb;
}
public void setBb(Byte bb) {
this.bb = bb;
}
public double getComplex1() {
return complex1;
}
public void setComplex1(double complex1) {
this.complex1 = complex1;
}
public double getComplex2() {
return complex2;
}
public void setComplex2(double complex2) {
this.complex2 = complex2;
}
public BigDecimal getBigDecimal1() {
return bigDecimal1;
}
public void setBigDecimal1(BigDecimal bigDecimal1) {
this.bigDecimal1 = bigDecimal1;
}
public BigInteger getBigInteger1() {
return bigInteger1;
}
public void setBigInteger1(BigInteger bigInteger1) {
this.bigInteger1 = bigInteger1;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright 2012-2015 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.conversion.numbers;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
String NUMBER_FORMAT = "##.00";
@Mappings( {
@Mapping( target = "i", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "ii", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "d", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "dd", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "l", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "ll", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "b", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "bb", numberFormat = NUMBER_FORMAT ),
@Mapping( target = "complex1", numberFormat = "##0.##E0" ),
@Mapping( target = "complex2", numberFormat = "$#.00" ),
@Mapping( target = "bigDecimal1", numberFormat = "#0.#E0" ),
@Mapping( target = "bigInteger1", numberFormat = "0.#############E0" )
} )
Target sourceToTarget(Source source);
@InheritInverseConfiguration
Source targetToSource(Target target);
}

View File

@ -0,0 +1,133 @@
/**
* Copyright 2012-2015 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.conversion.numbers;
public class Target {
private String i;
private String ii;
private String d;
private String dd;
private String l;
private String ll;
private String b;
private String bb;
private String complex1;
private String complex2;
private String bigDecimal1;
private String bigInteger1;
public String getI() {
return i;
}
public void setI(String i) {
this.i = i;
}
public String getIi() {
return ii;
}
public void setIi(String ii) {
this.ii = ii;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
public String getDd() {
return dd;
}
public void setDd(String dd) {
this.dd = dd;
}
public String getL() {
return l;
}
public void setL(String l) {
this.l = l;
}
public String getLl() {
return ll;
}
public void setLl(String ll) {
this.ll = ll;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getBb() {
return bb;
}
public void setBb(String bb) {
this.bb = bb;
}
public String getComplex1() {
return complex1;
}
public void setComplex1(String complex1) {
this.complex1 = complex1;
}
public String getComplex2() {
return complex2;
}
public void setComplex2(String complex2) {
this.complex2 = complex2;
}
public String getBigDecimal1() {
return bigDecimal1;
}
public void setBigDecimal1(String bigDecimal1) {
this.bigDecimal1 = bigDecimal1;
}
public String getBigInteger1() {
return bigInteger1;
}
public void setBigInteger1(String bigInteger1) {
this.bigInteger1 = bigInteger1;
}
}