#87 Supporting iterable mapping methods where the target element type is the same as or a super-type of the source element type; Raising an error if iterable or map mapping methods can't be generated

This commit is contained in:
Gunnar Morling 2014-01-06 00:00:23 +01:00
parent 22da3becab
commit f9bfc4572e
7 changed files with 78 additions and 2 deletions

View File

@ -188,6 +188,8 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
*
* @return {@code true} if and only if this type is assignable to the given other type.
*/
// TODO This doesn't yet take wild card types into account; e.g. ? extends Integer wouldn't be assignable to Number
// atm.
public boolean isAssignableTo(Type other) {
if ( equals( other ) ) {
return true;

View File

@ -27,6 +27,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.Messager;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
@ -490,9 +491,25 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
)
);
MappingMethodReference elementMappingMethod =
getMappingMethodReference( methods, sourceElementType, targetElementType );
if ( !sourceElementType.isAssignableTo( targetElementType ) && conversion == null &&
elementMappingMethod == null ) {
messager.printMessage(
Kind.ERROR,
String.format(
"Can't create implementation of method %s. Found no method nor built-in conversion for mapping "
+ "source element type into target element type.",
method
),
method.getExecutable()
);
}
return new IterableMappingMethod(
method,
getMappingMethodReference( methods, sourceElementType, targetElementType ),
elementMappingMethod,
conversion
);
}
@ -524,6 +541,31 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
targetValueType
);
if ( !sourceKeyType.isAssignableTo( targetKeyType ) && keyConversion == null && keyMappingMethod == null ) {
messager.printMessage(
Kind.ERROR,
String.format(
"Can't create implementation of method %s. Found no method nor built-in conversion for mapping "
+ "source key type to target key type.",
method
),
method.getExecutable()
);
}
if ( !sourceValueType.isAssignableTo( targetValueType ) && valueConversion == null &&
valueMappingMethod == null ) {
messager.printMessage(
Kind.ERROR,
String.format(
"Can't create implementation of method %s. Found no method nor built-in conversion for mapping "
+ "source value type to target value type.",
method
),
method.getExecutable()
);
}
return new MapMappingMethod( method, keyMappingMethod, keyConversion, valueMappingMethod, valueConversion );
}

View File

@ -34,7 +34,7 @@ public <@includeModel object=returnType/> ${name}(<#list parameters as param><@i
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
<#if elementMappingMethod??>
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
<#else>
<#elseif conversion??>
<#if (conversion.exceptionTypes?size == 0) >
${resultName}.add( <@includeModel object=conversion/> );
<#else>
@ -47,6 +47,8 @@ public <@includeModel object=returnType/> ${name}(<#list parameters as param><@i
}
</#list>
</#if>
<#else>
${resultName}.add( ${loopVariableName} );
</#if>
}
<#if returnType.name != "void">

View File

@ -24,6 +24,7 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.MapperTestBase;
@ -287,4 +288,14 @@ public class CollectionMappingTest extends MapperTestBase {
assertThat( source.getStringLongMap() ).hasSize( 2 );
}
@Test
@IssueKey("87")
public void shouldMapIntegerSetToNumberSet() {
Set<Number> numbers = SourceTargetMapper.INSTANCE
.integerSetToNumberSet( new HashSet<Integer>( Arrays.asList( 123, 456 ) ) );
assertThat( numbers ).isNotNull();
assertThat( numbers ).containsOnly( 123, 456 );
}
}

View File

@ -46,4 +46,6 @@ public interface SourceTargetMapper {
Set<String> colourSetToStringSet(Set<Colour> colours);
Set<Colour> stringSetToColourSet(Set<String> colours);
Set<Number> integerSetToNumberSet(Set<Integer> integers);
}

View File

@ -147,4 +147,19 @@ public class MapMappingTest extends MapperTestBase {
entry( 121L, new GregorianCalendar( 2013, 6, 20 ).getTime() )
);
}
@Test
@IssueKey("87")
public void shouldCreateMapMethodImplementationWithoutConversionOrElementMappingMethod() {
Map<String, String> values = createStringStringMap();
Map<Object, Object> target = SourceTargetMapper.INSTANCE.stringStringMapToObjectObjectMap( values );
assertThat( target ).isNotNull();
assertThat( target ).hasSize( 2 );
assertThat( target ).includes(
entry( "42", "01.01.1980" ),
entry( "121", "20.07.2013" )
);
}
}

View File

@ -47,4 +47,6 @@ public interface SourceTargetMapper {
Target sourceToTarget(Source source);
Source targetToSource(Target target);
Map<Object, Object> stringStringMapToObjectObjectMap(Map<String, String> source);
}