mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#44 Adding support for specification of date formats in Map mapping methods
This commit is contained in:
parent
346512cc82
commit
ea436e061f
53
core/src/main/java/org/mapstruct/MapMapping.java
Normal file
53
core/src/main/java/org/mapstruct/MapMapping.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the mapping between two map types, e.g. {@code Map<String, String>} and {@code Map<Long, Date>}.
|
||||||
|
*
|
||||||
|
* @author Gunnar Morling
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface MapMapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with key type
|
||||||
|
* {@code String} to an map with key type {@link Date} or vice-versa. Will be ignored for all other key types.
|
||||||
|
*
|
||||||
|
* @return A date format string as processable by {@link SimpleDateFormat}.
|
||||||
|
*/
|
||||||
|
String keyDateFormat() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with value
|
||||||
|
* type {@code String} to an map with value type {@link Date} or vice-versa. Will be ignored for all other value
|
||||||
|
* types.
|
||||||
|
*
|
||||||
|
* @return A date format string as processable by {@link SimpleDateFormat}.
|
||||||
|
*/
|
||||||
|
String valueDateFormat() default "";
|
||||||
|
}
|
@ -40,9 +40,10 @@ import javax.lang.model.util.ElementKindVisitor6;
|
|||||||
import net.java.dev.hickory.prism.GeneratePrism;
|
import net.java.dev.hickory.prism.GeneratePrism;
|
||||||
import net.java.dev.hickory.prism.GeneratePrisms;
|
import net.java.dev.hickory.prism.GeneratePrisms;
|
||||||
import org.mapstruct.IterableMapping;
|
import org.mapstruct.IterableMapping;
|
||||||
|
import org.mapstruct.MapMapping;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Mappings;
|
import org.mapstruct.Mappings;
|
||||||
import org.mapstruct.ap.model.Mapper;
|
|
||||||
import org.mapstruct.ap.model.Options;
|
import org.mapstruct.ap.model.Options;
|
||||||
import org.mapstruct.ap.model.ReportingPolicy;
|
import org.mapstruct.ap.model.ReportingPolicy;
|
||||||
import org.mapstruct.ap.processor.DefaultModelElementProcessorContext;
|
import org.mapstruct.ap.processor.DefaultModelElementProcessorContext;
|
||||||
@ -71,10 +72,11 @@ import org.mapstruct.ap.processor.ModelElementProcessor.ProcessorContext;
|
|||||||
*/
|
*/
|
||||||
@SupportedAnnotationTypes("org.mapstruct.Mapper")
|
@SupportedAnnotationTypes("org.mapstruct.Mapper")
|
||||||
@GeneratePrisms({
|
@GeneratePrisms({
|
||||||
@GeneratePrism(value = org.mapstruct.Mapper.class, publicAccess = true),
|
@GeneratePrism(value = Mapper.class, publicAccess = true),
|
||||||
@GeneratePrism(value = Mapping.class, publicAccess = true),
|
@GeneratePrism(value = Mapping.class, publicAccess = true),
|
||||||
@GeneratePrism(value = Mappings.class, publicAccess = true),
|
@GeneratePrism(value = Mappings.class, publicAccess = true),
|
||||||
@GeneratePrism(value = IterableMapping.class, publicAccess = true)
|
@GeneratePrism(value = IterableMapping.class, publicAccess = true),
|
||||||
|
@GeneratePrism(value = MapMapping.class, publicAccess = true)
|
||||||
})
|
})
|
||||||
@SupportedOptions({
|
@SupportedOptions({
|
||||||
MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP,
|
MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP,
|
||||||
|
@ -30,6 +30,8 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.util.Strings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the type of a bean property, parameter etc.
|
* Represents the type of a bean property, parameter etc.
|
||||||
*
|
*
|
||||||
@ -244,4 +246,14 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
|
|||||||
public int compareTo(Type o) {
|
public int compareTo(Type o) {
|
||||||
return getFullyQualifiedName().compareTo( o.getFullyQualifiedName() );
|
return getFullyQualifiedName().compareTo( o.getFullyQualifiedName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if ( !typeParameters.isEmpty() ) {
|
||||||
|
return name + "<" + Strings.join( typeParameters, ", " ) + ">";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* 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.source;
|
||||||
|
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.MapMappingPrism;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a map mapping as configured via {@code @MapMapping}.
|
||||||
|
*
|
||||||
|
* @author Gunnar Morling
|
||||||
|
*/
|
||||||
|
public class MapMapping {
|
||||||
|
|
||||||
|
private final String keyFormat;
|
||||||
|
private final String valueFormat;
|
||||||
|
private final AnnotationMirror mirror;
|
||||||
|
|
||||||
|
public static MapMapping fromPrism(MapMappingPrism mapMapping) {
|
||||||
|
if ( mapMapping == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MapMapping(
|
||||||
|
mapMapping.keyDateFormat(),
|
||||||
|
mapMapping.valueDateFormat(),
|
||||||
|
mapMapping.mirror
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MapMapping(String keyFormat, String valueFormat, AnnotationMirror mirror) {
|
||||||
|
this.keyFormat = keyFormat;
|
||||||
|
this.valueFormat = valueFormat;
|
||||||
|
this.mirror = mirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyFormat() {
|
||||||
|
return keyFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValueFormat() {
|
||||||
|
return valueFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationMirror getMirror() {
|
||||||
|
return mirror;
|
||||||
|
}
|
||||||
|
}
|
@ -39,11 +39,12 @@ public class Method {
|
|||||||
private final Type targetType;
|
private final Type targetType;
|
||||||
private Map<String, Mapping> mappings;
|
private Map<String, Mapping> mappings;
|
||||||
private IterableMapping iterableMapping;
|
private IterableMapping iterableMapping;
|
||||||
|
private MapMapping mapMapping;
|
||||||
|
|
||||||
public static Method forMethodRequiringImplementation(ExecutableElement executable, String parameterName,
|
public static Method forMethodRequiringImplementation(ExecutableElement executable, String parameterName,
|
||||||
Type sourceType, Type targetType,
|
Type sourceType, Type targetType,
|
||||||
Map<String, Mapping> mappings,
|
Map<String, Mapping> mappings,
|
||||||
IterableMapping iterableMapping) {
|
IterableMapping iterableMapping, MapMapping mapMapping) {
|
||||||
|
|
||||||
return new Method(
|
return new Method(
|
||||||
null,
|
null,
|
||||||
@ -52,7 +53,8 @@ public class Method {
|
|||||||
sourceType,
|
sourceType,
|
||||||
targetType,
|
targetType,
|
||||||
mappings,
|
mappings,
|
||||||
iterableMapping
|
iterableMapping,
|
||||||
|
mapMapping
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +68,14 @@ public class Method {
|
|||||||
sourceType,
|
sourceType,
|
||||||
targetType,
|
targetType,
|
||||||
Collections.<String, Mapping>emptyMap(),
|
Collections.<String, Mapping>emptyMap(),
|
||||||
|
null,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType,
|
private Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType,
|
||||||
Type targetType, Map<String, Mapping> mappings, IterableMapping iterableMapping) {
|
Type targetType, Map<String, Mapping> mappings, IterableMapping iterableMapping,
|
||||||
|
MapMapping mapMapping) {
|
||||||
this.declaringMapper = declaringMapper;
|
this.declaringMapper = declaringMapper;
|
||||||
this.executable = executable;
|
this.executable = executable;
|
||||||
this.parameterName = parameterName;
|
this.parameterName = parameterName;
|
||||||
@ -79,6 +83,7 @@ public class Method {
|
|||||||
this.targetType = targetType;
|
this.targetType = targetType;
|
||||||
this.mappings = mappings;
|
this.mappings = mappings;
|
||||||
this.iterableMapping = iterableMapping;
|
this.iterableMapping = iterableMapping;
|
||||||
|
this.mapMapping = mapMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,6 +133,14 @@ public class Method {
|
|||||||
this.iterableMapping = iterableMapping;
|
this.iterableMapping = iterableMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MapMapping getMapMapping() {
|
||||||
|
return mapMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMapMapping(MapMapping mapMapping) {
|
||||||
|
this.mapMapping = mapMapping;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean reverses(Method method) {
|
public boolean reverses(Method method) {
|
||||||
return
|
return
|
||||||
equals( sourceType, method.getTargetType() ) &&
|
equals( sourceType, method.getTargetType() ) &&
|
||||||
|
@ -166,6 +166,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
|||||||
mappingMethods.add( getIterableMappingMethod( methods, method ) );
|
mappingMethods.add( getIterableMappingMethod( methods, method ) );
|
||||||
}
|
}
|
||||||
else if ( method.isMapMapping() ) {
|
else if ( method.isMapMapping() ) {
|
||||||
|
if ( method.getMapMapping() == null && reverseMappingMethod != null &&
|
||||||
|
reverseMappingMethod.getMapMapping() != null ) {
|
||||||
|
method.setMapMapping( reverseMappingMethod.getMapMapping() );
|
||||||
|
}
|
||||||
mappingMethods.add( getMapMappingMethod( methods, method ) );
|
mappingMethods.add( getMapMappingMethod( methods, method ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -382,8 +386,17 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
|||||||
Type targetKeyType = method.getTargetType().getTypeParameters().get( 0 );
|
Type targetKeyType = method.getTargetType().getTypeParameters().get( 0 );
|
||||||
Type targetValueType = method.getTargetType().getTypeParameters().get( 1 );
|
Type targetValueType = method.getTargetType().getTypeParameters().get( 1 );
|
||||||
|
|
||||||
TypeConversion valueConversion = getConversion( sourceValueType, targetValueType, null, "entry.getValue()" );
|
String keyDateFormat = method.getMapMapping() != null ? method.getMapMapping().getKeyFormat() : null;
|
||||||
TypeConversion keyConversion = getConversion( sourceKeyType, targetKeyType, null, "entry.getKey()" );
|
String valueDateFormat = method.getMapMapping() != null ? method.getMapMapping().getValueFormat() : null;
|
||||||
|
|
||||||
|
TypeConversion keyConversion = getConversion( sourceKeyType, targetKeyType, keyDateFormat, "entry.getKey()" );
|
||||||
|
TypeConversion valueConversion = getConversion(
|
||||||
|
sourceValueType,
|
||||||
|
targetValueType,
|
||||||
|
valueDateFormat,
|
||||||
|
"entry.getValue()"
|
||||||
|
);
|
||||||
|
|
||||||
MappingMethodReference keyMappingMethod = getMappingMethodReference( methods, sourceKeyType, targetKeyType );
|
MappingMethodReference keyMappingMethod = getMappingMethodReference( methods, sourceKeyType, targetKeyType );
|
||||||
MappingMethodReference valueMappingMethod = getMappingMethodReference(
|
MappingMethodReference valueMappingMethod = getMappingMethodReference(
|
||||||
methods,
|
methods,
|
||||||
|
@ -31,11 +31,13 @@ import javax.lang.model.util.Types;
|
|||||||
import javax.tools.Diagnostic.Kind;
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
|
||||||
import org.mapstruct.ap.IterableMappingPrism;
|
import org.mapstruct.ap.IterableMappingPrism;
|
||||||
|
import org.mapstruct.ap.MapMappingPrism;
|
||||||
import org.mapstruct.ap.MapperPrism;
|
import org.mapstruct.ap.MapperPrism;
|
||||||
import org.mapstruct.ap.MappingPrism;
|
import org.mapstruct.ap.MappingPrism;
|
||||||
import org.mapstruct.ap.MappingsPrism;
|
import org.mapstruct.ap.MappingsPrism;
|
||||||
import org.mapstruct.ap.model.Type;
|
import org.mapstruct.ap.model.Type;
|
||||||
import org.mapstruct.ap.model.source.IterableMapping;
|
import org.mapstruct.ap.model.source.IterableMapping;
|
||||||
|
import org.mapstruct.ap.model.source.MapMapping;
|
||||||
import org.mapstruct.ap.model.source.Mapping;
|
import org.mapstruct.ap.model.source.Mapping;
|
||||||
import org.mapstruct.ap.model.source.Method;
|
import org.mapstruct.ap.model.source.Method;
|
||||||
import org.mapstruct.ap.model.source.Parameter;
|
import org.mapstruct.ap.model.source.Parameter;
|
||||||
@ -128,7 +130,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
parameter.getType(),
|
parameter.getType(),
|
||||||
returnType,
|
returnType,
|
||||||
getMappings( method ),
|
getMappings( method ),
|
||||||
getIterableMapping( method )
|
IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ),
|
||||||
|
MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ) )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -204,11 +207,4 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
|
|
||||||
return mappings;
|
return mappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IterableMapping getIterableMapping(ExecutableElement method) {
|
|
||||||
IterableMappingPrism iterableMappingAnnotation = IterableMappingPrism.getInstanceOn( method );
|
|
||||||
return iterableMappingAnnotation != null ?
|
|
||||||
IterableMapping.fromIterableMappingPrism( iterableMappingAnnotation ) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,4 +49,8 @@ public class Strings {
|
|||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(String string) {
|
||||||
|
return string == null || string.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,11 @@ package org.mapstruct.ap.test.collection.map;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.mapstruct.ap.testutil.IssueKey;
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
import org.mapstruct.ap.testutil.MapperTestBase;
|
import org.mapstruct.ap.testutil.MapperTestBase;
|
||||||
import org.mapstruct.ap.testutil.WithClasses;
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
import org.testng.annotations.BeforeMethod;
|
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import static org.fest.assertions.Assertions.assertThat;
|
import static org.fest.assertions.Assertions.assertThat;
|
||||||
@ -42,11 +40,6 @@ import static org.fest.assertions.MapAssert.entry;
|
|||||||
@IssueKey("44")
|
@IssueKey("44")
|
||||||
public class MapMappingTest extends MapperTestBase {
|
public class MapMappingTest extends MapperTestBase {
|
||||||
|
|
||||||
@BeforeMethod
|
|
||||||
public void setDefaultLocale() {
|
|
||||||
Locale.setDefault( Locale.GERMAN );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateMapMethodImplementation() {
|
public void shouldCreateMapMethodImplementation() {
|
||||||
Map<Long, Date> values = new HashMap<Long, Date>();
|
Map<Long, Date> values = new HashMap<Long, Date>();
|
||||||
@ -57,14 +50,14 @@ public class MapMappingTest extends MapperTestBase {
|
|||||||
|
|
||||||
assertThat( target ).isNotNull();
|
assertThat( target ).isNotNull();
|
||||||
assertThat( target ).hasSize( 2 );
|
assertThat( target ).hasSize( 2 );
|
||||||
assertThat( target ).includes( entry( "42", "01.01.80 00:00" ), entry( "121", "20.07.13 00:00" ) );
|
assertThat( target ).includes( entry( "42", "01.01.1980" ), entry( "121", "20.07.2013" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateReverseMapMethodImplementation() {
|
public void shouldCreateReverseMapMethodImplementation() {
|
||||||
Map<String, String> values = new HashMap<String, String>();
|
Map<String, String> values = new HashMap<String, String>();
|
||||||
values.put( "42", "01.01.80 00:00" );
|
values.put( "42", "01.01.1980" );
|
||||||
values.put( "121", "20.07.13 00:00" );
|
values.put( "121", "20.07.2013" );
|
||||||
|
|
||||||
Map<Long, Date> target = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values );
|
Map<Long, Date> target = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values );
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ package org.mapstruct.ap.test.collection.map;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.mapstruct.MapMapping;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mappers;
|
import org.mapstruct.Mappers;
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ public interface SourceTargetMapper {
|
|||||||
|
|
||||||
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
|
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
|
||||||
|
|
||||||
|
@MapMapping( valueDateFormat = "dd.MM.yyyy" )
|
||||||
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
|
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
|
||||||
|
|
||||||
Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source);
|
Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user