mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#6 Making primitive conversions usable for iterable types
This commit is contained in:
parent
321e1a3206
commit
ae4655a2ba
@ -178,12 +178,36 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
boolean isIterableMapping = method.getSourceType().isIterableType() && method.getTargetType()
|
||||
.isIterableType();
|
||||
|
||||
String toConversionString = null;
|
||||
String fromConversionString = null;
|
||||
|
||||
if ( isIterableMapping ) {
|
||||
toConversionString = getIterableConversionString(
|
||||
conversions,
|
||||
method.getSourceType().getElementType(),
|
||||
method.getTargetType().getElementType(),
|
||||
true
|
||||
);
|
||||
fromConversionString = getIterableConversionString(
|
||||
conversions,
|
||||
method.getTargetType().getElementType(),
|
||||
method.getSourceType().getElementType(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
BeanMapping mapping = new BeanMapping(
|
||||
method.getSourceType(),
|
||||
method.getTargetType(),
|
||||
propertyMappings,
|
||||
mappingMethod,
|
||||
reverseMappingMethod
|
||||
reverseMappingMethod,
|
||||
toConversionString,
|
||||
fromConversionString
|
||||
);
|
||||
|
||||
mappings.add( mapping );
|
||||
@ -191,6 +215,19 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
private String getIterableConversionString(Conversions conversions, Type sourceElementType, Type targetElementType, boolean isToConversion) {
|
||||
Conversion conversion = conversions.getConversion( sourceElementType, targetElementType );
|
||||
|
||||
if ( conversion == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return conversion.to(
|
||||
Introspector.decapitalize( sourceElementType.getName() ),
|
||||
targetElementType
|
||||
);
|
||||
}
|
||||
|
||||
private List<Type> getUsedMapperTypes(TypeElement element) {
|
||||
List<Type> usedMapperTypes = new LinkedList<Type>();
|
||||
MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
|
||||
|
@ -54,6 +54,9 @@ public class Conversions {
|
||||
if ( sourceType.isEnumType() && targetType.equals( typeUtil.getType( stringType ) ) ) {
|
||||
sourceType = typeUtil.getType( enumType );
|
||||
}
|
||||
else if ( targetType.isEnumType() && sourceType.equals( typeUtil.getType( stringType ) ) ) {
|
||||
targetType = typeUtil.getType( enumType );
|
||||
}
|
||||
|
||||
return conversions.get( new Key( sourceType, targetType ) );
|
||||
}
|
||||
|
@ -25,15 +25,19 @@ public class BeanMapping {
|
||||
private final MappingMethod mappingMethod;
|
||||
private final MappingMethod reverseMappingMethod;
|
||||
private final boolean isIterableMapping;
|
||||
private final String toConversion;
|
||||
private final String fromConversion;
|
||||
|
||||
public BeanMapping(Type sourceType, Type targetType, List<PropertyMapping> propertyMappings, MappingMethod mappingMethod,
|
||||
MappingMethod reverseMappingMethod) {
|
||||
MappingMethod reverseMappingMethod, String toConversion, String fromConversion) {
|
||||
this.sourceType = sourceType;
|
||||
this.targetType = targetType;
|
||||
this.propertyMappings = propertyMappings;
|
||||
this.mappingMethod = mappingMethod;
|
||||
this.reverseMappingMethod = reverseMappingMethod;
|
||||
this.isIterableMapping = sourceType.isIterableType() && targetType.isIterableType();
|
||||
this.toConversion = toConversion;
|
||||
this.fromConversion = fromConversion;
|
||||
}
|
||||
|
||||
public Type getSourceType() {
|
||||
@ -60,6 +64,14 @@ public class BeanMapping {
|
||||
return isIterableMapping;
|
||||
}
|
||||
|
||||
public String getToConversion() {
|
||||
return toConversion;
|
||||
}
|
||||
|
||||
public String getFromConversion() {
|
||||
return fromConversion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder( "BeanMapping {" );
|
||||
@ -76,6 +88,8 @@ public class BeanMapping {
|
||||
|
||||
sb.append( "\n mappingMethod=" + mappingMethod.toString().replaceAll( "\n", "\n " ) + ',' );
|
||||
sb.append( "\n reverseMappingMethod=" + reverseMappingMethod + ',' );
|
||||
sb.append( "\n toConversion=" + toConversion + ',' );
|
||||
sb.append( "\n fromConversion=" + fromConversion + ',' );
|
||||
sb.append( "\n isIterableMapping=" + isIterableMapping );
|
||||
sb.append( "\n}" );
|
||||
|
||||
|
@ -48,7 +48,11 @@ public class ${implementationName} implements ${interfaceName} {
|
||||
<#if beanMapping.targetType.name == "Iterable" && beanMapping.targetType.packageName == "java.lang">${beanMapping.targetType.iterableImplementationType.name}<#else>${beanMapping.targetType.name}</#if><${beanMapping.targetType.elementType.name}> ${beanMapping.targetType.name?uncap_first} = new <#if beanMapping.targetType.iterableImplementationType??>${beanMapping.targetType.iterableImplementationType.name}<#else>${beanMapping.targetType.name}</#if><${beanMapping.targetType.elementType.name}>();
|
||||
|
||||
for ( ${beanMapping.sourceType.elementType.name} ${beanMapping.sourceType.elementType.name?uncap_first} : ${beanMapping.mappingMethod.parameterName} ) {
|
||||
<#if beanMapping.toConversion??>
|
||||
${beanMapping.targetType.name?uncap_first}.add( ${beanMapping.toConversion} );
|
||||
<#else>
|
||||
${beanMapping.targetType.name?uncap_first}.add( ${beanMapping.mappingMethod.elementMappingMethod.name}( ${beanMapping.sourceType.elementType.name?uncap_first} ) );
|
||||
</#if>
|
||||
}
|
||||
|
||||
return ${beanMapping.targetType.name?uncap_first};
|
||||
@ -94,6 +98,7 @@ public class ${implementationName} implements ${interfaceName} {
|
||||
<#if beanMapping.reverseMappingMethod??>
|
||||
<#if beanMapping.reverseMappingMethod.generationRequired == true>
|
||||
<#if beanMapping.iterableMapping == true>
|
||||
|
||||
@Override
|
||||
public ${beanMapping.sourceType.name}<${beanMapping.sourceType.elementType.name}> ${beanMapping.reverseMappingMethod.name}(${beanMapping.targetType.name}<${beanMapping.targetType.elementType.name}> ${beanMapping.reverseMappingMethod.parameterName}) {
|
||||
if( ${beanMapping.reverseMappingMethod.parameterName} == null ) {
|
||||
@ -102,9 +107,13 @@ public class ${implementationName} implements ${interfaceName} {
|
||||
|
||||
<#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side -->
|
||||
<#if beanMapping.sourceType.name == "Iterable" && beanMapping.sourceType.packageName == "java.lang">${beanMapping.sourceType.iterableImplementationType.name}<#else>${beanMapping.sourceType.name}</#if><${beanMapping.sourceType.elementType.name}> ${beanMapping.sourceType.name?uncap_first} = new <#if beanMapping.sourceType.iterableImplementationType??>${beanMapping.sourceType.iterableImplementationType.name}<#else>${beanMapping.sourceType.name}</#if><${beanMapping.sourceType.elementType.name}>();
|
||||
|
||||
|
||||
for ( ${beanMapping.targetType.elementType.name} ${beanMapping.targetType.elementType.name?uncap_first} : ${beanMapping.reverseMappingMethod.parameterName} ) {
|
||||
<#if beanMapping.fromConversion??>
|
||||
${beanMapping.sourceType.name?uncap_first}.add( ${beanMapping.fromConversion} );
|
||||
<#else>
|
||||
${beanMapping.sourceType.name?uncap_first}.add( ${beanMapping.reverseMappingMethod.elementMappingMethod.name}( ${beanMapping.targetType.elementType.name?uncap_first} ) );
|
||||
</#if>
|
||||
}
|
||||
|
||||
return ${beanMapping.sourceType.name?uncap_first};
|
||||
|
@ -17,6 +17,7 @@ package org.mapstruct.ap.test.collection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@ -33,6 +34,7 @@ public class CollectionMappingTest extends MapperTestBase {
|
||||
return Arrays.<Class<?>>asList(
|
||||
Source.class,
|
||||
Target.class,
|
||||
Colour.class,
|
||||
SourceTargetMapper.class
|
||||
);
|
||||
}
|
||||
@ -214,4 +216,52 @@ public class CollectionMappingTest extends MapperTestBase {
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getSet() ).containsOnly( 1, 2 );
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("6")
|
||||
public void shouldMapIntegerSetToStringSet() {
|
||||
Source source = new Source();
|
||||
source.setAnotherIntegerSet( new HashSet<Integer>( Arrays.asList( 1, 2 ) ) );
|
||||
|
||||
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getAnotherStringSet() ).containsOnly( "1", "2" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("6")
|
||||
public void shouldReverseMapIntegerSetToStringSet() {
|
||||
Target target = new Target();
|
||||
target.setAnotherStringSet( new HashSet<String>( Arrays.asList( "1", "2" ) ) );
|
||||
|
||||
Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
|
||||
|
||||
assertThat( source ).isNotNull();
|
||||
assertThat( source.getAnotherIntegerSet() ).containsOnly( 1, 2 );
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("6")
|
||||
public void shouldMapSetOfEnumToStringSet() {
|
||||
Source source = new Source();
|
||||
source.setColours( EnumSet.of( Colour.BLUE, Colour.GREEN ) );
|
||||
|
||||
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getColours() ).containsOnly( "BLUE", "GREEN" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("6")
|
||||
public void shouldReverseMapSetOfEnumToStringSet() {
|
||||
Target target = new Target();
|
||||
target.setColours( new HashSet<String>( Arrays.asList( "BLUE", "GREEN" ) ) );
|
||||
|
||||
Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
|
||||
|
||||
assertThat( source ).isNotNull();
|
||||
assertThat( source.getColours() ).containsOnly( Colour.GREEN, Colour.BLUE );
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
*
|
||||
* 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.collection;
|
||||
|
||||
public enum Colour {
|
||||
RED, GREEN, BLUE;
|
||||
}
|
@ -35,6 +35,10 @@ public class Source {
|
||||
|
||||
private Set<Integer> integerSet;
|
||||
|
||||
private Set<Integer> anotherIntegerSet;
|
||||
|
||||
private Set<Colour> colours;
|
||||
|
||||
public List<String> getStringList() {
|
||||
return stringList;
|
||||
}
|
||||
@ -90,4 +94,20 @@ public class Source {
|
||||
public void setIntegerSet(Set<Integer> integerSet) {
|
||||
this.integerSet = integerSet;
|
||||
}
|
||||
|
||||
public Set<Integer> getAnotherIntegerSet() {
|
||||
return anotherIntegerSet;
|
||||
}
|
||||
|
||||
public void setAnotherIntegerSet(Set<Integer> anotherIntegerSet) {
|
||||
this.anotherIntegerSet = anotherIntegerSet;
|
||||
}
|
||||
|
||||
public Set<Colour> getColours() {
|
||||
return colours;
|
||||
}
|
||||
|
||||
public void setColours(Set<Colour> colours) {
|
||||
this.colours = colours;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.collection;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mappers;
|
||||
import org.mapstruct.Mapping;
|
||||
@ -27,9 +29,18 @@ public interface SourceTargetMapper {
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "integerList", target = "integerCollection"),
|
||||
@Mapping(source = "integerSet", target = "set")
|
||||
@Mapping(source = "integerSet", target = "set"),
|
||||
@Mapping(source = "anotherIntegerSet", target = "anotherStringSet")
|
||||
})
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
Source targetToSource(Target target);
|
||||
|
||||
Set<String> integerSetToStringSet(Set<Integer> integers);
|
||||
|
||||
Set<Integer> stringSetToIntegerSet(Set<String> strings);
|
||||
|
||||
Set<String> colourSetToStringSet(Set<Colour> colours);
|
||||
|
||||
Set<Colour> stringSetToColourSet(Set<String> colours);
|
||||
}
|
||||
|
@ -33,6 +33,10 @@ public class Target {
|
||||
|
||||
private Collection<Integer> integerCollection;
|
||||
|
||||
private Set<String> anotherStringSet;
|
||||
|
||||
private Set<String> colours;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Set set;
|
||||
|
||||
@ -93,4 +97,20 @@ public class Target {
|
||||
public void setSet(Set set) {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
public Set<String> getAnotherStringSet() {
|
||||
return anotherStringSet;
|
||||
}
|
||||
|
||||
public void setAnotherStringSet(Set<String> anotherStringSet) {
|
||||
this.anotherStringSet = anotherStringSet;
|
||||
}
|
||||
|
||||
public void setColours(Set<String> colours) {
|
||||
this.colours = colours;
|
||||
}
|
||||
|
||||
public Set<String> getColours() {
|
||||
return colours;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user