#6 Making primitive conversions usable for iterable types

This commit is contained in:
Gunnar Morling 2013-05-05 19:12:19 +02:00
parent 321e1a3206
commit ae4655a2ba
9 changed files with 188 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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