#679 Adding testcases, helpermethods for decimalconversion, doc update

This commit is contained in:
sjaakd 2016-05-17 22:02:18 +02:00
parent 607d0fd6f0
commit 3700052cc6
37 changed files with 975 additions and 344 deletions

View File

@ -4,6 +4,7 @@
Andreas Gudian
Christian Schuster
Christophe Labouisse
Ciaran Liedeman
Dilip Krishnan
Ewald Volkert
Gunnar Morling

View File

@ -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 "";

View File

@ -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'

View File

@ -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 "";

View File

@ -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 "";

View File

@ -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<String> prices(List<Integer> 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<T>` and `T`, `List<JAXBElement<T>>` and `List<T>`

View File

@ -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<Type> 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<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) );
}
else {
return super.getFromConversionImportTypes( conversionContext );
}
}
@Override
protected List<Type> getFromConversionExceptionTypes(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
return Collections.singletonList( conversionContext.getTypeFactory().getType( ParseException.class ) );
}
else {
return super.getFromConversionExceptionTypes( conversionContext );
}
}
}

View File

@ -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 "<SOURCE>.toString()";
public String getToExpression(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
StringBuilder sb = new StringBuilder();
appendDecimalFormatter( sb, conversionContext );
sb.append( ".format( <SOURCE> )" );
return sb.toString();
}
else {
return "<SOURCE>.toString()";
}
}
@Override
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return "new BigDecimal( <SOURCE> )";
public String getFromExpression(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
StringBuilder sb = new StringBuilder();
sb.append( "(BigDecimal) " );
appendDecimalFormatter( sb, conversionContext );
sb.append( ".parse( <SOURCE> )" );
return sb.toString();
}
else {
return "new BigDecimal( <SOURCE> )";
}
}
@Override
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
Set<Type> importTypes = new HashSet<Type>();
importTypes.addAll( super.getFromConversionImportTypes( conversionContext ) );
importTypes.add( conversionContext.getTypeFactory().getType( BigDecimal.class ) );
return importTypes;
return asSet( conversionContext.getTypeFactory().getType( BigDecimal.class ) );
}
@Override
public List<HelperMethod> 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( " )" );
}
}

View File

@ -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 "<SOURCE>.toString()";
public String getToExpression(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
StringBuilder sb = new StringBuilder();
appendDecimalFormatter( sb, conversionContext );
sb.append( ".format( <SOURCE> )" );
return sb.toString();
}
else {
return "<SOURCE>.toString()";
}
}
@Override
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return "new BigInteger( <SOURCE> )";
public String getFromExpression(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
StringBuilder sb = new StringBuilder();
sb.append( "( (BigDecimal) " );
appendDecimalFormatter( sb, conversionContext );
sb.append( ".parse( <SOURCE> )" );
sb.append( " ).toBigInteger()" );
return sb.toString();
}
else {
return "new BigInteger( <SOURCE> )";
}
}
@Override
protected Set<Type> getFromConversionImportTypes(ConversionContext conversionContext) {
Set<Type> importTypes = new HashSet<Type>();
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<HelperMethod> 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( " )" );
}
}

View File

@ -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<HelperMethod> getRequiredHelperMethods(ConversionContext conversionContext);
}

View File

@ -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();

View File

@ -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<Type> 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<Type> getImportTypes() {
return importTypes;
}
@Override
public Parameter getParameter() {
return parameter;
}
@Override
public Type getReturnType() {
return returnType;
}
}

View File

@ -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<HelperMethod> getRequiredHelperMethods(ConversionContext conversionContext) {
return Collections.emptyList();
}
private String getConversionExpression(ConversionContext conversionContext, String method) {
StringBuilder conversionString = new StringBuilder( "new SimpleDateFormat(" );

View File

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

View File

@ -19,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( <SOURCE> )" );
return sb.toString();
}
else {
return "String.valueOf( <SOURCE> )";
}
}
@Override
protected String getFallbackToExpression(ConversionContext conversionContext) {
return "String.valueOf( <SOURCE> )";
public String getFromExpression(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
StringBuilder sb = new StringBuilder();
appendDecimalFormatter( sb, conversionContext );
sb.append( ".parse( <SOURCE> )." );
sb.append( sourceType.getSimpleName() );
sb.append( "Value()" );
return sb.toString();
}
else {
return wrapperType.getSimpleName() + ".parse"
+ Strings.capitalize( sourceType.getSimpleName() ) + "( <SOURCE> )";
}
}
@Override
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return getParseTypeExpression( "<SOURCE>" );
}
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( " )" );
}
}

View File

@ -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<HelperMethod> getRequiredHelperMethods(ConversionContext conversionContext) {
return Collections.emptyList();
}
}

View File

@ -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<HelperMethod> getRequiredHelperMethods(ConversionContext conversionContext) {
return Collections.emptyList();
}
/**
* Returns the conversion string from source to target. The placeholder {@code <SOURCE>} can be used to represent a
* reference to the source value.

View File

@ -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( <SOURCE> )";
public String getToExpression(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
StringBuilder sb = new StringBuilder();
appendDecimalFormatter( sb, conversionContext );
sb.append( ".format( <SOURCE> )" );
return sb.toString();
}
else {
return "String.valueOf( <SOURCE> )";
}
}
@Override
protected String getFallbackFromExpression(ConversionContext conversionContext) {
return sourceType.getSimpleName() + ".parse" +
Strings.capitalize( primitiveType.getSimpleName() ) + "( <SOURCE> )";
public String getFromExpression(ConversionContext conversionContext) {
if ( requiresDecimalFormat( conversionContext ) ) {
StringBuilder sb = new StringBuilder();
appendDecimalFormatter( sb, conversionContext );
sb.append( ".parse( <SOURCE> )." );
sb.append( primitiveType.getSimpleName() );
sb.append( "Value()" );
return sb.toString();
}
else {
return sourceType.getSimpleName() + ".parse"
+ Strings.capitalize( primitiveType.getSimpleName() ) + "( <SOURCE> )";
}
}
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( " )" );
}
}

View File

@ -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.<String>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.<String>emptyList() )
.build();

View File

@ -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<Type> getImportTypes() {
return Collections.<Type>emptySet();
}
/**
* {@inheritDoc}
* <p>
* 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<Type> sourceTypes, Type targetType) {
throw new IllegalStateException( "Irrelevant." );
}
@Override
public List<Parameter> getSourceParameters() {
return getParameters();
}
/**
* {@inheritDoc}
* <p>
* 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<Parameter> 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<Type> getThrownTypes() {
return Collections.emptyList();
}
@Override
public Type getResultType() {
return getReturnType();
}
@Override
public List<String> getParameterNames() {
List<String> parameterNames = new ArrayList<String>( 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
}
}

View File

@ -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

View File

@ -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<Type> 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

View File

@ -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);

View File

@ -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<PropertyMappingBuilder> {
// 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<ConstantMappingBuilder> {
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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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() {

View File

@ -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<String> dependsOn) {
String defaultValue, boolean isIgnored, AnnotationMirror mirror,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue,
FormattingParameters formattingParameters, SelectionParameters selectionParameters,
AnnotationValue dependsOnAnnotationValue, List<String> 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.<String>emptyList()
@ -355,13 +349,12 @@ public class Mapping {
constant,
javaExpression,
targetName,
dateFormat,
numberFormat,
defaultValue,
isIgnored,
mirror,
sourceAnnotationValue,
targetAnnotationValue,
formattingParameters,
selectionParameters,
dependsOnAnnotationValue,
dependsOn

View File

@ -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<List<Sourc
IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder();
String dateFormat = null;
FormattingParameters formattingParameters = null;
SelectionParameters selectionParameters = null;
NullValueMappingStrategyPrism nullValueMappingStrategy = null;
if ( mappingOptions.getIterableMapping() != null ) {
dateFormat = mappingOptions.getIterableMapping().getDateFormat();
formattingParameters = mappingOptions.getIterableMapping().getFormattingParameters();
selectionParameters = mappingOptions.getIterableMapping().getSelectionParameters();
nullValueMappingStrategy = mappingOptions.getIterableMapping().getNullValueMappingStrategy();
}
@ -282,7 +283,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
IterableMappingMethod iterableMappingMethod = builder
.mappingContext( mappingContext )
.method( method )
.dateFormat( dateFormat )
.formattingParameters( formattingParameters )
.selectionParameters( selectionParameters )
.nullValueMappingStrategy( nullValueMappingStrategy )
.build();
@ -294,26 +295,26 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
MapMappingMethod.Builder builder = new MapMappingMethod.Builder();
String keyDateFormat = null;
String valueDateFormat = null;
SelectionParameters keySelectionParameters = null;
FormattingParameters keyFormattingParameters = null;
SelectionParameters valueSelectionParameters = null;
FormattingParameters valueFormattingParameters = null;
NullValueMappingStrategyPrism nullValueMappingStrategy = null;
if ( mappingOptions.getMapMapping() != null ) {
keyDateFormat = mappingOptions.getMapMapping().getKeyFormat();
keySelectionParameters = mappingOptions.getMapMapping().getKeySelectionParameters();
keyFormattingParameters = mappingOptions.getMapMapping().getKeyFormattingParameters();
valueSelectionParameters = mappingOptions.getMapMapping().getValueSelectionParameters();
valueDateFormat = mappingOptions.getMapMapping().getValueFormat();
valueFormattingParameters = mappingOptions.getMapMapping().getValueFormattingParameters();
nullValueMappingStrategy = mappingOptions.getMapMapping().getNullValueMappingStrategy();
}
MapMappingMethod mapMappingMethod = builder
.mappingContext( mappingContext )
.method( method )
.keyDateFormat( keyDateFormat )
.keyFormattingParameters( keyFormattingParameters )
.keySelectionParameters( keySelectionParameters )
.valueDateFormat( valueDateFormat )
.valueFormattingParameters( valueFormattingParameters )
.valueSelectionParameters( valueSelectionParameters )
.nullValueMappingStrategy( nullValueMappingStrategy )
.build();

View File

@ -43,8 +43,10 @@ import org.mapstruct.ap.internal.model.VirtualMappingMethod;
import org.mapstruct.ap.internal.model.assignment.Assignment;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.DefaultConversionContext;
import org.mapstruct.ap.internal.model.HelperMethod;
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;
@ -105,12 +107,19 @@ 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) {
Type targetType, String targetPropertyName, FormattingParameters formattingParameters,
SelectionParameters selectionParameters, String sourceReference, boolean preferUpdateMapping) {
SelectionCriteria criteria =
new SelectionCriteria( selectionParameters, targetPropertyName, preferUpdateMapping );
String dateFormat = null;
String numberFormat = null;
if ( formattingParameters != null ) {
dateFormat = formattingParameters.getDate();
numberFormat = formattingParameters.getNumber();
}
ResolvingAttempt attempt = new ResolvingAttempt(
sourceModel,
mappingMethod,
@ -260,9 +269,13 @@ public class MappingResolverImpl implements MappingResolver {
if ( conversionProvider == null ) {
return null;
}
ConversionContext ctx = new DefaultConversionContext( typeFactory, messager, sourceType, targetType,
dateFormat, numberFormat );
ConversionContext ctx =
new DefaultConversionContext( typeFactory, messager, sourceType, targetType, dateFormat, numberFormat);
// add helper methods required in conversion
for ( HelperMethod helperMethod : conversionProvider.getRequiredHelperMethods( ctx ) ) {
usedVirtualMappings.add( new VirtualMappingMethod( helperMethod ) );
}
return conversionProvider.to( ctx );
}

View File

@ -0,0 +1,26 @@
<#--
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.
-->
private DecimalFormat ${name}( String numberFormat ) {
DecimalFormat df = new DecimalFormat( numberFormat );
df.setParseBigDecimal( true );
return df;
}

View File

@ -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.

View File

@ -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<String> target = SourceTargetMapper.INSTANCE.sourceToTarget( Arrays.asList( 2f ) );
assertThat( target ).hasSize( 1 );
assertThat( target ).isEqualTo( Arrays.asList( "2.00" ) );
List<Float> source = SourceTargetMapper.INSTANCE.targetToSource( target );
assertThat( source ).hasSize( 1 );
assertThat( source ).isEqualTo( Arrays.asList( 2.00f ) );
}
@Test
public void shouldApplyStringConversionsToMaps() {
Map<Float, Float> source1 = new HashMap<Float, Float>();
source1.put( 1.0001f, 2.01f );
Map<String, String> target = SourceTargetMapper.INSTANCE.sourceToTarget( source1 );
assertThat( target ).hasSize( 1 );
assertThat( target ).includes( entry( "1.00", "2" ) );
Map<Float, Float> source2 = SourceTargetMapper.INSTANCE.targetToSource( target );
assertThat( source2 ).hasSize( 1 );
assertThat( source2 ).includes( entry( 1.00f, 2f ) );
}
}

View File

@ -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;
}

View File

@ -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<String> sourceToTarget(List<Float> source);
@InheritInverseConfiguration
List<Float> targetToSource(List<String> source);
@MapMapping( keyNumberFormat = NUMBER_FORMAT, valueNumberFormat = "##" )
Map<String, String> sourceToTarget(Map<Float, Float> source);
@InheritInverseConfiguration
Map<Float, Float> targetToSource(Map<String, String> source);
}

View File

@ -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;
}