diff --git a/copyright.txt b/copyright.txt index 026c2a272..4f1c07002 100644 --- a/copyright.txt +++ b/copyright.txt @@ -4,6 +4,7 @@ Andreas Gudian Christian Schuster Christophe Labouisse +Ciaran Liedeman Dilip Krishnan Ewald Volkert Gunnar Morling diff --git a/core-common/src/main/java/org/mapstruct/IterableMapping.java b/core-common/src/main/java/org/mapstruct/IterableMapping.java index faa6fa6d7..c8a8f6973 100644 --- a/core-common/src/main/java/org/mapstruct/IterableMapping.java +++ b/core-common/src/main/java/org/mapstruct/IterableMapping.java @@ -24,6 +24,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.text.SimpleDateFormat; +import java.text.DecimalFormat; import java.util.Date; /** @@ -47,10 +48,10 @@ public @interface IterableMapping { String dateFormat() default ""; /** - * A format string as processable by {@link java.text.DecimalFormat} if the annotated method maps from a + * A format string as processable by {@link 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}. + * @return A decimal format string as processable by {@link DecimalFormat}. */ String numberFormat() default ""; diff --git a/core-common/src/main/java/org/mapstruct/MapMapping.java b/core-common/src/main/java/org/mapstruct/MapMapping.java index a675fdf30..d2fd3134e 100644 --- a/core-common/src/main/java/org/mapstruct/MapMapping.java +++ b/core-common/src/main/java/org/mapstruct/MapMapping.java @@ -24,6 +24,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.text.SimpleDateFormat; +import java.text.DecimalFormat; import java.util.Date; /** @@ -54,6 +55,22 @@ public @interface MapMapping { */ String valueDateFormat() default ""; + /** + * A format string as processable by {@link DecimalFormat} if the annotated method maps from a + * {@link Number} to a {@link String} or vice-versa. Will be ignored for all other key types. + * + * @return A decimal format string as processable by {@link DecimalFormat}. + */ + String keyNumberFormat() default ""; + + /** + * A format string as processable by {@link DecimalFormat} if the annotated method maps from a + * {@link Number} to a {@link String} or vice-versa. Will be ignored for all other value types. + * + * @return A decimal format string as processable by {@link DecimalFormat}. + */ + String valueNumberFormat() default ""; + /** * A key value 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' diff --git a/core-jdk8/src/main/java/org/mapstruct/Mapping.java b/core-jdk8/src/main/java/org/mapstruct/Mapping.java index 3d1420eb5..ff217389c 100644 --- a/core-jdk8/src/main/java/org/mapstruct/Mapping.java +++ b/core-jdk8/src/main/java/org/mapstruct/Mapping.java @@ -25,6 +25,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.text.SimpleDateFormat; +import java.text.DecimalFormat; import java.util.Date; /** @@ -85,10 +86,10 @@ public @interface Mapping { String dateFormat() default ""; /** - * A format string as processable by {@link java.text.DecimalFormat} if the annotated method maps from a + * A format string as processable by {@link 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}. + * @return A decimal format string as processable by {@link DecimalFormat}. */ String numberFormat() default ""; diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 4d4bce29f..4a902fe70 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -24,6 +24,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.text.SimpleDateFormat; +import java.text.DecimalFormat; import java.util.Date; /** @@ -83,10 +84,10 @@ public @interface Mapping { String dateFormat() default ""; /** - * A format string as processable by {@link java.text.DecimalFormat} if the annotated method maps from a + * A format string as processable by {@link 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}. + * @return A decimal format string as processable by {@link DecimalFormat}. */ String numberFormat() default ""; diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc index 4d2b10045..1698f5b70 100644 --- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc +++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc @@ -505,11 +505,42 @@ Currently the following conversions are applied automatically: Converting from larger data types to smaller ones (e.g. from `long` to `int`) can cause a value or precision loss. There https://github.com/mapstruct/mapstruct/issues/5[will be] an option for raising a warning in such cases in a future MapStruct version. ==== -* Between all Java primitive types (including their wrappers) and `String`, e.g. between `int` and `String` or `Boolean` and `String`. +* Between all Java primitive types (including their wrappers) and `String`, e.g. between `int` and `String` or `Boolean` and `String`. A format string as understood by `java.text.DecimalFormat` can be specified. +.Conversion from int to String +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CarMapper { + + @Mapping(source = "price", numberFormat = "$#.00") + CarDto carToCarDto(Car car); + + @IterableMapping(numberFormat = "$#.00") + List prices(List prices); +} +---- +==== * Between `enum` types and `String`. -* Between big number types (`java.math.BigInteger`, `java.math.BigDecimal`) and Java primitive types (including their wrappers) as well as String +* Between big number types (`java.math.BigInteger`, `java.math.BigDecimal`) and Java primitive types (including their wrappers) as well as String. A format string as understood by `java.text.DecimalFormat` can be specified. +.Conversion from BigDecimal to String +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CarMapper { + + @Mapping(source = "power", numberFormat = "#.##E0") + CarDto carToCarDto(Car car); + +} +---- +==== + * Between `JAXBElement` and `T`, `List>` and `List` @@ -1178,21 +1209,21 @@ public class OrderMapperImpl implements OrderMapper { By default an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@ValueMapping`. This ensures that all constants are mapped in a safe and predictable manner. The generated mapping method will throw an IllegalStateException if for some reason an unrecognized source value occurs. -MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings. It comes in two flavors: `` and ``. +MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings. It comes in two flavors: `` and ``. -In case of source `` MapStruct will continue to map a source enum constant to a target enum constant with the same name. The remainder of the source enum constants will be mapped to the target specified in the `@ValueMapping` with `` source. +In case of source `` MapStruct will continue to map a source enum constant to a target enum constant with the same name. The remainder of the source enum constants will be mapped to the target specified in the `@ValueMapping` with `` source. MapStruct will *not* attempt such name based mapping for `` and directly apply the target specified in the `@ValueMapping` with `` source to the remainder. -MapStruct is able to handle `null` sources and `null` targets by means of the `` keyword. - +MapStruct is able to handle `null` sources and `null` targets by means of the `` keyword. + [TIP] ==== Constants for ``, `` and `` are available in the `MappingConstants` class. ==== - + Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. - + .Enum mapping method, and ==== [source, java, linenums] @@ -1244,8 +1275,8 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper { } ---- ==== - -*Note:* MapStruct would have refrained from mapping the `RETAIL` and `B2B` when `` was used instead of ``. + +*Note:* MapStruct would have refrained from mapping the `RETAIL` and `B2B` when `` was used instead of ``. [WARNING] diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractNumberToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractNumberToStringConversion.java new file mode 100644 index 000000000..c40fc7289 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractNumberToStringConversion.java @@ -0,0 +1,81 @@ +/** + * Copyright 2012-2016 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 java.text.DecimalFormat; +import java.text.ParseException; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Abstract base class for {@link PrimitiveToStringConversion}, {@link WrapperToStringConversion}, + * {@link BigDecimalToStringConversion} and {@link BigIntegerToStringConversion} + * + * Contains shared utility methods. + * + * @author Ciaran Liedeman + */ +public abstract class AbstractNumberToStringConversion extends SimpleConversion { + + private final boolean sourceTypeNumberSubclass; + + public AbstractNumberToStringConversion(boolean sourceTypeNumberSubclass) { + + this.sourceTypeNumberSubclass = sourceTypeNumberSubclass; + } + + + @Override + public Set getToConversionImportTypes(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) ); + } + else { + return super.getToConversionImportTypes( conversionContext ); + } + } + + protected boolean requiresDecimalFormat(ConversionContext conversionContext) { + return sourceTypeNumberSubclass && conversionContext.getNumberFormat() != null; + } + + @Override + protected Set getFromConversionImportTypes(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) ); + } + else { + return super.getFromConversionImportTypes( conversionContext ); + } + } + + @Override + protected List getFromConversionExceptionTypes(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + return Collections.singletonList( conversionContext.getTypeFactory().getType( ParseException.class ) ); + } + else { + return super.getFromConversionExceptionTypes( conversionContext ); + } + } + +} 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 68fb04811..df24c5b3d 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,39 +18,76 @@ */ package org.mapstruct.ap.internal.conversion; -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.Arrays; +import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.HelperMethod; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * Conversion between {@link BigDecimal} and {@link String}. * * @author Gunnar Morling */ -public class BigDecimalToStringConversion extends PossibleNumberToStringConversion { +public class BigDecimalToStringConversion extends AbstractNumberToStringConversion { public BigDecimalToStringConversion() { - super( BigDecimal.class ); + super( true ); } @Override - protected String getFallbackToExpression(ConversionContext conversionContext) { - return ".toString()"; + public String getToExpression(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".format( )" ); + return sb.toString(); + } + else { + return ".toString()"; + } } @Override - protected String getFallbackFromExpression(ConversionContext conversionContext) { - return "new BigDecimal( )"; + public String getFromExpression(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + sb.append( "(BigDecimal) " ); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".parse( )" ); + return sb.toString(); + } + else { + return "new BigDecimal( )"; + } } + @Override protected Set getFromConversionImportTypes(ConversionContext conversionContext) { - Set importTypes = new HashSet(); - importTypes.addAll( super.getFromConversionImportTypes( conversionContext ) ); - importTypes.add( conversionContext.getTypeFactory().getType( BigDecimal.class ) ); - return importTypes; + return asSet( conversionContext.getTypeFactory().getType( BigDecimal.class ) ); } + + @Override + public List getRequiredHelperMethods(ConversionContext conversionContext) { + HelperMethod helperMethod = new CreateDecimalFormat( conversionContext.getTypeFactory() ); + return Arrays.asList( helperMethod ); + } + + private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversionContext) { + sb.append( "createDecimalFormat( " ); + if ( conversionContext.getNumberFormat() != null ) { + sb.append( "\"" ); + sb.append( conversionContext.getNumberFormat() ); + sb.append( "\"" ); + } + + sb.append( " )" ); + } + } 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 4cdb6a47e..72f670cf3 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,39 +18,81 @@ */ package org.mapstruct.ap.internal.conversion; -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.Arrays; +import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.HelperMethod; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * Conversion between {@link BigInteger} and {@link String}. * * @author Gunnar Morling */ -public class BigIntegerToStringConversion extends PossibleNumberToStringConversion { +public class BigIntegerToStringConversion extends AbstractNumberToStringConversion { public BigIntegerToStringConversion() { - super( BigInteger.class ); + super( true ); } @Override - protected String getFallbackToExpression(ConversionContext conversionContext) { - return ".toString()"; + public String getToExpression(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".format( )" ); + return sb.toString(); + } + else { + return ".toString()"; + } } @Override - protected String getFallbackFromExpression(ConversionContext conversionContext) { - return "new BigInteger( )"; + public String getFromExpression(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + sb.append( "( (BigDecimal) " ); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".parse( )" ); + sb.append( " ).toBigInteger()" ); + return sb.toString(); + } + else { + return "new BigInteger( )"; + } } @Override protected Set getFromConversionImportTypes(ConversionContext conversionContext) { - Set importTypes = new HashSet(); - importTypes.addAll( super.getFromConversionImportTypes( conversionContext ) ); - importTypes.add( conversionContext.getTypeFactory().getType( BigInteger.class ) ); - return importTypes; + if ( requiresDecimalFormat( conversionContext ) ) { + // no imports are required when decimal format is used. + return super.getFromConversionImportTypes( conversionContext ); + } + else { + return asSet( conversionContext.getTypeFactory().getType( BigInteger.class ) ); + } + } + + @Override + public List getRequiredHelperMethods(ConversionContext conversionContext) { + HelperMethod helperMethod = new CreateDecimalFormat( conversionContext.getTypeFactory() ); + return Arrays.asList( helperMethod ); + } + + private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversionContext) { + sb.append( "createDecimalFormat( " ); + if ( conversionContext.getNumberFormat() != null ) { + sb.append( "\"" ); + sb.append( conversionContext.getNumberFormat() ); + sb.append( "\"" ); + } + + sb.append( " )" ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java index 1e77c6edd..b07a747d6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java @@ -18,9 +18,11 @@ */ package org.mapstruct.ap.internal.conversion; +import java.util.List; import org.mapstruct.ap.internal.model.TypeConversion; import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.HelperMethod; /** * Implementations create inline {@link TypeConversion}s such as @@ -52,4 +54,11 @@ public interface ConversionProvider { * @return A conversion from target to source. */ Assignment from(ConversionContext conversionContext); + + /** + * @param conversionContext ConversionContext providing optional information required for creating the conversion. + * + * @return any helper methods when required. + */ + List getRequiredHelperMethods(ConversionContext conversionContext); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 4f5a8ef18..3ece35a3c 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -178,8 +178,8 @@ public class Conversions { registerToStringConversion( Boolean.class ); register( char.class, String.class, new CharToStringConversion() ); register( Character.class, String.class, new CharWrapperToStringConversion() ); - register( BigInteger.class, String.class, new BigIntegerToStringConversion() ); - register( BigDecimal.class, String.class, new BigDecimalToStringConversion() ); + register( BigInteger.class, String.class, new BigIntegerToStringConversion( ) ); + register( BigDecimal.class, String.class, new BigDecimalToStringConversion( ) ); registerJodaConversions(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java new file mode 100644 index 000000000..2d7dfd42b --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java @@ -0,0 +1,65 @@ +/** + * Copyright 2012-2016 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 java.text.DecimalFormat; +import java.util.Set; +import org.mapstruct.ap.internal.model.HelperMethod; +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.common.TypeFactory; +import static org.mapstruct.ap.internal.util.Collections.asSet; + +/** + * HelperMethod that creates a {@link java.text.DecimalFormat} + * + * {@code DecimalFormat df = new DecimalFormat( numberFormat )} + * with setParseBigDecimal set to true. + * + * @author Sjaak Derksen + */ +public class CreateDecimalFormat extends HelperMethod { + + + private final Parameter parameter; + private final Type returnType; + private final Set importTypes; + + public CreateDecimalFormat(TypeFactory typeFactory) { + this.parameter = new Parameter( "numberFormat", typeFactory.getType( String.class ) ); + this.returnType = typeFactory.getType( DecimalFormat.class ); + this.importTypes = asSet( parameter.getType(), returnType ); + } + + @Override + public Set getImportTypes() { + return importTypes; + } + + @Override + public Parameter getParameter() { + return parameter; + } + + @Override + public Type getReturnType() { + return returnType; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java index 8b0a2496a..11f1ea193 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java @@ -25,10 +25,12 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; +import java.util.List; import org.mapstruct.ap.internal.model.AssignmentFactory; import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.common.Type; /** @@ -56,6 +58,11 @@ public class DateToStringConversion implements ConversionProvider { ); } + @Override + public List getRequiredHelperMethods(ConversionContext conversionContext) { + return Collections.emptyList(); + } + private String getConversionExpression(ConversionContext conversionContext, String method) { StringBuilder conversionString = new StringBuilder( "new SimpleDateFormat(" ); 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 deleted file mode 100644 index b5c601321..000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/PossibleNumberToStringConversion.java +++ /dev/null @@ -1,168 +0,0 @@ -/** - * 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 68a78b86b..9e814dbbc 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,6 +19,8 @@ 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 @@ -26,23 +28,58 @@ import org.mapstruct.ap.internal.model.common.ConversionContext; * * @author Gunnar Morling */ -public class PrimitiveToStringConversion extends PossibleNumberToStringConversion { +public class PrimitiveToStringConversion extends AbstractNumberToStringConversion { + + private final Class sourceType; + private final Class wrapperType; public PrimitiveToStringConversion(Class sourceType) { - super( sourceType ); + super( NativeTypes.isNumber( 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) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".format( )" ); + return sb.toString(); + } + else { + return "String.valueOf( )"; + } } @Override - protected String getFallbackToExpression(ConversionContext conversionContext) { - return "String.valueOf( )"; + public String getFromExpression(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".parse( )." ); + sb.append( sourceType.getSimpleName() ); + sb.append( "Value()" ); + return sb.toString(); + } + else { + return wrapperType.getSimpleName() + ".parse" + + Strings.capitalize( sourceType.getSimpleName() ) + "( )"; + } } - @Override - protected String getFallbackFromExpression(ConversionContext conversionContext) { - return getParseTypeExpression( "" ); - } + 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( " )" ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java index f0bdf78e5..4c8a3323f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java @@ -18,8 +18,11 @@ */ package org.mapstruct.ap.internal.conversion; +import java.util.Collections; +import java.util.List; import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.HelperMethod; /** * A {@link ConversionProvider} which creates the reversed conversions for a @@ -48,4 +51,10 @@ public class ReverseConversion implements ConversionProvider { public Assignment from(ConversionContext conversionContext) { return conversionProvider.to( conversionContext ); } + + @Override + public List getRequiredHelperMethods(ConversionContext conversionContext) { + return Collections.emptyList(); + } + } 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 483c3f4b8..8539b6d68 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 @@ -26,6 +26,7 @@ import org.mapstruct.ap.internal.model.AssignmentFactory; import org.mapstruct.ap.internal.model.TypeConversion; import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.common.Type; /** @@ -55,6 +56,12 @@ public abstract class SimpleConversion implements ConversionProvider { ); } + @Override + public List getRequiredHelperMethods(ConversionContext conversionContext) { + return Collections.emptyList(); + } + + /** * Returns the conversion string from source to target. The placeholder {@code } can be used to represent a * reference to the source value. 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 c34b42788..72c4d9c73 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,6 +19,7 @@ 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; /** @@ -26,24 +27,58 @@ import org.mapstruct.ap.internal.util.Strings; * * @author Gunnar Morling */ -public class WrapperToStringConversion extends PossibleNumberToStringConversion { +public class WrapperToStringConversion extends AbstractNumberToStringConversion { private final Class sourceType; + private final Class primitiveType; public WrapperToStringConversion(Class sourceType) { - super( sourceType ); + super( NativeTypes.isNumber( sourceType ) ); + if ( sourceType.isPrimitive() ) { + throw new IllegalArgumentException( sourceType + " is no wrapper type." ); + } + this.sourceType = sourceType; + this.primitiveType = NativeTypes.getPrimitiveType( sourceType ); } @Override - protected String getFallbackToExpression(ConversionContext conversionContext) { - return "String.valueOf( )"; + public String getToExpression(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".format( )" ); + return sb.toString(); + } + else { + return "String.valueOf( )"; + } } @Override - protected String getFallbackFromExpression(ConversionContext conversionContext) { - return sourceType.getSimpleName() + ".parse" + - Strings.capitalize( primitiveType.getSimpleName() ) + "( )"; + public String getFromExpression(ConversionContext conversionContext) { + if ( requiresDecimalFormat( conversionContext ) ) { + StringBuilder sb = new StringBuilder(); + appendDecimalFormatter( sb, conversionContext ); + sb.append( ".parse( )." ); + sb.append( primitiveType.getSimpleName() ); + sb.append( "Value()" ); + return sb.toString(); + } + else { + return sourceType.getSimpleName() + ".parse" + + Strings.capitalize( primitiveType.getSimpleName() ) + "( )"; + } } + 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( " )" ); + } } 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 8b88b2285..eb0bd7878 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 @@ -293,8 +293,7 @@ public class BeanMappingMethod extends MappingMethod { .targetPropertyName( mapping.getTargetName() ) .sourceReference( sourceRef ) .selectionParameters( mapping.getSelectionParameters() ) - .dateFormat( mapping.getDateFormat() ) - .numberFormat( mapping.getNumberFormat() ) + .formattingParameters( mapping.getFormattingParameters() ) .existingVariableNames( existingVariableNames ) .dependsOn( mapping.getDependsOn() ) .defaultValue( mapping.getDefaultValue() ) @@ -318,7 +317,7 @@ public class BeanMappingMethod extends MappingMethod { .targetWriteAccessor( targetWriteAccessor ) .targetReadAccessor( getTargetPropertyReadAccessor( mapping.getTargetName() ) ) .targetPropertyName( mapping.getTargetName() ) - .dateFormat( mapping.getDateFormat() ) + .formattingParameters( mapping.getFormattingParameters() ) .selectionParameters( mapping.getSelectionParameters() ) .existingVariableNames( existingVariableNames ) .dependsOn( mapping.getDependsOn() ) @@ -415,9 +414,8 @@ public class BeanMappingMethod extends MappingMethod { .targetReadAccessor( getTargetPropertyReadAccessor( targetProperty.getKey() ) ) .targetPropertyName( targetProperty.getKey() ) .sourceReference( sourceRef ) + .formattingParameters( mapping != null ? mapping.getFormattingParameters() : null ) .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,9 +478,8 @@ public class BeanMappingMethod extends MappingMethod { .targetReadAccessor( getTargetPropertyReadAccessor( targetProperty.getKey() ) ) .targetPropertyName( targetProperty.getKey() ) .sourceReference( sourceRef ) + .formattingParameters( mapping != null ? mapping.getFormattingParameters() : null ) .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/HelperMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java new file mode 100644 index 000000000..a2693bf16 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java @@ -0,0 +1,247 @@ +/** + * Copyright 2012-2016 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.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import javax.lang.model.element.ExecutableElement; +import org.mapstruct.ap.internal.model.common.Accessibility; +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.Strings; + +/** + * A non mapping method to be generated. + * + * Can be called from for instance conversions or built-in methods as shared helper method. + * + * One example of such method is the creation of a decimal formatter + * {@link org.mapstruct.ap.internal.conversion.CreateDecimalFormat}, which is used in 2 conversions + * (BigInteger to String and BigDecimal to String) + * + * @author Sjaak Derksen + */ +public abstract class HelperMethod implements Method { + + + /** + * {@inheritDoc } + * + * @return default method name is equal to class name of build in method name + */ + @Override + public String getName() { + return Strings.decapitalize( this.getClass().getSimpleName() ); + } + + /** + * Returns the types used by this method for which import statements need to be generated. Defaults to the empty + * set. To be overridden by implementations in case they make use of additional types (note that the parameter and + * return type don't need to be added). + * + * @return the types used by this method for which import statements need to be generated + */ + public Set getImportTypes() { + return Collections.emptySet(); + } + + /** + * {@inheritDoc} + *

