#6 Support mapping of java.lang.Iterable

This commit is contained in:
Gunnar Morling 2013-05-03 19:31:21 +02:00
parent 07c1009c6b
commit d404775519
8 changed files with 91 additions and 21 deletions

View File

@ -33,7 +33,7 @@ public class BeanMapping {
this.propertyMappings = propertyMappings; this.propertyMappings = propertyMappings;
this.mappingMethod = mappingMethod; this.mappingMethod = mappingMethod;
this.reverseMappingMethod = reverseMappingMethod; this.reverseMappingMethod = reverseMappingMethod;
this.isIterableMapping = mappingMethod.getElementMappingMethod() != null; this.isIterableMapping = sourceType.isIterableType() && targetType.isIterableType();
} }
public Type getSourceType() { public Type getSourceType() {
@ -56,7 +56,7 @@ public class BeanMapping {
return reverseMappingMethod; return reverseMappingMethod;
} }
public boolean getIterableMapping() { public boolean isIterableMapping() {
return isIterableMapping; return isIterableMapping;
} }

View File

@ -35,20 +35,26 @@ public class Type {
Arrays.asList( "boolean", "char", "byte", "short", "int", "long", "float", "double" ) Arrays.asList( "boolean", "char", "byte", "short", "int", "long", "float", "double" )
); );
private final static ConcurrentMap<String, Type> defaultIterableImplementationTypes = new ConcurrentHashMap<String, Type>();
private final static ConcurrentMap<String, Type> defaultCollectionImplementationTypes = new ConcurrentHashMap<String, Type>(); private final static ConcurrentMap<String, Type> defaultCollectionImplementationTypes = new ConcurrentHashMap<String, Type>();
static { static {
defaultCollectionImplementationTypes.put( List.class.getName(), forClass( ArrayList.class ) ); defaultCollectionImplementationTypes.put( List.class.getName(), forClass( ArrayList.class ) );
defaultCollectionImplementationTypes.put( Set.class.getName(), forClass( HashSet.class ) ); defaultCollectionImplementationTypes.put( Set.class.getName(), forClass( HashSet.class ) );
defaultCollectionImplementationTypes.put( Collection.class.getName(), forClass( ArrayList.class ) ); defaultCollectionImplementationTypes.put( Collection.class.getName(), forClass( ArrayList.class ) );
defaultIterableImplementationTypes.put( Iterable.class.getName(), forClass( ArrayList.class ) );
defaultIterableImplementationTypes.putAll( defaultCollectionImplementationTypes );
} }
private final String packageName; private final String packageName;
private final String name; private final String name;
private final Type elementType; private final Type elementType;
private final boolean isEnumType; private final boolean isEnumType;
private final Type implementingType;
private final boolean isCollectionType; private final boolean isCollectionType;
private final boolean isIterableType;
private final Type collectionImplementationType;
private final Type iterableImplementationType;
public static Type forClass(Class<?> clazz) { public static Type forClass(Class<?> clazz) {
Package pakkage = clazz.getPackage(); Package pakkage = clazz.getPackage();
@ -59,7 +65,8 @@ public class Type {
clazz.getSimpleName(), clazz.getSimpleName(),
null, null,
clazz.isEnum(), clazz.isEnum(),
Collection.class.isAssignableFrom( clazz ) Collection.class.isAssignableFrom( clazz ),
Iterable.class.isAssignableFrom( clazz )
); );
} }
else { else {
@ -68,16 +75,30 @@ public class Type {
} }
public Type(String name) { public Type(String name) {
this( null, name, null, false, false ); this( null, name, null, false, false, false );
} }
public Type(String packageName, String name, Type elementType, boolean isEnumType, boolean isCollectionType) { public Type(String packageName, String name, Type elementType, boolean isEnumType, boolean isCollectionType, boolean isIterableType) {
this.packageName = packageName; this.packageName = packageName;
this.name = name; this.name = name;
this.elementType = elementType; this.elementType = elementType;
this.isEnumType = isEnumType; this.isEnumType = isEnumType;
implementingType = defaultCollectionImplementationTypes.get( packageName + "." + name );
this.isCollectionType = isCollectionType; this.isCollectionType = isCollectionType;
this.isIterableType = isIterableType;
if ( isCollectionType ) {
collectionImplementationType = defaultCollectionImplementationTypes.get( packageName + "." + name );
}
else {
collectionImplementationType = null;
}
if ( isIterableType ) {
iterableImplementationType = defaultIterableImplementationTypes.get( packageName + "." + name );
}
else {
iterableImplementationType = null;
}
} }
public String getPackageName() { public String getPackageName() {
@ -100,14 +121,22 @@ public class Type {
return isEnumType; return isEnumType;
} }
public Type getImplementingType() { public Type getCollectionImplementationType() {
return implementingType; return collectionImplementationType;
}
public Type getIterableImplementationType() {
return iterableImplementationType;
} }
public boolean isCollectionType() { public boolean isCollectionType() {
return isCollectionType; return isCollectionType;
} }
public boolean isIterableType() {
return isIterableType;
}
@Override @Override
public String toString() { public String toString() {
if ( packageName == null ) { if ( packageName == null ) {

View File

@ -29,18 +29,21 @@ public class TypeUtil {
private final Elements elementUtils; private final Elements elementUtils;
private final Types typeUtils; private final Types typeUtils;
private TypeMirror collectionType; private final TypeMirror collectionType;
private final TypeMirror iterableType;
public TypeUtil(Elements elementUtils, Types typeUtils) { public TypeUtil(Elements elementUtils, Types typeUtils) {
this.elementUtils = elementUtils; this.elementUtils = elementUtils;
this.typeUtils = typeUtils; this.typeUtils = typeUtils;
collectionType = elementUtils.getTypeElement( Collection.class.getCanonicalName() ).asType(); collectionType = elementUtils.getTypeElement( Collection.class.getCanonicalName() ).asType();
iterableType = elementUtils.getTypeElement( Iterable.class.getCanonicalName() ).asType();
} }
public Type getType(DeclaredType type) { public Type getType(DeclaredType type) {
Type elementType = null; Type elementType = null;
if ( isIterableType( type ) && !type.getTypeArguments().isEmpty() ) { boolean isIterableType = isIterableType( type );
if ( isIterableType && !type.getTypeArguments().isEmpty() ) {
elementType = retrieveType( type.getTypeArguments().iterator().next() ); elementType = retrieveType( type.getTypeArguments().iterator().next() );
} }
@ -49,13 +52,17 @@ public class TypeUtil {
type.asElement().getSimpleName().toString(), type.asElement().getSimpleName().toString(),
elementType, elementType,
type.asElement().getKind() == ElementKind.ENUM, type.asElement().getKind() == ElementKind.ENUM,
typeUtils.isAssignable( typeUtils.erasure( type ), typeUtils.erasure( collectionType ) ) isCollectionType( type ),
isIterableType
); );
} }
private boolean isIterableType(DeclaredType type) { private boolean isIterableType(DeclaredType type) {
TypeMirror iterableType = typeUtils.getDeclaredType( elementUtils.getTypeElement( Iterable.class.getCanonicalName() ) ); return typeUtils.isAssignable( typeUtils.erasure( type ), typeUtils.erasure( iterableType ) );
return typeUtils.isSubtype( type, iterableType ); }
private boolean isCollectionType(DeclaredType type) {
return typeUtils.isAssignable( typeUtils.erasure( type ), typeUtils.erasure( collectionType ) );
} }
public Type retrieveType(TypeMirror mirror) { public Type retrieveType(TypeMirror mirror) {

View File

@ -44,12 +44,13 @@ public class ${implementationName} implements ${interfaceName} {
return null; return null;
} }
${beanMapping.targetType.name}<${beanMapping.targetType.elementType.name}> ${beanMapping.targetType.name?uncap_first} = new <#if beanMapping.targetType.implementingType??>${beanMapping.targetType.implementingType.name}<#else>${beanMapping.targetType.name}</#if><${beanMapping.targetType.elementType.name}>(); <#-- 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.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} ) { for ( ${beanMapping.sourceType.elementType.name} ${beanMapping.sourceType.elementType.name?uncap_first} : ${beanMapping.mappingMethod.parameterName} ) {
${beanMapping.targetType.name?uncap_first}.add( ${beanMapping.mappingMethod.elementMappingMethod.name}( ${beanMapping.sourceType.elementType.name?uncap_first} ) ); ${beanMapping.targetType.name?uncap_first}.add( ${beanMapping.mappingMethod.elementMappingMethod.name}( ${beanMapping.sourceType.elementType.name?uncap_first} ) );
} }
return ${beanMapping.targetType.name?uncap_first}; return ${beanMapping.targetType.name?uncap_first};
} }
<#else> <#else>
@ -77,7 +78,7 @@ public class ${implementationName} implements ${interfaceName} {
<#else> <#else>
<#if propertyMapping.targetType.collectionType == true> <#if propertyMapping.targetType.collectionType == true>
if ( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() != null ) { 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><#if propertyMapping.targetType.elementType??><${propertyMapping.targetType.elementType.name}></#if>( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() ) ); ${beanMapping.targetType.name?uncap_first}.set${propertyMapping.targetName?cap_first}( new <#if propertyMapping.targetType.collectionImplementationType??>${propertyMapping.targetType.collectionImplementationType.name}<#else>${propertyMapping.targetType.name}</#if><#if propertyMapping.targetType.elementType??><${propertyMapping.targetType.elementType.name}></#if>( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() ) );
} }
<#else> <#else>
${beanMapping.targetType.name?uncap_first}.set${propertyMapping.targetName?cap_first}( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() ); ${beanMapping.targetType.name?uncap_first}.set${propertyMapping.targetName?cap_first}( ${beanMapping.mappingMethod.parameterName}.get${propertyMapping.sourceName?cap_first}() );
@ -99,7 +100,8 @@ public class ${implementationName} implements ${interfaceName} {
return null; return null;
} }
${beanMapping.sourceType.name}<${beanMapping.sourceType.elementType.name}> ${beanMapping.sourceType.name?uncap_first} = new <#if beanMapping.sourceType.implementingType??>${beanMapping.sourceType.implementingType.name}<#else>${beanMapping.sourceType.name}</#if><${beanMapping.sourceType.elementType.name}>(); <#-- 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} ) { for ( ${beanMapping.targetType.elementType.name} ${beanMapping.targetType.elementType.name?uncap_first} : ${beanMapping.reverseMappingMethod.parameterName} ) {
${beanMapping.sourceType.name?uncap_first}.add( ${beanMapping.reverseMappingMethod.elementMappingMethod.name}( ${beanMapping.targetType.elementType.name?uncap_first} ) ); ${beanMapping.sourceType.name?uncap_first}.add( ${beanMapping.reverseMappingMethod.elementMappingMethod.name}( ${beanMapping.targetType.elementType.name?uncap_first} ) );
@ -132,7 +134,7 @@ public class ${implementationName} implements ${interfaceName} {
<#else> <#else>
<#if propertyMapping.sourceType.collectionType == true> <#if propertyMapping.sourceType.collectionType == true>
if ( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() != null ) { 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><#if propertyMapping.sourceType.elementType??><${propertyMapping.sourceType.elementType.name}></#if>( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() ) ); ${beanMapping.sourceType.name?uncap_first}.set${propertyMapping.sourceName?cap_first}( new <#if propertyMapping.sourceType.collectionImplementationType??>${propertyMapping.sourceType.collectionImplementationType.name}<#else>${propertyMapping.sourceType.name}</#if><#if propertyMapping.sourceType.elementType??><${propertyMapping.sourceType.elementType.name}></#if>( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() ) );
} }
<#else> <#else>
${beanMapping.sourceType.name?uncap_first}.set${propertyMapping.sourceName?cap_first}( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() ); ${beanMapping.sourceType.name?uncap_first}.set${propertyMapping.sourceName?cap_first}( ${beanMapping.reverseMappingMethod.parameterName}.get${propertyMapping.targetName?cap_first}() );

View File

@ -70,4 +70,15 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
assertThat( target.getFooCollection() ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) ); assertThat( target.getFooCollection() ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) );
} }
@Test
@IssueKey("6")
public void shouldUseDefaultImplementationForIterable() {
Source source = new Source();
source.setFooIterable( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFooIterable() ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) );
}
} }

