diff --git a/processor/src/main/java/org/mapstruct/ap/model/Type.java b/processor/src/main/java/org/mapstruct/ap/model/Type.java index a4551c3a8..ff2c2a675 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/model/Type.java @@ -48,12 +48,19 @@ public class Type { private final Type elementType; private final boolean isEnumType; private final Type implementingType; + private final boolean isCollectionType; public static Type forClass(Class clazz) { Package pakkage = clazz.getPackage(); if ( pakkage != null ) { - return new Type( pakkage.getName(), clazz.getSimpleName(), null, clazz.isEnum() ); + return new Type( + pakkage.getName(), + clazz.getSimpleName(), + null, + clazz.isEnum(), + Collection.class.isAssignableFrom( clazz ) + ); } else { return new Type( clazz.getSimpleName() ); @@ -61,15 +68,16 @@ public class Type { } public Type(String name) { - this( null, name, null, false ); + this( null, name, null, false, false ); } - public Type(String packageName, String name, Type elementType, boolean isEnumType) { + public Type(String packageName, String name, Type elementType, boolean isEnumType, boolean isCollectionType) { this.packageName = packageName; this.name = name; this.elementType = elementType; this.isEnumType = isEnumType; implementingType = defaultCollectionImplementationTypes.get( packageName + "." + name ); + this.isCollectionType = isCollectionType; } public String getPackageName() { @@ -96,6 +104,10 @@ public class Type { return implementingType; } + public boolean isCollectionType() { + return isCollectionType; + } + @Override public String toString() { if ( packageName == null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java b/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java index 259de4799..27ca1a498 100644 --- a/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java +++ b/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java @@ -15,6 +15,7 @@ */ package org.mapstruct.ap.util; +import java.util.Collection; import javax.lang.model.element.ElementKind; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; @@ -28,20 +29,27 @@ public class TypeUtil { private final Elements elementUtils; private final Types typeUtils; + private TypeMirror collectionType; public TypeUtil(Elements elementUtils, Types typeUtils) { this.elementUtils = elementUtils; this.typeUtils = typeUtils; + collectionType = elementUtils.getTypeElement( Collection.class.getCanonicalName() ).asType(); } public Type getType(DeclaredType type) { - Type elementType = isIterableType( type ) ? retrieveType( type.getTypeArguments().iterator().next() ) : null; + Type elementType = null; + + if ( isIterableType( type ) && !type.getTypeArguments().isEmpty() ) { + elementType = retrieveType( type.getTypeArguments().iterator().next() ); + } return new Type( elementUtils.getPackageOf( type.asElement() ).toString(), type.asElement().getSimpleName().toString(), elementType, - type.asElement().getKind() == ElementKind.ENUM + type.asElement().getKind() == ElementKind.ENUM, + typeUtils.isAssignable( typeUtils.erasure( type ), typeUtils.erasure( collectionType ) ) ); } diff --git a/processor/src/main/resources/mapper-implementation.ftl b/processor/src/main/resources/mapper-implementation.ftl index be1d95a23..aba8eb272 100644 --- a/processor/src/main/resources/mapper-implementation.ftl +++ b/processor/src/main/resources/mapper-implementation.ftl @@ -75,7 +75,13 @@ public class ${implementationName} implements ${interfaceName} { <#elseif propertyMapping.mappingMethod??> ${beanMapping.targetType.name?uncap_first}.set${propertyMapping.targetName?cap_first}( <#if propertyMapping.mappingMethod.declaringMapper??>${propertyMapping.mappingMethod.declaringMapper.name?uncap_first}.${propertyMapping.mappingMethod.name}( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() ) ); <#else> + <#if propertyMapping.targetType.collectionType == true> + if ( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() != null ) { + ${beanMapping.targetType.name?uncap_first}.set${propertyMapping.targetName?cap_first}( new <#if propertyMapping.targetType.implementingType??>${propertyMapping.targetType.implementingType.name}<#else>${propertyMapping.targetType.name}<#if propertyMapping.targetType.elementType??><${propertyMapping.targetType.elementType.name}>( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() ) ); + } + <#else> ${beanMapping.targetType.name?uncap_first}.set${propertyMapping.targetName?cap_first}( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() ); + @@ -124,7 +130,13 @@ public class ${implementationName} implements ${interfaceName} { <#elseif propertyMapping.reverseMappingMethod??> ${beanMapping.sourceType.name?uncap_first}.set${propertyMapping.sourceName?cap_first}( <#if propertyMapping.reverseMappingMethod.declaringMapper??>${propertyMapping.reverseMappingMethod.declaringMapper.name?uncap_first}.${propertyMapping.reverseMappingMethod.name}( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() ) ); <#else> + <#if propertyMapping.sourceType.collectionType == true> + if ( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() != null ) { + ${beanMapping.sourceType.name?uncap_first}.set${propertyMapping.sourceName?cap_first}( new <#if propertyMapping.sourceType.implementingType??>${propertyMapping.sourceType.implementingType.name}<#else>${propertyMapping.sourceType.name}<#if propertyMapping.sourceType.elementType??><${propertyMapping.sourceType.elementType.name}>( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() ) ); + } + <#else> ${beanMapping.sourceType.name?uncap_first}.set${propertyMapping.sourceName?cap_first}( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() ); + diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java index dd848373e..ca12eac90 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java @@ -15,7 +15,9 @@ */ package org.mapstruct.ap.test.collection; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import org.mapstruct.ap.testutil.IssueKey; @@ -45,4 +47,159 @@ public class CollectionMappingTest extends MapperTestBase { assertThat( target ).isNotNull(); assertThat( target.getStringList() ).isNull(); } + + @Test + @IssueKey("6") + public void shouldReverseMapNullList() { + Target target = new Target(); + + Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getStringList() ).isNull(); + } + + @Test + @IssueKey("6") + public void shouldMapList() { + Source source = new Source(); + source.setStringList( Arrays.asList( "Bob", "Alice" ) ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getStringList() ).containsExactly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldReverseMapList() { + Target target = new Target(); + target.setStringList( Arrays.asList( "Bob", "Alice" ) ); + + Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getStringList() ).containsExactly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldMapListAsCopy() { + Source source = new Source(); + source.setStringList( Arrays.asList( "Bob", "Alice" ) ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + target.getStringList().add( "Bill" ); + + assertThat( source.getStringList() ).containsExactly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldReverseMapListAsCopy() { + Target target = new Target(); + target.setStringList( Arrays.asList( "Bob", "Alice" ) ); + + Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); + source.getStringList().add( "Bill" ); + + assertThat( target.getStringList() ).containsExactly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldMapArrayList() { + Source source = new Source(); + source.setStringArrayList( new ArrayList( Arrays.asList( "Bob", "Alice" ) ) ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getStringArrayList() ).containsExactly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldReverseMapArrayList() { + Target target = new Target(); + target.setStringArrayList( new ArrayList( Arrays.asList( "Bob", "Alice" ) ) ); + + Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getStringArrayList() ).containsExactly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldMapSet() { + Source source = new Source(); + source.setStringSet( new HashSet( Arrays.asList( "Bob", "Alice" ) ) ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getStringSet() ).contains( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldReverseMapSet() { + Target target = new Target(); + target.setStringSet( new HashSet( Arrays.asList( "Bob", "Alice" ) ) ); + + Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getStringSet() ).contains( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldMapSetAsCopy() { + Source source = new Source(); + source.setStringSet( new HashSet( Arrays.asList( "Bob", "Alice" ) ) ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + target.getStringSet().add( "Bill" ); + + assertThat( source.getStringSet() ).containsOnly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldReverseMapSetAsCopy() { + Target target = new Target(); + target.setStringSet( new HashSet( Arrays.asList( "Bob", "Alice" ) ) ); + + Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); + source.getStringSet().add( "Bill" ); + + assertThat( target.getStringSet() ).containsOnly( "Bob", "Alice" ); + } + + @Test + @IssueKey("6") + public void shouldMapListToCollection() { + Source source = new Source(); + source.setIntegerList( Arrays.asList( 1, 2 ) ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getIntegerCollection() ).containsOnly( 1, 2 ); + } + + @Test + @IssueKey("6") + public void shouldReverseMapListToCollection() { + Target target = new Target(); + target.setIntegerCollection( Arrays.asList( 1, 2 ) ); + + Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getIntegerList() ).containsOnly( 1, 2 ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/Source.java b/processor/src/test/java/org/mapstruct/ap/test/collection/Source.java index c4ed2cfa6..f4d7d1d1d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/Source.java @@ -15,11 +15,23 @@ */ package org.mapstruct.ap.test.collection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class Source { private List stringList; + private ArrayList stringArrayList; + + private Set stringSet; + private HashSet stringHashSet; + + private Collection stringCollection; + + private List integerList; public List getStringList() { return stringList; @@ -28,4 +40,44 @@ public class Source { public void setStringList(List stringList) { this.stringList = stringList; } + + public ArrayList getStringArrayList() { + return stringArrayList; + } + + public void setStringArrayList(ArrayList stringArrayList) { + this.stringArrayList = stringArrayList; + } + + public Set getStringSet() { + return stringSet; + } + + public void setStringSet(Set stringSet) { + this.stringSet = stringSet; + } + + public HashSet getStringHashSet() { + return stringHashSet; + } + + public void setStringHashSet(HashSet stringHashSet) { + this.stringHashSet = stringHashSet; + } + + public Collection getStringCollection() { + return stringCollection; + } + + public void setStringCollection(Collection stringCollection) { + this.stringCollection = stringCollection; + } + + public List getIntegerList() { + return integerList; + } + + public void setIntegerList(List integerList) { + this.integerList = integerList; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java index 02ed7fba6..cbe38fc12 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java @@ -15,14 +15,20 @@ */ package org.mapstruct.ap.test.collection; - import org.mapstruct.Mapper; import org.mapstruct.Mappers; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; @Mapper public interface SourceTargetMapper { public static SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + @Mappings({ + @Mapping(source = "integerList", target = "integerCollection") + }) Target sourceToTarget(Source source); + + Source targetToSource(Target target); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java b/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java index d21dd5a6b..701449457 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java @@ -15,11 +15,23 @@ */ package org.mapstruct.ap.test.collection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class Target { private List stringList; + private ArrayList stringArrayList; + + private Set stringSet; + private HashSet stringHashSet; + + private Collection stringCollection; + + private Collection integerCollection; public List getStringList() { return stringList; @@ -28,4 +40,44 @@ public class Target { public void setStringList(List stringList) { this.stringList = stringList; } + + public ArrayList getStringArrayList() { + return stringArrayList; + } + + public void setStringArrayList(ArrayList stringArrayList) { + this.stringArrayList = stringArrayList; + } + + public Set getStringSet() { + return stringSet; + } + + public void setStringSet(Set stringSet) { + this.stringSet = stringSet; + } + + public HashSet getStringHashSet() { + return stringHashSet; + } + + public void setStringHashSet(HashSet stringHashSet) { + this.stringHashSet = stringHashSet; + } + + public Collection getStringCollection() { + return stringCollection; + } + + public void setStringCollection(Collection stringCollection) { + this.stringCollection = stringCollection; + } + + public Collection getIntegerCollection() { + return integerCollection; + } + + public void setIntegerCollection(Collection integerCollection) { + this.integerCollection = integerCollection; + } }