#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.mappingMethod = mappingMethod;
this.reverseMappingMethod = reverseMappingMethod;
this.isIterableMapping = mappingMethod.getElementMappingMethod() != null;
this.isIterableMapping = sourceType.isIterableType() && targetType.isIterableType();
}
public Type getSourceType() {
@ -56,7 +56,7 @@ public class BeanMapping {
return reverseMappingMethod;
}
public boolean getIterableMapping() {
public boolean isIterableMapping() {
return isIterableMapping;
}

View File

@ -35,20 +35,26 @@ public class Type {
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>();
static {
defaultCollectionImplementationTypes.put( List.class.getName(), forClass( ArrayList.class ) );
defaultCollectionImplementationTypes.put( Set.class.getName(), forClass( HashSet.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 name;
private final Type elementType;
private final boolean isEnumType;
private final Type implementingType;
private final boolean isCollectionType;
private final boolean isIterableType;
private final Type collectionImplementationType;
private final Type iterableImplementationType;
public static Type forClass(Class<?> clazz) {
Package pakkage = clazz.getPackage();
@ -59,7 +65,8 @@ public class Type {
clazz.getSimpleName(),
null,
clazz.isEnum(),
Collection.class.isAssignableFrom( clazz )
Collection.class.isAssignableFrom( clazz ),
Iterable.class.isAssignableFrom( clazz )
);
}
else {
@ -68,16 +75,30 @@ public class Type {
}
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.name = name;
this.elementType = elementType;
this.isEnumType = isEnumType;
implementingType = defaultCollectionImplementationTypes.get( packageName + "." + name );
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() {
@ -100,14 +121,22 @@ public class Type {
return isEnumType;
}
public Type getImplementingType() {
return implementingType;
public Type getCollectionImplementationType() {
return collectionImplementationType;
}
public Type getIterableImplementationType() {
return iterableImplementationType;
}
public boolean isCollectionType() {
return isCollectionType;
}
public boolean isIterableType() {
return isIterableType;
}
@Override
public String toString() {
if ( packageName == null ) {

View File

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

View File

@ -44,7 +44,8 @@ public class ${implementationName} implements ${interfaceName} {
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} ) {
${beanMapping.targetType.name?uncap_first}.add( ${beanMapping.mappingMethod.elementMappingMethod.name}( ${beanMapping.sourceType.elementType.name?uncap_first} ) );
@ -77,7 +78,7 @@ public class ${implementationName} implements ${interfaceName} {
<#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><#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>
${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;
}
${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} ) {
${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>
<#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><#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>
${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.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 Iterable<SourceFoo> fooIterable;
public List<SourceFoo> getFooList() {
return fooList;
}
@ -50,4 +52,12 @@ public class Source {
public void setFooCollection(Collection<SourceFoo> 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;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@ -37,4 +36,6 @@ public interface SourceTargetMapper {
Set<TargetFoo> sourceFoosToTargetFoos(Set<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 Iterable<TargetFoo> fooIterable;
public List<TargetFoo> getFooList() {
return fooList;
}
@ -50,4 +52,12 @@ public class Target {
public void setFooCollection(Collection<TargetFoo> fooCollection) {
this.fooCollection = fooCollection;
}
public Iterable<TargetFoo> getFooIterable() {
return fooIterable;
}
public void setFooIterable(Iterable<TargetFoo> fooIterable) {
this.fooIterable = fooIterable;
}
}