View File

@ -27,6 +27,8 @@ public class Source {
private Collection<SourceFoo> fooCollection; private Collection<SourceFoo> fooCollection;
private Iterable<SourceFoo> fooIterable;
public List<SourceFoo> getFooList() { public List<SourceFoo> getFooList() {
return fooList; return fooList;
} }
@ -50,4 +52,12 @@ public class Source {
public void setFooCollection(Collection<SourceFoo> fooCollection) { public void setFooCollection(Collection<SourceFoo> fooCollection) {
this.fooCollection = fooCollection; this.fooCollection = fooCollection;
} }
public Iterable<SourceFoo> getFooIterable() {
return fooIterable;
}
public void setFooIterable(Iterable<SourceFoo> fooIterable) {
this.fooIterable = fooIterable;
}
} }

View File

@ -15,7 +15,6 @@
*/ */
package org.mapstruct.ap.test.collection.defaultimplementation; package org.mapstruct.ap.test.collection.defaultimplementation;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -37,4 +36,6 @@ public interface SourceTargetMapper {
Set<TargetFoo> sourceFoosToTargetFoos(Set<SourceFoo> foos); Set<TargetFoo> sourceFoosToTargetFoos(Set<SourceFoo> foos);
Collection<TargetFoo> sourceFoosToTargetFoos(Collection<SourceFoo> foos); Collection<TargetFoo> sourceFoosToTargetFoos(Collection<SourceFoo> foos);
Iterable<TargetFoo> sourceFoosToTargetFoos(Iterable<SourceFoo> foos);
} }

View File

@ -27,6 +27,8 @@ public class Target {
private Collection<TargetFoo> fooCollection; private Collection<TargetFoo> fooCollection;
private Iterable<TargetFoo> fooIterable;
public List<TargetFoo> getFooList() { public List<TargetFoo> getFooList() {
return fooList; return fooList;
} }
@ -50,4 +52,12 @@ public class Target {
public void setFooCollection(Collection<TargetFoo> fooCollection) { public void setFooCollection(Collection<TargetFoo> fooCollection) {
this.fooCollection = fooCollection; this.fooCollection = fooCollection;
} }
public Iterable<TargetFoo> getFooIterable() {
return fooIterable;
}
public void setFooIterable(Iterable<TargetFoo> fooIterable) {
this.fooIterable = fooIterable;
}
} }