mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#2233 Allow generic of generics in types when matching
e.g. method signature such as <T> T fromOptional(Optional<T> optional) when T resolves to another generic class such as Collection<String>
This commit is contained in:
parent
6102d0cc8e
commit
74d06fea5d
@ -296,8 +296,17 @@ public class MethodMatcher {
|
|||||||
public Boolean visitTypeVariable(TypeVariable t, TypeMirror p) {
|
public Boolean visitTypeVariable(TypeVariable t, TypeMirror p) {
|
||||||
if ( genericTypesMap.containsKey( t ) ) {
|
if ( genericTypesMap.containsKey( t ) ) {
|
||||||
// when already found, the same mapping should apply
|
// when already found, the same mapping should apply
|
||||||
|
// Then we should visit the resolved generic type.
|
||||||
|
// Which can potentially be another generic type
|
||||||
|
// e.g.
|
||||||
|
// <T> T fromOptional(Optional<T> optional)
|
||||||
|
// T resolves to Collection<Integer>
|
||||||
|
// We know what T resolves to, so we should treat it as if the method signature was
|
||||||
|
// Collection<Integer> fromOptional(Optional<Collection<Integer> optional)
|
||||||
TypeMirror p1 = genericTypesMap.get( t );
|
TypeMirror p1 = genericTypesMap.get( t );
|
||||||
return typeUtils.isSubtype( p, p1 );
|
// p (Integer) should be a subType of p1 (Number)
|
||||||
|
// i.e. you can assign p (Integer) to p1 (Number)
|
||||||
|
return visit( p, p1 );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// check if types are in bound
|
// check if types are in bound
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2233;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.tuple;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("2233")
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@WithClasses( {
|
||||||
|
Program.class,
|
||||||
|
ProgramAggregate.class,
|
||||||
|
ProgramDto.class,
|
||||||
|
ProgramMapper.class,
|
||||||
|
ProgramResponseDto.class,
|
||||||
|
} )
|
||||||
|
public class Issue2233Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCorrectlyMapFromOptionalToCollection() {
|
||||||
|
ProgramResponseDto response = ProgramMapper.INSTANCE.map( new ProgramAggregate( Collections.singleton(
|
||||||
|
new Program(
|
||||||
|
"Optional Mapping",
|
||||||
|
"123"
|
||||||
|
) ) ) );
|
||||||
|
|
||||||
|
assertThat( response ).isNotNull();
|
||||||
|
assertThat( response.getPrograms() ).isPresent();
|
||||||
|
assertThat( response.getPrograms().get() )
|
||||||
|
.extracting( ProgramDto::getName, ProgramDto::getNumber )
|
||||||
|
.containsExactly(
|
||||||
|
tuple( Optional.of( "Optional Mapping" ), Optional.of( "123" ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2233;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Program {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String number;
|
||||||
|
|
||||||
|
public Program(String name, String number) {
|
||||||
|
this.name = name;
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getName() {
|
||||||
|
return Optional.ofNullable( name );
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getNumber() {
|
||||||
|
return Optional.ofNullable( number );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2233;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class ProgramAggregate {
|
||||||
|
|
||||||
|
private final Collection<Program> programs;
|
||||||
|
|
||||||
|
public ProgramAggregate(Collection<Program> programs) {
|
||||||
|
this.programs = programs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Collection<Program>> getPrograms() {
|
||||||
|
return Optional.ofNullable( programs );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2233;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class ProgramDto {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String number;
|
||||||
|
|
||||||
|
public ProgramDto(String name, String number) {
|
||||||
|
this.name = name;
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getName() {
|
||||||
|
return Optional.ofNullable( name );
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getNumber() {
|
||||||
|
return Optional.ofNullable( number );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2233;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ProgramMapper {
|
||||||
|
|
||||||
|
ProgramMapper INSTANCE = Mappers.getMapper( ProgramMapper.class );
|
||||||
|
|
||||||
|
ProgramResponseDto map(ProgramAggregate programAggregate);
|
||||||
|
|
||||||
|
ProgramDto map(Program sourceProgramDto);
|
||||||
|
|
||||||
|
Collection<ProgramDto> mapPrograms(Collection<Program> sourcePrograms);
|
||||||
|
|
||||||
|
default <T> T fromOptional(Optional<T> optional) {
|
||||||
|
return optional.orElse( null );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2233;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class ProgramResponseDto {
|
||||||
|
|
||||||
|
private final Collection<ProgramDto> programs;
|
||||||
|
|
||||||
|
public ProgramResponseDto(Collection<ProgramDto> programs) {
|
||||||
|
this.programs = programs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Collection<ProgramDto>> getPrograms() {
|
||||||
|
return Optional.ofNullable( programs );
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user