mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#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:
parent
22da3becab
commit
f9bfc4572e
@ -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.
|
* @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) {
|
public boolean isAssignableTo(Type other) {
|
||||||
if ( equals( other ) ) {
|
if ( equals( other ) ) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -27,6 +27,7 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.processing.Messager;
|
import javax.annotation.processing.Messager;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.TypeElement;
|
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(
|
return new IterableMappingMethod(
|
||||||
method,
|
method,
|
||||||
getMappingMethodReference( methods, sourceElementType, targetElementType ),
|
elementMappingMethod,
|
||||||
conversion
|
conversion
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -524,6 +541,31 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
|||||||
targetValueType
|
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 );
|
return new MapMappingMethod( method, keyMappingMethod, keyConversion, valueMappingMethod, valueConversion );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public <@includeModel object=returnType/> ${name}(<#list parameters as param><@i
|
|||||||
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
|
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
|
||||||
<#if elementMappingMethod??>
|
<#if elementMappingMethod??>
|
||||||
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
|
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
|
||||||
<#else>
|
<#elseif conversion??>
|
||||||
<#if (conversion.exceptionTypes?size == 0) >
|
<#if (conversion.exceptionTypes?size == 0) >
|
||||||
${resultName}.add( <@includeModel object=conversion/> );
|
${resultName}.add( <@includeModel object=conversion/> );
|
||||||
<#else>
|
<#else>
|
||||||
@ -47,6 +47,8 @@ public <@includeModel object=returnType/> ${name}(<#list parameters as param><@i
|
|||||||
}
|
}
|
||||||
</#list>
|
</#list>
|
||||||
</#if>
|
</#if>
|
||||||
|
<#else>
|
||||||
|
${resultName}.add( ${loopVariableName} );
|
||||||
</#if>
|
</#if>
|
||||||
}
|
}
|
||||||
<#if returnType.name != "void">
|
<#if returnType.name != "void">
|
||||||
|
@ -24,6 +24,7 @@ import java.util.EnumSet;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.mapstruct.ap.testutil.IssueKey;
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
import org.mapstruct.ap.testutil.MapperTestBase;
|
import org.mapstruct.ap.testutil.MapperTestBase;
|
||||||
@ -287,4 +288,14 @@ public class CollectionMappingTest extends MapperTestBase {
|
|||||||
|
|
||||||
assertThat( source.getStringLongMap() ).hasSize( 2 );
|
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,4 +46,6 @@ public interface SourceTargetMapper {
|
|||||||
Set<String> colourSetToStringSet(Set<Colour> colours);
|
Set<String> colourSetToStringSet(Set<Colour> colours);
|
||||||
|
|
||||||
Set<Colour> stringSetToColourSet(Set<String> colours);
|
Set<Colour> stringSetToColourSet(Set<String> colours);
|
||||||
|
|
||||||
|
Set<Number> integerSetToNumberSet(Set<Integer> integers);
|
||||||
}
|
}
|
||||||
|
@ -147,4 +147,19 @@ public class MapMappingTest extends MapperTestBase {
|
|||||||
entry( 121L, new GregorianCalendar( 2013, 6, 20 ).getTime() )
|
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" )
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,4 +47,6 @@ public interface SourceTargetMapper {
|
|||||||
Target sourceToTarget(Source source);
|
Target sourceToTarget(Source source);
|
||||||
|
|
||||||
Source targetToSource(Target target);
|
Source targetToSource(Target target);
|
||||||
|
|
||||||
|
Map<Object, Object> stringStringMapToObjectObjectMap(Map<String, String> source);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user