#26 Adding support for mappings between Date and String, optionally applying a format string

This commit is contained in:
Gunnar Morling 2013-07-07 14:45:47 +02:00
parent c4cec154cd
commit abba4fb716
29 changed files with 722 additions and 122 deletions

View File

@ -18,6 +18,9 @@
*/
package org.mapstruct;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Configures the mapping of one bean attribute.
*
@ -33,9 +36,18 @@ public @interface Mapping {
String source();
/**
* The target name of the configured property as defined by the JavaBeans specification.
* The target name of the configured property as defined by the JavaBeans specification. Defaults to the
* source name if not given.
*
* @return The target name of the configured property.
*/
String target();
String target() default "";
/**
* A format string as processable by {@link SimpleDateFormat} if the attribute is mapped from {@code String} to
* {@link Date} or vice-versa. Will be ignored for all other attribute types.
*
* @return A date format string as processable by {@link SimpleDateFormat}.
*/
String dateFormat() default "";
}

View File

@ -20,14 +20,12 @@ package org.mapstruct.ap.conversion;
import java.math.BigInteger;
import org.mapstruct.ap.model.Type;
/**
* Conversion between {@link BigInteger} and native number types.
*
* @author Gunnar Morling
*/
public class BigIntegerToPrimitiveConversion implements Conversion {
public class BigIntegerToPrimitiveConversion extends SimpleConversion {
private final Class<?> targetType;
@ -40,19 +38,19 @@ public class BigIntegerToPrimitiveConversion implements Conversion {
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
return sourcePropertyAccessor + "." + targetType.getName() + "Value()";
public String getToConversionString(String sourceReference, Context conversionContext) {
return sourceReference + "." + targetType.getName() + "Value()";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
public String getFromConversionString(String targetReference, Context conversionContext) {
StringBuilder conversion = new StringBuilder( "BigInteger.valueOf( " );
if ( targetType == float.class || targetType == double.class ) {
conversion.append( "(long) " );
}
conversion.append( targetPropertyAccessor ).append( " )" );
conversion.append( targetReference ).append( " )" );
return conversion.toString();
}

View File

@ -20,22 +20,20 @@ package org.mapstruct.ap.conversion;
import java.math.BigInteger;
import org.mapstruct.ap.model.Type;
/**
* Conversion between {@link BigInteger} and {@link String}.
*
* @author Gunnar Morling
*/
public class BigIntegerToStringConversion implements Conversion {
public class BigIntegerToStringConversion extends SimpleConversion {
@Override
public String to(String sourcePropertyAccessor, Type type) {
return sourcePropertyAccessor + ".toString()";
public String getToConversionString(String sourceReference, Context conversionContext) {
return sourceReference + ".toString()";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
return "new BigInteger( " + targetPropertyAccessor + " )";
public String getFromConversionString(String targetReference, Context conversionContext) {
return "new BigInteger( " + targetReference + " )";
}
}

View File

@ -20,7 +20,6 @@ package org.mapstruct.ap.conversion;
import java.math.BigInteger;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.NativeTypes;
/**
@ -28,7 +27,7 @@ import org.mapstruct.ap.util.NativeTypes;
*
* @author Gunnar Morling
*/
public class BigIntegerToWrapperConversion implements Conversion {
public class BigIntegerToWrapperConversion extends SimpleConversion {
private final Class<?> targetType;
@ -41,15 +40,15 @@ public class BigIntegerToWrapperConversion implements Conversion {
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
return sourcePropertyAccessor + "." + targetType.getName() + "Value()";
public String getToConversionString(String sourceReference, Context conversionContext) {
return sourceReference + "." + targetType.getName() + "Value()";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
public String getFromConversionString(String targetReference, Context conversionContext) {
StringBuilder conversion = new StringBuilder( "BigInteger.valueOf( " );
conversion.append( targetPropertyAccessor );
conversion.append( targetReference );
if ( targetType == float.class || targetType == double.class ) {
conversion.append( ".longValue()" );

View File

@ -18,17 +18,20 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
public class CharToStringConversion implements Conversion {
/**
* Conversion between {@code char} and {@link String}.
*
* @author Gunnar Morling
*/
public class CharToStringConversion extends SimpleConversion {
@Override
public String to(String sourcePropertyAccessor, Type type) {
return "String.valueOf( " + sourcePropertyAccessor + " )";
public String getToConversionString(String sourceReference, Context conversionContext) {
return "String.valueOf( " + sourceReference + " )";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
return targetPropertyAccessor + ".charAt( 0 )";
public String getFromConversionString(String targetReference, Context conversionContext) {
return targetReference + ".charAt( 0 )";
}
}

View File

@ -18,17 +18,20 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
public class CharWrapperToStringConversion implements Conversion {
/**
* Conversion between {@link Character} and {@link String}.
*
* @author Gunnar Morling
*/
public class CharWrapperToStringConversion extends SimpleConversion {
@Override
public String to(String sourcePropertyAccessor, Type type) {
return sourcePropertyAccessor + ".toString()";
public String getToConversionString(String sourceReference, Context conversionContext) {
return sourceReference + ".toString()";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
return targetPropertyAccessor + ".charAt( 0 )";
public String getFromConversionString(String targetReference, Context conversionContext) {
return targetReference + ".charAt( 0 )";
}
}

View File

@ -0,0 +1,84 @@
/**
* Copyright 2012-2013 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.conversion;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.TypeConversion;
/**
* Implementations create inline {@link TypeConversion}s such as
*
* <ul>
* <li>{@code (long)source},</li>
* <li>{@code Integer.valueOf(source)} or</li>
* <li>{@code new SimpleDateFormat().format( source )}.</li>
* </ul>
*
* @author Gunnar Morling
*/
public interface ConversionProvider {
/**
* Creates the conversion from source to target of a property mapping.
*
* @param sourceReference A reference to the source object, e.g.
* {@code beanName.getFoo()}.
* @param conversionContext Context providing optional information required for creating
* the conversion.
*
* @return A conversion from source to target.
*/
TypeConversion to(String sourceReference, Context conversionContext);
/**
* Creates the conversion from target to source of a property mapping.
*
* @param targetReference A reference to the targetReference object, e.g.
* {@code beanName.getFoo()}.
* @param conversionContext Context providing optional information required for creating
* the conversion.
*
* @return A conversion from target to source.
*/
TypeConversion from(String targetReference, Context conversionContext);
/**
* Context object passed to conversion providers.
*
* @author Gunnar Morling
*/
public interface Context {
/**
* Returns the target type of this conversion.
*
* @return The target type of this conversion.
*/
Type getTargetType();
/**
* Returns the date format if this conversion is from String to
* {@link Date} or vice versa. Returns {@code null} for other types or
* if not given.
*
* @return The date format if this conversion.
*/
String getDateFormat();
}
}

View File

@ -19,6 +19,7 @@
package org.mapstruct.ap.conversion;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.lang.model.type.DeclaredType;
@ -31,14 +32,14 @@ import org.mapstruct.ap.util.TypeUtil;
import static org.mapstruct.ap.conversion.ReverseConversion.reverse;
/**
* Holds built-in {@link Conversion}s such as from {@code int} to {@code String}.
* Holds built-in {@link ConversionProvider}s such as from {@code int} to {@code String}.
*
* @author Gunnar Morling
*/
public class Conversions {
private TypeUtil typeUtil;
private final Map<Key, Conversion> conversions = new HashMap<Conversions.Key, Conversion>();
private final Map<Key, ConversionProvider> conversions = new HashMap<Conversions.Key, ConversionProvider>();
private final DeclaredType enumType;
private final DeclaredType stringType;
@ -164,6 +165,7 @@ public class Conversions {
//misc.
register( Enum.class, String.class, new EnumStringConversion() );
register( Date.class, String.class, new DateToStringConversion() );
}
private void registerNativeTypeConversion(Class<?> sourceType, Class<?> targetType) {
@ -199,12 +201,12 @@ public class Conversions {
}
}
private void register(Class<?> sourceType, Class<?> targetType, Conversion conversion) {
private void register(Class<?> sourceType, Class<?> targetType, ConversionProvider conversion) {
conversions.put( Key.forClasses( sourceType, targetType ), conversion );
conversions.put( Key.forClasses( targetType, sourceType ), reverse( conversion ) );
}
public Conversion getConversion(Type sourceType, Type targetType) {
public ConversionProvider getConversion(Type sourceType, Type targetType) {
if ( sourceType.isEnumType() && targetType.equals( typeUtil.getType( stringType ) ) ) {
sourceType = typeUtil.getType( enumType );
}

View File

@ -0,0 +1,74 @@
/**
* Copyright 2012-2013 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.conversion;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.TypeConversion;
import static org.mapstruct.ap.util.Collections.asSet;
/**
* Conversion between {@link String} and {@link Date}.
*
* @author Gunnar Morling
*/
public class DateToStringConversion implements ConversionProvider {
@Override
public TypeConversion to(String sourceReference, Context conversionContext) {
return new TypeConversion(
asSet( Type.forClass( SimpleDateFormat.class ) ),
Collections.<Type>emptyList(),
getConversionString( sourceReference, conversionContext, "format" )
);
}
@Override
public TypeConversion from(String targetReference, Context conversionContext) {
return new TypeConversion(
asSet( Type.forClass( SimpleDateFormat.class ) ),
Arrays.asList( Type.forClass( ParseException.class ) ),
getConversionString( targetReference, conversionContext, "parse" )
);
}
private String getConversionString(String targetReference, Context conversionContext, String method) {
StringBuilder conversionString = new StringBuilder( "new SimpleDateFormat(" );
if ( conversionContext.getDateFormat() != null ) {
conversionString.append( " \"" );
conversionString.append( conversionContext.getDateFormat() );
conversionString.append( "\" " );
}
conversionString.append( ")." );
conversionString.append( method );
conversionString.append( "( " );
conversionString.append( targetReference );
conversionString.append( " )" );
return conversionString.toString();
}
}

View File

@ -0,0 +1,48 @@
/**
* Copyright 2012-2013 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.conversion;
import org.mapstruct.ap.conversion.ConversionProvider.Context;
import org.mapstruct.ap.model.Type;
/**
* Default implementation of the {@link Context} passed to conversion providers.
*
* @author Gunnar Morling
*/
public class DefaultConversionContext implements ConversionProvider.Context {
private final Type targetType;
private final String format;
public DefaultConversionContext(Type targetType, String format) {
this.targetType = targetType;
this.format = format;
}
@Override
public Type getTargetType() {
return targetType;
}
@Override
public String getDateFormat() {
return format;
}
}

View File

@ -18,17 +18,20 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
public class EnumStringConversion implements Conversion {
/**
* Conversion between {@link String} and {@link Enum} types.
*
* @author Gunnar Morling
*/
public class EnumStringConversion extends SimpleConversion {
@Override
public String to(String sourcePropertyAccessor, Type type) {
return sourcePropertyAccessor + ".toString()";
public String getToConversionString(String sourceReference, Context conversionContext) {
return sourceReference + ".toString()";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
return "Enum.valueOf( " + type.getName() + ".class, " + targetPropertyAccessor + " )";
public String getFromConversionString(String targetReference, Context conversionContext) {
return "Enum.valueOf( " + conversionContext.getTargetType().getName() + ".class, " + targetReference + " )";
}
}

View File

@ -18,9 +18,12 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
public class PrimitiveToPrimitiveConversion implements Conversion {
/**
* Conversion between primitive types such as {@code byte} or {@code long}.
*
* @author Gunnar Morling
*/
public class PrimitiveToPrimitiveConversion extends SimpleConversion {
private final Class<?> sourceType;
@ -33,12 +36,12 @@ public class PrimitiveToPrimitiveConversion implements Conversion {
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
return sourcePropertyAccessor;
public String getToConversionString(String sourceReference, Context conversionContext) {
return sourceReference;
}
@Override
public String from(String targetPropertyAccessor, Type type) {
return "(" + sourceType + ") " + targetPropertyAccessor;
public String getFromConversionString(String targetReference, Context conversionContext) {
return "(" + sourceType + ") " + targetReference;
}
}

View File

@ -18,11 +18,16 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.NativeTypes;
import org.mapstruct.ap.util.Strings;
public class PrimitiveToStringConversion implements Conversion {
/**
* Conversion between primitive types such as {@code byte} or {@code long} and
* {@link String}.
*
* @author Gunnar Morling
*/
public class PrimitiveToStringConversion extends SimpleConversion {
private final Class<?> sourceType;
private final Class<?> wrapperType;
@ -37,13 +42,13 @@ public class PrimitiveToStringConversion implements Conversion {
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
return "String.valueOf( " + sourcePropertyAccessor + " )";
public String getToConversionString(String sourceReference, Context conversionContext) {
return "String.valueOf( " + sourceReference + " )";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
public String getFromConversionString(String targetReference, Context conversionContext) {
return wrapperType.getSimpleName() + ".parse" + Strings.capitalize( sourceType.getSimpleName() ) + "( " +
targetPropertyAccessor + " )";
targetReference + " )";
}
}

View File

@ -18,10 +18,15 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.NativeTypes;
public class PrimitiveToWrapperConversion implements Conversion {
/**
* Conversion between primitive types such as {@code byte} and wrapper types
* such as {@link Integer}.
*
* @author Gunnar Morling
*/
public class PrimitiveToWrapperConversion extends SimpleConversion {
private final Class<?> sourceType;
private final Class<?> targetType;
@ -39,17 +44,17 @@ public class PrimitiveToWrapperConversion implements Conversion {
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
public String getToConversionString(String sourceReference, Context conversionContext) {
if ( sourceType == targetType ) {
return sourcePropertyAccessor;
return sourceReference;
}
else {
return "(" + targetType.getName() + ") " + sourcePropertyAccessor;
return "(" + targetType.getName() + ") " + sourceReference;
}
}
@Override
public String from(String targetPropertyAccessor, Type type) {
return targetPropertyAccessor + "." + sourceType.getName() + "Value()";
public String getFromConversionString(String targetReference, Context conversionContext) {
return targetReference + "." + sourceType.getName() + "Value()";
}
}

View File

@ -18,27 +18,33 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.TypeConversion;
public class ReverseConversion implements Conversion {
/**
* A {@link ConversionProvider} which creates the reversed conversions for a
* given conversion provider.
*
* @author Gunnar Morling
*/
public class ReverseConversion implements ConversionProvider {
private Conversion conversion;
private ConversionProvider conversionProvider;
public static ReverseConversion reverse(Conversion conversion) {
return new ReverseConversion( conversion );
public static ReverseConversion reverse(ConversionProvider conversionProvider) {
return new ReverseConversion( conversionProvider );
}
private ReverseConversion(Conversion conversion) {
this.conversion = conversion;
private ReverseConversion(ConversionProvider conversionProvider) {
this.conversionProvider = conversionProvider;
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
return conversion.from( sourcePropertyAccessor, type );
public TypeConversion to(String sourceReference, Context conversionContext) {
return conversionProvider.from( sourceReference, conversionContext );
}
@Override
public String from(String targetPropertyAccessor, Type type) {
return conversion.to( targetPropertyAccessor, type );
public TypeConversion from(String targetReference, Context conversionContext) {
return conversionProvider.to( targetReference, conversionContext );
}
}

View File

@ -0,0 +1,68 @@
/**
* Copyright 2012-2013 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.conversion;
import org.mapstruct.ap.model.TypeConversion;
/**
* Base class for {@link ConversionProvider}s creating {@link TypeConversion}s
* which don't require any type imports nor declare any exception types.
*
* @author Gunnar Morling
*/
public abstract class SimpleConversion implements ConversionProvider {
@Override
public TypeConversion to(String sourceReference, Context conversionContext) {
return new TypeConversion(
getToConversionString( sourceReference, conversionContext )
);
}
@Override
public TypeConversion from(String targetReference, Context conversionContext) {
return new TypeConversion(
getFromConversionString( targetReference, conversionContext )
);
}
/**
* Returns the conversion string from source to target.
*
* @param sourceReference A reference to the source object, e.g.
* {@code beanName.getFoo()}.
* @param conversionContext Context providing optional information required for creating
* the conversion.
*
* @return The conversion string from source to target.
*/
protected abstract String getToConversionString(String sourceReference, Context conversionContext);
/**
* Creates the conversion string from target to source.
*
* @param targetReference A reference to the targetReference object, e.g.
* {@code beanName.getFoo()}.
* @param conversionContext Context providing optional information required for creating
* the conversion.
*
* @return The conversion string from target to source.
*/
protected abstract String getFromConversionString(String targetReference, Context conversionContext);
}

View File

@ -18,11 +18,15 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.NativeTypes;
import org.mapstruct.ap.util.Strings;
public class WrapperToStringConversion implements Conversion {
/**
* Conversion between wrapper types such as {@link Integer} and {@link String}.
*
* @author Gunnar Morling
*/
public class WrapperToStringConversion extends SimpleConversion {
private final Class<?> sourceType;
private final Class<?> primitiveType;
@ -37,13 +41,13 @@ public class WrapperToStringConversion implements Conversion {
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
return "String.valueOf( " + sourcePropertyAccessor + " )";
public String getToConversionString(String sourceReference, Context conversionContext) {
return "String.valueOf( " + sourceReference + " )";
}
@Override
public String from(String targetPropertyAccessor, Type type) {
public String getFromConversionString(String targetReference, Context conversionContext) {
return sourceType.getSimpleName() + ".parse" + Strings.capitalize( primitiveType.getSimpleName() ) + "( " +
targetPropertyAccessor + " )";
targetReference + " )";
}
}

View File

@ -18,10 +18,14 @@
*/
package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.NativeTypes;
public class WrapperToWrapperConversion implements Conversion {
/**
* Conversion between wrapper types such as {@link Integer} or {@link Long}.
*
* @author Gunnar Morling
*/
public class WrapperToWrapperConversion extends SimpleConversion {
private final Class<?> sourceType;
private final Class<?> targetType;
@ -39,22 +43,22 @@ public class WrapperToWrapperConversion implements Conversion {
}
@Override
public String to(String sourcePropertyAccessor, Type type) {
public String getToConversionString(String sourceReference, Context conversionContext) {
if ( sourceType == targetType ) {
return sourcePropertyAccessor;
return sourceReference;
}
else {
return sourcePropertyAccessor + "." + targetType.getName() + "Value()";
return sourceReference + "." + targetType.getName() + "Value()";
}
}
@Override
public String from(String targetPropertyAccessor, Type type) {
public String getFromConversionString(String targetReference, Context conversionContext) {
if ( sourceType == targetType ) {
return targetPropertyAccessor;
return targetReference;
}
else {
return targetPropertyAccessor + "." + sourceType.getName() + "Value()";
return targetReference + "." + sourceType.getName() + "Value()";
}
}
}

View File

@ -18,12 +18,10 @@
*/
package org.mapstruct.ap.model;
import org.mapstruct.ap.conversion.Conversion;
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one
* iterable type to another. The collection elements are mapped either by a
* {@link Conversion} or another mapping method.
* {@link TypeConversion} or another mapping method.
*
* @author Gunnar Morling
*/

View File

@ -18,10 +18,9 @@
*/
package org.mapstruct.ap.model;
import java.util.HashSet;
import java.util.Set;
import org.mapstruct.ap.util.Collections;
/**
* Represents the mapping between a source and target property, e.g. from
* {@code String Source#foo} to {@code int Target#bar}. Name and type of source
@ -44,11 +43,11 @@ public class PropertyMapping extends AbstractModelElement {
private final Type targetType;
private final MappingMethodReference mappingMethod;
private final String conversion;
private final TypeConversion conversion;
public PropertyMapping(String sourceBeanName, String targetBeanName, String sourceName, String sourceAccessorName,
Type sourceType, String targetName, String targetAccessorName, Type targetType,
MappingMethodReference mappingMethod, String conversion) {
MappingMethodReference mappingMethod, TypeConversion conversion) {
this.sourceBeanName = sourceBeanName;
this.targetBeanName = targetBeanName;
@ -100,13 +99,20 @@ public class PropertyMapping extends AbstractModelElement {
return mappingMethod;
}
public String getConversion() {
public TypeConversion getConversion() {
return conversion;
}
@Override
public Set<Type> getImportTypes() {
return Collections.asSet( sourceType, targetType );
Set<Type> importTypes = new HashSet<Type>();
importTypes.add( sourceType );
importTypes.add( targetType );
if ( conversion != null ) {
importTypes.addAll( conversion.getImportTypes() );
}
return importTypes;
}
@Override

View File

@ -0,0 +1,60 @@
/**
* Copyright 2012-2013 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.model;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* An inline conversion between source and target type of a mapping.
*
* @author Gunnar Morling
*/
public class TypeConversion extends AbstractModelElement {
private final Set<Type> importTypes;
private final List<Type> exceptionTypes;
private final String conversionString;
public TypeConversion(String conversionString) {
this( Collections.<Type>emptySet(), Collections.<Type>emptyList(), conversionString );
}
public TypeConversion(Set<Type> importTypes, List<Type> exceptionTypes, String conversionString) {
this.importTypes = new HashSet<Type>( importTypes );
this.importTypes.addAll( exceptionTypes );
this.exceptionTypes = exceptionTypes;
this.conversionString = conversionString;
}
@Override
public Set<Type> getImportTypes() {
return importTypes;
}
public List<Type> getExceptionTypes() {
return exceptionTypes;
}
public String getConversionString() {
return conversionString;
}
}

View File

@ -35,6 +35,7 @@ public class Mapping {
private final String sourceName;
private final String targetName;
private final String dateFormat;
private final AnnotationMirror mirror;
private final AnnotationValue sourceAnnotationValue;
private final AnnotationValue targetAnnotationValue;
@ -53,17 +54,18 @@ public class Mapping {
return new Mapping(
mapping.source(),
mapping.target(),
mapping.dateFormat(),
mapping.mirror,
mapping.values.source(),
mapping.values.target()
);
}
private Mapping(String sourceName, String targetName, AnnotationMirror mirror,
AnnotationValue sourceAnnotationValue,
AnnotationValue targetAnnotationValue) {
private Mapping(String sourceName, String targetName, String dateFormat, AnnotationMirror mirror,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) {
this.sourceName = sourceName;
this.targetName = targetName;
this.targetName = targetName.equals( "" ) ? sourceName : targetName;
this.dateFormat = dateFormat;
this.mirror = mirror;
this.sourceAnnotationValue = sourceAnnotationValue;
this.targetAnnotationValue = targetAnnotationValue;
@ -77,6 +79,10 @@ public class Mapping {
return targetName;
}
public String getDateFormat() {
return dateFormat;
}
public AnnotationMirror getMirror() {
return mirror;
}
@ -90,7 +96,7 @@ public class Mapping {
}
public Mapping reverse() {
return new Mapping( targetName, sourceName, mirror, sourceAnnotationValue, targetAnnotationValue );
return new Mapping( targetName, sourceName, dateFormat, mirror, sourceAnnotationValue, targetAnnotationValue );
}
@Override

View File

@ -36,8 +36,9 @@ import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.MapperPrism;
import org.mapstruct.ap.conversion.Conversion;
import org.mapstruct.ap.conversion.ConversionProvider;
import org.mapstruct.ap.conversion.Conversions;
import org.mapstruct.ap.conversion.DefaultConversionContext;
import org.mapstruct.ap.model.BeanMappingMethod;
import org.mapstruct.ap.model.DefaultMapperReference;
import org.mapstruct.ap.model.IterableMappingMethod;
@ -213,6 +214,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
for ( ExecutableElement getterMethod : sourceGetters ) {
String sourcePropertyName = executables.getPropertyName( getterMethod );
Mapping mapping = mappings.get( sourcePropertyName );
String dateFormat = mapping != null ? mapping.getDateFormat() : null;
for ( ExecutableElement setterMethod : targetSetters ) {
String targetPropertyName = executables.getPropertyName( setterMethod );
@ -222,7 +224,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
methods,
method,
getterMethod,
setterMethod
setterMethod,
dateFormat
);
propertyMappings.add( property );
@ -310,12 +313,12 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
}
private PropertyMapping getPropertyMapping(List<Method> methods, Method method, ExecutableElement getterMethod,
ExecutableElement setterMethod) {
ExecutableElement setterMethod, String dateFormat) {
Type sourceType = executables.retrieveReturnType( getterMethod );
Type targetType = executables.retrieveParameter( setterMethod ).getType();
MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType );
Conversion conversion = conversions.getConversion(
ConversionProvider conversionProvider = conversions.getConversion(
sourceType,
targetType
);
@ -330,9 +333,9 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
setterMethod.getSimpleName().toString(),
targetType,
propertyMappingMethod,
conversion != null ? conversion.to(
conversionProvider != null ? conversionProvider.to(
method.getParameterName() + "." + getterMethod.getSimpleName().toString() + "()",
targetType
new DefaultConversionContext( targetType, dateFormat )
) : null
);
@ -368,7 +371,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private String getIterableConversionString(Conversions conversions, Type sourceElementType, Type targetElementType,
boolean isToConversion) {
Conversion conversion = conversions.getConversion( sourceElementType, targetElementType );
ConversionProvider conversion = conversions.getConversion( sourceElementType, targetElementType );
if ( conversion == null ) {
return null;
@ -376,8 +379,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return conversion.to(
Introspector.decapitalize( sourceElementType.getName() ),
targetElementType
);
new DefaultConversionContext( targetElementType, null )
).getConversionString();
}
private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType,

View File

@ -25,10 +25,32 @@
<#elseif conversion??>
<#if sourceType.primitive == false>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
${targetBeanName}.${targetAccessorName}( ${conversion} );
<#if (conversion.exceptionTypes?size == 0) >
${targetBeanName}.${targetAccessorName}( <@includeModel object=conversion/> );
<#else>
try {
${targetBeanName}.${targetAccessorName}( <@includeModel object=conversion/> );
}
<#list conversion.exceptionTypes as exceptionType>
catch( ${exceptionType.name} e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
}
<#else>
${targetBeanName}.${targetAccessorName}( ${conversion} );
<#if (conversion.exceptionTypes?size == 0) >
${targetBeanName}.${targetAccessorName}( <@includeModel object=conversion/> );
<#else>
try {
${targetBeanName}.${targetAccessorName}( <@includeModel object=conversion/> );
}
<#list conversion.exceptionTypes as exceptionType>
catch( ${exceptionType.name} e ) {
throw new RuntimeException( e );
}
</#list>
</#if>
</#if>
<#-- c) simply set -->
<#else>

View File

@ -0,0 +1,21 @@
<#--
Copyright 2012-2013 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.
-->
${conversionString}

View File

@ -0,0 +1,75 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.conversion.date;
import java.util.GregorianCalendar;
import java.util.Locale;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.MapperTestBase;
import org.mapstruct.ap.testutil.WithClasses;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.fest.assertions.Assertions.assertThat;
/**
* Tests application of format strings for conversions between strings and dates.
*
* @author Gunnar Morling
*/
@WithClasses({
Source.class,
Target.class,
SourceTargetMapper.class
})
@IssueKey("43")
public class DateConversionTest extends MapperTestBase {
@BeforeMethod
public void setDefaultLocale() {
Locale.setDefault( Locale.GERMAN );
}
@Test
public void shouldApplyDateFormatForConversions() {
Source source = new Source();
source.setDate( new GregorianCalendar( 2013, 6, 6 ).getTime() );
source.setAnotherDate( new GregorianCalendar( 2013, 1, 14 ).getTime() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getDate() ).isEqualTo( "06.07.2013" );
assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13 00:00" );
}
@Test
public void shouldApplyDateFormatForConversionInReverseMapping() {
Target target = new Target();
target.setDate( "06.07.2013" );
target.setAnotherDate( "14.02.13 8:30" );
Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
assertThat( source ).isNotNull();
assertThat( source.getDate() ).isEqualTo( new GregorianCalendar( 2013, 6, 6 ).getTime() );
assertThat( source.getAnotherDate() ).isEqualTo( new GregorianCalendar( 2013, 1, 14, 8, 30 ).getTime() );
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.conversion.date;
import java.util.Date;
public class Source {
private Date date;
private Date anotherDate;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Date getAnotherDate() {
return anotherDate;
}
public void setAnotherDate(Date anotherDate) {
this.anotherDate = anotherDate;
}
}

View File

@ -16,13 +16,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.conversion;
package org.mapstruct.ap.test.conversion.date;
import org.mapstruct.ap.model.Type;
import org.mapstruct.Mapper;
import org.mapstruct.Mappers;
import org.mapstruct.Mapping;
public interface Conversion {
@Mapper
public interface SourceTargetMapper {
String to(String sourcePropertyAccessor, Type type);
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
String from(String targetPropertyAccessor, Type type);
@Mapping(source = "date", dateFormat = "dd.MM.yyyy")
Target sourceToTarget(Source source);
Source targetToSource(Target target);
}

View File

@ -0,0 +1,41 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.conversion.date;
public class Target {
private String date;
private String anotherDate;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getAnotherDate() {
return anotherDate;
}
public void setAnotherDate(String anotherDate) {
this.anotherDate = anotherDate;
}
}