+ * Default the targetType should be assignable to the returnType and the sourceType to the parameter, + * excluding generic type variables. When the implementor sees a need for this, this method can be overridden. + */ + @Override + public boolean matches(List sourceTypes, Type targetType) { + throw new IllegalStateException( "Irrelevant." ); + + } + + @Override + public List getSourceParameters() { + return getParameters(); + } + + /** + * {@inheritDoc} + *

+ * For built-in methods, the declaring mapper is always {@code null} as they will be added as private methods to the + * generated mapper. + * + * @return {@code null} + */ + @Override + public final Type getDeclaringMapper() { + return null; + } + + @Override + public List getParameters() { + return Arrays.asList( getParameter() ); + } + + /** + * mapping target parameter mechanism not supported for built-in methods + * + * @return {@code null} + */ + @Override + public Parameter getMappingTargetParameter() { + return null; + } + + /** + * target type parameter mechanism not supported for built-in methods + * + * @return {@code null} + */ + @Override + public Parameter getTargetTypeParameter() { + return null; + } + + /** + * the conversion context is used to format an auxiliary parameter in the method call with context specific + * information such as a date format. + * + * @param conversionContext context + * @return null if no context parameter should be included "null" if there should be an explicit null call + * "'dateFormat'" for instance, to indicate how the build-in method should format the date + */ + public String getContextParameter(ConversionContext conversionContext) { + return null; + } + + /** + * hashCode based on class + * + * @return hashCode + */ + @Override + public int hashCode() { + return this.getClass().hashCode(); + } + + /** + * equals based on class + * + * @param obj other class + * + * @return true when classes are the same + */ + @Override + public boolean equals(Object obj) { + if ( obj == null ) { + return false; + } + return ( getClass() == obj.getClass() ); + } + + /** + * Analyzes the Java Generic type variables in the parameter do match the type variables in the build in method same + * goes for the returnType. + * + * @param parameter source + * @param returnType target + * @return {@code true}, iff the the type variables match + */ + public boolean doTypeVarsMatch(Type parameter, Type returnType) { + return true; + } + + + + /** + * There's currently only one parameter foreseen instead of a list of parameter + * + * @return the parameter + */ + public abstract Parameter getParameter(); + + @Override + public Accessibility getAccessibility() { + return Accessibility.PRIVATE; + } + + @Override + public List getThrownTypes() { + return Collections.emptyList(); + } + + @Override + public Type getResultType() { + return getReturnType(); + } + + @Override + public List getParameterNames() { + List parameterNames = new ArrayList( getParameters().size() ); + for ( Parameter parameter : getParameters() ) { + parameterNames.add( parameter.getName() ); + } + + return parameterNames; + } + + @Override + public boolean overridesMethod() { + return false; + } + + @Override + public ExecutableElement getExecutable() { + return null; + } + + @Override + public boolean isStatic() { + return false; + } + + @Override + public boolean isDefault() { + return false; + } + + @Override + public Type getDefiningType() { + return null; + } + + @Override + public MapperConfiguration getMapperConfiguration() { + return null; + } + + @Override + public boolean isLifecycleCallbackMethod() { + return false; + } + + @Override + public boolean isUpdateMethod() { + return false; // irrelevant + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java index 57725e084..50443908f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java @@ -31,6 +31,7 @@ import org.mapstruct.ap.internal.model.assignment.SetterWrapper; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; +import org.mapstruct.ap.internal.model.source.FormattingParameters; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; @@ -55,8 +56,8 @@ public class IterableMappingMethod extends MappingMethod { private Method method; private MappingBuilderContext ctx; - private String dateFormat; private SelectionParameters selectionParameters; + private FormattingParameters formattingParameters; private NullValueMappingStrategyPrism nullValueMappingStrategy; public Builder mappingContext(MappingBuilderContext mappingContext) { @@ -69,8 +70,8 @@ public class IterableMappingMethod extends MappingMethod { return this; } - public Builder dateFormat(String dateFormat) { - this.dateFormat = dateFormat; + public Builder formattingParameters(FormattingParameters formattingParameters) { + this.formattingParameters = formattingParameters; return this; } @@ -104,7 +105,7 @@ public class IterableMappingMethod extends MappingMethod { sourceElementType, targetElementType, null, // there is no targetPropertyName - dateFormat, + formattingParameters, selectionParameters, loopVariableName, false 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 a6c07cced..515f18eab 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 @@ -28,6 +28,7 @@ import org.mapstruct.ap.internal.model.assignment.LocalVarWrapper; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; +import org.mapstruct.ap.internal.model.source.FormattingParameters; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; @@ -50,9 +51,8 @@ public class MapMappingMethod extends MappingMethod { public static class Builder { - private String keyDateFormat; - private String keyNumberFormat; - private String valueDateFormat; + private FormattingParameters keyFormattingParameters; + private FormattingParameters valueFormattingParameters; private Method method; private MappingBuilderContext ctx; private NullValueMappingStrategyPrism nullValueMappingStrategy; @@ -79,13 +79,13 @@ public class MapMappingMethod extends MappingMethod { return this; } - public Builder keyDateFormat(String keyDateFormat) { - this.keyDateFormat = keyDateFormat; + public Builder keyFormattingParameters(FormattingParameters keyFormattingParameters) { + this.keyFormattingParameters = keyFormattingParameters; return this; } - public Builder valueDateFormat(String valueDateFormat) { - this.valueDateFormat = valueDateFormat; + public Builder valueFormattingParameters(FormattingParameters valueFormattingParameters) { + this.valueFormattingParameters = valueFormattingParameters; return this; } @@ -94,7 +94,6 @@ public class MapMappingMethod extends MappingMethod { return this; } - public MapMappingMethod build() { List sourceTypeParams = first( method.getSourceParameters() ).getType().getTypeParameters(); @@ -110,7 +109,7 @@ public class MapMappingMethod extends MappingMethod { keySourceType, keyTargetType, null, // there is no targetPropertyName - keyDateFormat, + keyFormattingParameters, keySelectionParameters, "entry.getKey()", false @@ -137,7 +136,7 @@ public class MapMappingMethod extends MappingMethod { valueSourceType, valueTargetType, null, // there is no targetPropertyName - valueDateFormat, + valueFormattingParameters, valueSelectionParameters, "entry.getValue()", false 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 8300eb7e4..0a6c68bad 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 @@ -29,6 +29,7 @@ import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.model.source.FormattingParameters; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; @@ -78,7 +79,7 @@ public class MappingBuilderContext { * @param sourceType parameter to match * @param targetType return type to match * @param targetPropertyName name of the target property - * @param dateFormat used for formatting dates in build in methods that need context information + * @param formattingParameters used for formatting dates and numbers * @param selectionParameters parameters used in the selection process * @param sourceReference call to source type as string * @param preferUpdateMethods selection should prefer update methods when present. @@ -93,7 +94,7 @@ public class MappingBuilderContext { */ @SuppressWarnings("checkstyle:parameternumber") Assignment getTargetAssignment(Method mappingMethod, String mappedElement, Type sourceType, Type targetType, - String targetPropertyName, String dateFormat, + String targetPropertyName, FormattingParameters formattingParameters, SelectionParameters selectionParameters, String sourceReference, boolean preferUpdateMethods); 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 432210256..e0bc63626 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 @@ -47,6 +47,7 @@ import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; +import org.mapstruct.ap.internal.model.source.FormattingParameters; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.SourceReference; @@ -166,10 +167,9 @@ public class PropertyMapping extends ModelElement { public static class PropertyMappingBuilder extends MappingBuilderBase { // initial properties - private String dateFormat; - private String numberFormat; private String defaultValue; private SourceReference sourceReference; + private FormattingParameters formattingParameters; private SelectionParameters selectionParameters; public PropertyMappingBuilder sourceReference(SourceReference sourceReference) { @@ -182,13 +182,8 @@ public class PropertyMapping extends ModelElement { return this; } - public PropertyMappingBuilder dateFormat(String dateFormat) { - this.dateFormat = dateFormat; - return this; - } - - public PropertyMappingBuilder numberFormat(String numberFormat) { - this.numberFormat = numberFormat; + public PropertyMappingBuilder formattingParameters(FormattingParameters formattingParameters) { + this.formattingParameters = formattingParameters; return this; } @@ -227,7 +222,7 @@ public class PropertyMapping extends ModelElement { sourceType, targetType, targetPropertyName, - dateFormat, + formattingParameters, selectionParameters, sourceRefStr, preferUpdateMethods @@ -286,7 +281,7 @@ public class PropertyMapping extends ModelElement { if ( defaultValue != null && !getSourceType().isPrimitive() ) { PropertyMapping build = new ConstantMappingBuilder() .constantExpression( '"' + defaultValue + '"' ) - .dateFormat( dateFormat ) + .formattingParameters( formattingParameters ) .selectionParameters( selectionParameters ) .dependsOn( dependsOn ) .existingVariableNames( existingVariableNames ) @@ -599,7 +594,7 @@ public class PropertyMapping extends ModelElement { public static class ConstantMappingBuilder extends MappingBuilderBase { private String constantExpression; - private String dateFormat; + private FormattingParameters formattingParameters; private SelectionParameters selectionParameters; public ConstantMappingBuilder constantExpression(String constantExpression) { @@ -607,8 +602,8 @@ public class PropertyMapping extends ModelElement { return this; } - public ConstantMappingBuilder dateFormat(String dateFormat) { - this.dateFormat = dateFormat; + public ConstantMappingBuilder formattingParameters(FormattingParameters formattingParameters) { + this.formattingParameters = formattingParameters; return this; } @@ -628,7 +623,7 @@ public class PropertyMapping extends ModelElement { sourceType, targetType, targetPropertyName, - dateFormat, + formattingParameters, selectionParameters, constantExpression, method.getMappingTargetParameter() != null diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/VirtualMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/VirtualMappingMethod.java index 76e02ed3d..bcbe52dd8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/VirtualMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/VirtualMappingMethod.java @@ -40,6 +40,12 @@ public class VirtualMappingMethod extends MappingMethod { this.templateName = getTemplateNameForClass( method.getClass() ); } + public VirtualMappingMethod(HelperMethod method) { + super( method ); + this.importTypes = method.getImportTypes(); + this.templateName = getTemplateNameForClass( method.getClass() ); + } + @Override public String getTemplateName() { return templateName; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/FormattingParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/FormattingParameters.java new file mode 100644 index 000000000..229b95bbe --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/FormattingParameters.java @@ -0,0 +1,43 @@ +/** + * Copyright 2012-2016 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.model.source; + +/** + * + * @author Sjaak Derksen + */ +public class FormattingParameters { + + private final String date; + private final String number; + + public FormattingParameters(String date, String number) { + this.date = date; + this.number = number; + } + + public String getDate() { + return date; + } + + public String getNumber() { + return number; + } + +} 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 71b37195e..46c435dd7 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 @@ -19,7 +19,6 @@ package org.mapstruct.ap.internal.model.source; import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.TypeKind; @@ -35,11 +34,9 @@ import org.mapstruct.ap.internal.util.Message; */ public class IterableMapping { - private final String dateFormat; private final SelectionParameters selectionParameters; + private final FormattingParameters formattingParameters; private final AnnotationMirror mirror; - private final AnnotationValue dateFormatAnnotationValue; - private final AnnotationValue numberFormatAnnotationValue; private final NullValueMappingStrategyPrism nullValueMappingStrategy; public static IterableMapping fromPrism(IterableMappingPrism iterableMapping, ExecutableElement method, @@ -57,6 +54,7 @@ public class IterableMapping { if ( !elementTargetTypeIsDefined && iterableMapping.dateFormat().isEmpty() + && iterableMapping.numberFormat().isEmpty() && iterableMapping.qualifiedBy().isEmpty() && iterableMapping.qualifiedByName().isEmpty() && ( nullValueMappingStrategy == null ) ) { @@ -69,50 +67,39 @@ public class IterableMapping { iterableMapping.qualifiedByName(), elementTargetTypeIsDefined ? iterableMapping.elementTargetType() : null ); - return new IterableMapping(iterableMapping.dateFormat(), + FormattingParameters formatting = new FormattingParameters( + iterableMapping.dateFormat(), + iterableMapping.numberFormat() ); + + return new IterableMapping( formatting, selection, iterableMapping.mirror, - iterableMapping.values.dateFormat(), - iterableMapping.values.numberFormat(), nullValueMappingStrategy ); } - private IterableMapping(String dateFormat, SelectionParameters selectionParameters, AnnotationMirror mirror, - AnnotationValue dateFormatAnnotationValue, NullValueMappingStrategyPrism nvms) { + private IterableMapping(FormattingParameters formattingParameters, SelectionParameters selectionParameters, + AnnotationMirror mirror, NullValueMappingStrategyPrism nvms) { - this.dateFormat = dateFormat; + this.formattingParameters = formattingParameters; this.selectionParameters = selectionParameters; this.mirror = mirror; - this.dateFormatAnnotationValue = dateFormatAnnotationValue; - this.numberFormatAnnotationValue = numberFormatAnnotationValue; this.nullValueMappingStrategy = nvms; } - public String getDateFormat() { - return dateFormat; - } public SelectionParameters getSelectionParameters() { return selectionParameters; } + public FormattingParameters getFormattingParameters() { + return formattingParameters; + } + public AnnotationMirror getMirror() { return mirror; } - public AnnotationValue getDateFormatAnnotationValue() { - 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/MapMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMapping.java index 001df5edc..89e4a40c9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMapping.java @@ -34,10 +34,10 @@ import org.mapstruct.ap.internal.util.Message; */ public class MapMapping { - private final String keyFormat; private final SelectionParameters keySelectionParameters; - private final String valueFormat; private final SelectionParameters valueSelectionParameters; + private final FormattingParameters keyFormattingParameters; + private final FormattingParameters valueFormattingParameters; private final AnnotationMirror mirror; private final NullValueMappingStrategyPrism nullValueMappingStrategy; @@ -56,9 +56,11 @@ public class MapMapping { boolean keyTargetTypeIsDefined = !TypeKind.VOID.equals( mapMapping.keyTargetType().getKind() ); boolean valueTargetTypeIsDefined = !TypeKind.VOID.equals( mapMapping.valueTargetType().getKind() ); if ( mapMapping.keyDateFormat().isEmpty() + && mapMapping.keyNumberFormat().isEmpty() && mapMapping.keyQualifiedBy().isEmpty() && mapMapping.keyQualifiedByName().isEmpty() && mapMapping.valueDateFormat().isEmpty() + && mapMapping.valueNumberFormat().isEmpty() && mapMapping.valueQualifiedBy().isEmpty() && mapMapping.valueQualifiedByName().isEmpty() && !keyTargetTypeIsDefined @@ -78,36 +80,45 @@ public class MapMapping { mapMapping.valueQualifiedByName(), valueTargetTypeIsDefined ? mapMapping.valueTargetType() : null); - return new MapMapping( + FormattingParameters keyFormatting = new FormattingParameters( mapMapping.keyDateFormat(), - keySelection, + mapMapping.keyNumberFormat() ); + + FormattingParameters valueFormatting = new FormattingParameters( mapMapping.valueDateFormat(), + mapMapping.valueNumberFormat() ); + + return new MapMapping( + keyFormatting, + keySelection, + valueFormatting, valueSelection, mapMapping.mirror, nullValueMappingStrategy ); } - private MapMapping(String keyFormat, SelectionParameters keySelectionParameters, String valueFormat, - SelectionParameters valueSelectionParameters, AnnotationMirror mirror, NullValueMappingStrategyPrism nvms ) { - this.keyFormat = keyFormat; + private MapMapping(FormattingParameters keyFormatting, SelectionParameters keySelectionParameters, + FormattingParameters valueFormatting, SelectionParameters valueSelectionParameters, AnnotationMirror mirror, + NullValueMappingStrategyPrism nvms ) { + this.keyFormattingParameters = keyFormatting; this.keySelectionParameters = keySelectionParameters; - this.valueFormat = valueFormat; + this.valueFormattingParameters = valueFormatting; this.valueSelectionParameters = valueSelectionParameters; this.mirror = mirror; this.nullValueMappingStrategy = nvms; } - public String getKeyFormat() { - return keyFormat; + public FormattingParameters getKeyFormattingParameters() { + return keyFormattingParameters; } public SelectionParameters getKeySelectionParameters() { return keySelectionParameters; } - public String getValueFormat() { - return valueFormat; + public FormattingParameters getValueFormattingParameters() { + return valueFormattingParameters; } public SelectionParameters getValueSelectionParameters() { 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 125ee5fb5..6882ccd40 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 @@ -54,9 +54,8 @@ public class Mapping { private final String constant; private final String javaExpression; private final String targetName; - private final String dateFormat; - private final String numberFormat; private final String defaultValue; + private final FormattingParameters formattingParameters; private final SelectionParameters selectionParameters; private final boolean isIgnored; @@ -145,18 +144,19 @@ public class Mapping { mappingPrism.qualifiedByName(), resultTypeIsDefined ? mappingPrism.resultType() : null); + FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat ); + return new Mapping( source, constant, expression, mappingPrism.target(), - dateFormat, - numberFormat, defaultValue, mappingPrism.ignore(), mappingPrism.mirror, mappingPrism.values.source(), mappingPrism.values.target(), + formattingParam, selectionParams, mappingPrism.values.dependsOn(), dependsOn @@ -165,21 +165,20 @@ public class Mapping { @SuppressWarnings("checkstyle:parameternumber") private Mapping(String sourceName, String constant, String javaExpression, String targetName, - String dateFormat, String defaultValue, boolean isIgnored, AnnotationMirror mirror, - AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, - SelectionParameters selectionParameters, AnnotationValue dependsOnAnnotationValue, - List dependsOn) { + String defaultValue, boolean isIgnored, AnnotationMirror mirror, + AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, + FormattingParameters formattingParameters, SelectionParameters selectionParameters, + AnnotationValue dependsOnAnnotationValue, List dependsOn) { this.sourceName = sourceName; this.constant = constant; this.javaExpression = javaExpression; this.targetName = targetName; - this.dateFormat = dateFormat; - this.numberFormat = numberFormat; this.defaultValue = defaultValue; this.isIgnored = isIgnored; this.mirror = mirror; this.sourceAnnotationValue = sourceAnnotationValue; this.targetAnnotationValue = targetAnnotationValue; + this.formattingParameters = formattingParameters; this.selectionParameters = selectionParameters; this.dependsOnAnnotationValue = dependsOnAnnotationValue; this.dependsOn = dependsOn; @@ -243,18 +242,14 @@ public class Mapping { return targetName; } - public String getDateFormat() { - return dateFormat; - } - - public String getNumberFormat() { - return numberFormat; - } - public String getDefaultValue() { return defaultValue; } + public FormattingParameters getFormattingParameters() { + return formattingParameters; + } + public SelectionParameters getSelectionParameters() { return selectionParameters; } @@ -327,13 +322,12 @@ public class Mapping { null, // constant null, // expression sourceName != null ? sourceName : targetName, - dateFormat, - numberFormat, null, isIgnored, mirror, sourceAnnotationValue, targetAnnotationValue, + formattingParameters, selectionParameters, dependsOnAnnotationValue, Collections.emptyList() @@ -355,13 +349,12 @@ public class Mapping { constant, javaExpression, targetName, - dateFormat, - numberFormat, defaultValue, isIgnored, mirror, sourceAnnotationValue, targetAnnotationValue, + formattingParameters, selectionParameters, dependsOnAnnotationValue, dependsOn diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 483ff3f9f..bde48781d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -50,6 +50,7 @@ import org.mapstruct.ap.internal.model.MappingMethod; import org.mapstruct.ap.internal.model.ValueMappingMethod; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.model.source.FormattingParameters; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; @@ -269,12 +270,12 @@ public class MapperCreationProcessor implements ModelElementProcessor +private DecimalFormat ${name}( String numberFormat ) { + + DecimalFormat df = new DecimalFormat( numberFormat ); + df.setParseBigDecimal( true ); + return df; +} \ No newline at end of file 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 index ab7a1f22a..d4cf6e5c1 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2012-2015 Gunnar Morling (http://www.gunnarmorling.de/) + * Copyright 2012-2016 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. 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 index 9d18cb6f1..0047c7c74 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright 2012-2015 Gunnar Morling (http://www.gunnarmorling.de/) + * Copyright 2012-2016 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. @@ -26,9 +26,14 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.Map; import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.MapAssert.entry; @WithClasses({ Source.class, @@ -50,6 +55,8 @@ public class NumberFormatConversionTest { source.setIi( 2 ); source.setD( 3.0 ); source.setDd( 4.0 ); + source.setF( 3.0f ); + source.setFf( 4.0f ); source.setL( 5L ); source.setLl( 6L ); source.setB( (byte) 7 ); @@ -68,6 +75,8 @@ public class NumberFormatConversionTest { assertThat( target.getIi() ).isEqualTo( "2.00" ); assertThat( target.getD() ).isEqualTo( "3.00" ); assertThat( target.getDd() ).isEqualTo( "4.00" ); + assertThat( target.getF() ).isEqualTo( "3.00" ); + assertThat( target.getFf() ).isEqualTo( "4.00" ); assertThat( target.getL() ).isEqualTo( "5.00" ); assertThat( target.getLl() ).isEqualTo( "6.00" ); assertThat( target.getB() ).isEqualTo( "7.00" ); @@ -87,6 +96,8 @@ public class NumberFormatConversionTest { target.setIi( "2.00" ); target.setD( "3.00" ); target.setDd( "4.00" ); + target.setF( "3.00" ); + target.setFf( "4.00" ); target.setL( "5.00" ); target.setLl( "6.00" ); target.setB( "7.00" ); @@ -105,6 +116,8 @@ public class NumberFormatConversionTest { assertThat( source.getIi() ).isEqualTo( Integer.valueOf( 2 ) ); assertThat( source.getD() ).isEqualTo( 3.0 ); assertThat( source.getDd() ).isEqualTo( Double.valueOf( 4.0 ) ); + assertThat( source.getF() ).isEqualTo( 3.0f ); + assertThat( source.getFf() ).isEqualTo( Float.valueOf( 4.0f ) ); assertThat( source.getL() ).isEqualTo( 5L ); assertThat( source.getLl() ).isEqualTo( Long.valueOf( 6L ) ); assertThat( source.getB() ).isEqualTo( (byte) 7 ); @@ -117,4 +130,32 @@ public class NumberFormatConversionTest { assertThat( source.getBigInteger1() ).isEqualTo( new BigInteger( "1234567890000" ) ); } + @Test + public void shouldApplyStringConversionsToIterables() { + + List target = SourceTargetMapper.INSTANCE.sourceToTarget( Arrays.asList( 2f ) ); + + assertThat( target ).hasSize( 1 ); + assertThat( target ).isEqualTo( Arrays.asList( "2.00" ) ); + + List source = SourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source ).hasSize( 1 ); + assertThat( source ).isEqualTo( Arrays.asList( 2.00f ) ); + } + + @Test + public void shouldApplyStringConversionsToMaps() { + + Map source1 = new HashMap(); + source1.put( 1.0001f, 2.01f ); + + Map target = SourceTargetMapper.INSTANCE.sourceToTarget( source1 ); + assertThat( target ).hasSize( 1 ); + assertThat( target ).includes( entry( "1.00", "2" ) ); + + Map source2 = SourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source2 ).hasSize( 1 ); + assertThat( source2 ).includes( entry( 1.00f, 2f ) ); + + } } 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 index 7c1d18e80..4d7c0c9af 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright 2012-2015 Gunnar Morling (http://www.gunnarmorling.de/) + * Copyright 2012-2016 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. @@ -27,6 +27,8 @@ public class Source { private Integer ii; private double d; private Double dd; + private float f; + private Float ff; private long l; private Long ll; private byte b; @@ -70,6 +72,22 @@ public class Source { this.dd = dd; } + public float getF() { + return f; + } + + public void setF(float f) { + this.f = f; + } + + public Float getFf() { + return ff; + } + + public void setFf(Float ff) { + this.ff = ff; + } + public long getL() { return l; } 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 index a4daa644c..5e7ae3f66 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright 2012-2015 Gunnar Morling (http://www.gunnarmorling.de/) + * Copyright 2012-2016 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. @@ -18,7 +18,11 @@ */ package org.mapstruct.ap.test.conversion.numbers; +import java.util.List; +import java.util.Map; import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; @@ -36,6 +40,8 @@ public interface SourceTargetMapper { @Mapping( target = "ii", numberFormat = NUMBER_FORMAT ), @Mapping( target = "d", numberFormat = NUMBER_FORMAT ), @Mapping( target = "dd", numberFormat = NUMBER_FORMAT ), + @Mapping( target = "f", numberFormat = NUMBER_FORMAT ), + @Mapping( target = "ff", numberFormat = NUMBER_FORMAT ), @Mapping( target = "l", numberFormat = NUMBER_FORMAT ), @Mapping( target = "ll", numberFormat = NUMBER_FORMAT ), @Mapping( target = "b", numberFormat = NUMBER_FORMAT ), @@ -50,4 +56,19 @@ public interface SourceTargetMapper { @InheritInverseConfiguration Source targetToSource(Target target); + + @IterableMapping( numberFormat = NUMBER_FORMAT ) + List sourceToTarget(List source); + + @InheritInverseConfiguration + List targetToSource(List source); + + @MapMapping( keyNumberFormat = NUMBER_FORMAT, valueNumberFormat = "##" ) + Map sourceToTarget(Map source); + + @InheritInverseConfiguration + Map targetToSource(Map source); + } + + 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 index 669d83a1c..e87bbda44 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright 2012-2015 Gunnar Morling (http://www.gunnarmorling.de/) + * Copyright 2012-2016 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. @@ -24,6 +24,8 @@ public class Target { private String ii; private String d; private String dd; + private String f; + private String ff; private String l; private String ll; private String b; @@ -67,6 +69,22 @@ public class Target { this.dd = dd; } + public String getF() { + return f; + } + + public void setF(String f) { + this.f = f; + } + + public String getFf() { + return ff; + } + + public void setFf(String ff) { + this.ff = ff; + } + public String getL() { return l; }