From 607d0fd6f0e0c192c75d74c08abeb15ed6d0c3c4 Mon Sep 17 00:00:00 2001 From: cliedeman Date: Tue, 22 Dec 2015 00:18:46 +0200 Subject: [PATCH] Implemented Feature from Issue #679: Support of numberFormat for Number to String mapping --- .../java/org/mapstruct/IterableMapping.java | 8 + .../src/main/java/org/mapstruct/Mapping.java | 8 + core/src/main/java/org/mapstruct/Mapping.java | 8 + .../BigDecimalToStringConversion.java | 24 ++- .../BigIntegerToStringConversion.java | 24 ++- .../PossibleNumberToStringConversion.java | 168 ++++++++++++++++++ .../PrimitiveToStringConversion.java | 19 +- .../internal/conversion/SimpleConversion.java | 17 +- .../conversion/WrapperToStringConversion.java | 17 +- .../ap/internal/model/BeanMappingMethod.java | 3 + .../ap/internal/model/MapMappingMethod.java | 1 + .../internal/model/MappingBuilderContext.java | 1 + .../ap/internal/model/PropertyMapping.java | 6 + .../model/common/ConversionContext.java | 2 + .../common/DefaultConversionContext.java | 9 +- .../model/source/IterableMapping.java | 11 ++ .../ap/internal/model/source/Mapping.java | 10 ++ .../creation/MappingResolverImpl.java | 11 +- .../ap/internal/util/NativeTypes.java | 29 +++ .../common/DefaultConversionContextTest.java | 6 +- .../ap/internal/util/NativeTypesTest.java | 47 +++++ .../numbers/NumberFormatConversionTest.java | 120 +++++++++++++ .../ap/test/conversion/numbers/Source.java | 136 ++++++++++++++ .../numbers/SourceTargetMapper.java | 53 ++++++ .../ap/test/conversion/numbers/Target.java | 133 ++++++++++++++ 25 files changed, 818 insertions(+), 53 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/PossibleNumberToStringConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Target.java diff --git a/core-common/src/main/java/org/mapstruct/IterableMapping.java b/core-common/src/main/java/org/mapstruct/IterableMapping.java index 8fecdff69..faa6fa6d7 100644 --- a/core-common/src/main/java/org/mapstruct/IterableMapping.java +++ b/core-common/src/main/java/org/mapstruct/IterableMapping.java @@ -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. diff --git a/core-jdk8/src/main/java/org/mapstruct/Mapping.java b/core-jdk8/src/main/java/org/mapstruct/Mapping.java index 995473eb9..3d1420eb5 100644 --- a/core-jdk8/src/main/java/org/mapstruct/Mapping.java +++ b/core-jdk8/src/main/java/org/mapstruct/Mapping.java @@ -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 diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 3728f1785..4d4bce29f 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -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 diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java index 72a97fe44..68fb04811 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java @@ -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 ".toString()"; } @Override - public String getFromExpression(ConversionContext conversionContext) { + protected String getFallbackFromExpression(ConversionContext conversionContext) { return "new BigDecimal( )"; } @Override protected Set getFromConversionImportTypes(ConversionContext conversionContext) { - return asSet( conversionContext.getTypeFactory().getType( BigDecimal.class ) ); + Set importTypes = new HashSet(); + importTypes.addAll( super.getFromConversionImportTypes( conversionContext ) ); + importTypes.add( conversionContext.getTypeFactory().getType( BigDecimal.class ) ); + return importTypes; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java index e84de7e5a..4cdb6a47e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java @@ -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 ".toString()"; } @Override - public String getFromExpression(ConversionContext conversionContext) { + protected String getFallbackFromExpression(ConversionContext conversionContext) { return "new BigInteger( )"; } @Override protected Set getFromConversionImportTypes(ConversionContext conversionContext) { - return asSet( conversionContext.getTypeFactory().getType( BigInteger.class ) ); + Set importTypes = new HashSet(); + importTypes.addAll( super.getFromConversionImportTypes( conversionContext ) ); + importTypes.add( conversionContext.getTypeFactory().getType( BigInteger.class ) ); + return importTypes; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/PossibleNumberToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/PossibleNumberToStringConversion.java new file mode 100644 index 000000000..b5c601321 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/PossibleNumberToStringConversion.java @@ -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 if + * 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 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 getFromConversionImportTypes(ConversionContext conversionContext) { + if ( isDecimalFormatConversion( conversionContext ) ) { + return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) ); + } + else { + return super.getFromConversionImportTypes( conversionContext ); + } + } + + protected List 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( "( )" ); + + return sb.toString(); + } + + private String getFormatterFromConversionExpression(ConversionContext conversionContext) { + StringBuilder sb = new StringBuilder(); + + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".parse" ); + sb.append( "( ).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 + " )"; + } + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java index 0ec41e38e..68a78b86b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java @@ -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( )"; } @Override - public String getFromExpression(ConversionContext conversionContext) { - return wrapperType.getSimpleName() + ".parse" + - Strings.capitalize( sourceType.getSimpleName() ) + "( )"; + protected String getFallbackFromExpression(ConversionContext conversionContext) { + return getParseTypeExpression( "" ); } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java index b6cbd5719..483c3f4b8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java @@ -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.emptyList(), + getToConversionExceptionTypes( conversionContext ), toExpression ); } @@ -49,7 +50,7 @@ public abstract class SimpleConversion implements ConversionProvider { String fromExpression = getFromExpression( conversionContext ); return AssignmentFactory.createTypeConversion( getFromConversionImportTypes( conversionContext ), - Collections.emptyList(), + getFromConversionExceptionTypes( conversionContext ), fromExpression ); } @@ -83,7 +84,7 @@ public abstract class SimpleConversion implements ConversionProvider { * @return conversion types required in the "from" conversion */ protected Set getFromConversionImportTypes(ConversionContext conversionContext) { - return Collections.emptySet(); + return Collections.emptySet(); } /** @@ -95,6 +96,14 @@ public abstract class SimpleConversion implements ConversionProvider { * @return conversion types required in the "to" conversion */ protected Set getToConversionImportTypes(ConversionContext conversionContext) { - return Collections.emptySet(); + return Collections.emptySet(); + } + + protected List getToConversionExceptionTypes(ConversionContext conversionContext) { + return Collections.emptyList(); + } + + protected List getFromConversionExceptionTypes(ConversionContext conversionContext) { + return Collections.emptyList(); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java index 825f07a76..c34b42788 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java @@ -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( )"; } @Override - public String getFromExpression(ConversionContext conversionContext) { + protected String getFallbackFromExpression(ConversionContext conversionContext) { return sourceType.getSimpleName() + ".parse" + - Strings.capitalize( primitiveType.getSimpleName() ) + "( )"; + Strings.capitalize( primitiveType.getSimpleName() ) + "( )"; } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 97d4716d8..8b88b2285 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -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.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.emptyList() ) .build(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index 7ca4cf4d9..a6c07cced 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -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; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index 2e30c857f..8300eb7e4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -91,6 +91,7 @@ public class MappingBuilderContext { *
  • null, no assignment found
  • * */ + @SuppressWarnings("checkstyle:parameternumber") Assignment getTargetAssignment(Method mappingMethod, String mappedElement, Type sourceType, Type targetType, String targetPropertyName, String dateFormat, SelectionParameters selectionParameters, String sourceReference, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index fa12d0b97..432210256 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -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; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java index f16432c60..10fe32fc6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java @@ -43,6 +43,8 @@ public interface ConversionContext { */ String getDateFormat(); + String getNumberFormat(); + TypeFactory getTypeFactory(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java index 53d48440e..2e225c77d 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java @@ -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; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMapping.java index 279706ea9..71b37195e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMapping.java @@ -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; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java index 31e91de3b..125ee5fb5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java @@ -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, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index dcc4fc475..c2e39ad77 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -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 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 virtualMethodCandidates; private ResolvingAttempt(List 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(); 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; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java index 738d572cc..ae9ba4a81 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java @@ -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> WRAPPER_TO_PRIMITIVE_TYPES; private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPES; + private static final Set> NUMBER_TYPES = new HashSet>(); 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 ); + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java index e321e42a4..8c6523d89 100755 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java @@ -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(); } diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java new file mode 100644 index 000000000..ab7a1f22a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java @@ -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 ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java new file mode 100644 index 000000000..9d18cb6f1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java @@ -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" ) ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Source.java new file mode 100644 index 000000000..7c1d18e80 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Source.java @@ -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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java new file mode 100644 index 000000000..a4daa644c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java @@ -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); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Target.java new file mode 100644 index 000000000..669d83a1c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/Target.java @@ -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; + } +}