#434 construct resultType based on @BeanMaping#resultType when no factory method selected

This commit is contained in:
sjaakd 2015-01-28 21:13:51 +01:00
parent 897c8fbb6d
commit f778d6c5ad
6 changed files with 150 additions and 8 deletions

View File

@ -35,10 +35,12 @@ import org.mapstruct.ap.model.PropertyMapping.JavaExpressionMappingBuilder;
import org.mapstruct.ap.model.PropertyMapping.PropertyMappingBuilder;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.BeanMapping;
import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.model.source.SourceReference;
import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.BeanMappingPrism;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.NullValueMappingPrism;
import org.mapstruct.ap.util.Executables;
@ -59,6 +61,7 @@ public class BeanMappingMethod extends MappingMethod {
private final List<PropertyMapping> constantMappings;
private final MethodReference factoryMethod;
private final boolean mapNullToDefault;
private final Type resultType;
public static class Builder {
@ -106,7 +109,26 @@ public class BeanMappingMethod extends MappingMethod {
MapperConfig.getInstanceOn( ctx.getMapperTypeElement() ).isMapToDefault( prism );
MethodReference factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, method.getResultType() );
return new BeanMappingMethod( method, propertyMappings, factoryMethod, mapNullToDefault );
// if there's no factory method, try the resultType in the @BeanMapping
Type resultType = null;
if ( factoryMethod == null ) {
BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() );
BeanMapping beanMapping
= BeanMapping.fromPrism( beanMappingPrism, method.getExecutable(), ctx.getMessager() );
if ( beanMapping != null && beanMapping.getResultType() != null ) {
resultType = ctx.getTypeFactory().getType( beanMapping.getResultType() );
if ( !resultType.isAssignableTo( method.getResultType() ) ) {
ctx.getMessager().printMessage(
Diagnostic.Kind.ERROR,
String.format( "%s not assignable to: %s.", resultType, method.getResultType() ),
method.getExecutable(),
beanMappingPrism.mirror );
}
}
}
return new BeanMappingMethod( method, propertyMappings, factoryMethod, mapNullToDefault, resultType );
}
/**
@ -429,7 +451,8 @@ public class BeanMappingMethod extends MappingMethod {
private BeanMappingMethod(SourceMethod method,
List<PropertyMapping> propertyMappings,
MethodReference factoryMethod,
boolean mapNullToDefault) {
boolean mapNullToDefault,
Type resultType ) {
super( method );
this.propertyMappings = propertyMappings;
@ -449,6 +472,7 @@ public class BeanMappingMethod extends MappingMethod {
}
this.factoryMethod = factoryMethod;
this.mapNullToDefault = mapNullToDefault;
this.resultType = resultType;
}
public List<PropertyMapping> getPropertyMappings() {
@ -467,6 +491,16 @@ public class BeanMappingMethod extends MappingMethod {
return mapNullToDefault;
}
@Override
public Type getResultType() {
if ( resultType == null ) {
return super.getResultType();
}
else {
return resultType;
}
}
@Override
public Set<Type> getImportTypes() {
Set<Type> types = super.getImportTypes();

View File

@ -24,6 +24,11 @@ package org.mapstruct.ap.test.selection.resulttype;
*/
public class Apple extends Fruit {
public Apple() {
super( "constructed-by-constructor" );
}
public Apple(String type) {
super( type );
}

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2015 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.selection.resulttype;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper
public interface ErroneousFruitMapper2 {
ErroneousFruitMapper2 INSTANCE = Mappers.getMapper( ErroneousFruitMapper2.class );
@BeanMapping(resultType = Banana.class)
@Mapping(target = "type", ignore = true)
Apple map(AppleDto source);
}

View File

@ -65,16 +65,42 @@ public class InheritanceSelectionTest {
}
@Test
@WithClasses( { ConflictingFruitFactory.class, TargetTypeSelectingFruitMapper.class, Banana.class } )
public void testForkedInheritanceHierarchyButDefinedTargetType() {
@WithClasses( { ConflictingFruitFactory.class, ResultTypeSelectingFruitMapper.class, Banana.class } )
public void testResultTypeBasedFactoryMethodSelection() {
FruitDto fruitDto = new FruitDto( null );
Fruit fruit = TargetTypeSelectingFruitMapper.INSTANCE.map( fruitDto );
Fruit fruit = ResultTypeSelectingFruitMapper.INSTANCE.map( fruitDto );
assertThat( fruit ).isNotNull();
assertThat( fruit.getType() ).isEqualTo( "apple" );
}
@Test
@IssueKey("434")
@WithClasses( { ResultTypeConstructingFruitMapper.class } )
public void testResultTypeBasedConstructionOfResult() {
FruitDto fruitDto = new FruitDto( null );
Fruit fruit = ResultTypeConstructingFruitMapper.INSTANCE.map( fruitDto );
assertThat( fruit ).isNotNull();
assertThat( fruit.getType() ).isEqualTo( "constructed-by-constructor" );
}
@Test
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousFruitMapper2.class,
kind = Kind.ERROR,
line = 35,
messageRegExp = ".*\\.Banana not assignable to: .*\\.Apple.")
}
)
@IssueKey("434")
@WithClasses( { ErroneousFruitMapper2.class, Banana.class } )
public void testResultTypeBasedConstructionOfResultNonAssignable() {
}
@Test
@IssueKey("433")
@WithClasses( {

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2015 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.selection.resulttype;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper
public interface ResultTypeConstructingFruitMapper {
ResultTypeConstructingFruitMapper INSTANCE = Mappers.getMapper( ResultTypeConstructingFruitMapper.class );
@BeanMapping(resultType = Apple.class)
@Mapping(target = "type", ignore = true)
Fruit map(FruitDto source);
}

View File

@ -28,10 +28,9 @@ import org.mapstruct.factory.Mappers;
* @author Sjaak Derksen
*/
@Mapper(uses = ConflictingFruitFactory.class)
public interface ResultTypeSelectingFruitMapper {
public interface TargetTypeSelectingFruitMapper {
TargetTypeSelectingFruitMapper INSTANCE = Mappers.getMapper( TargetTypeSelectingFruitMapper.class );
ResultTypeSelectingFruitMapper INSTANCE = Mappers.getMapper( ResultTypeSelectingFruitMapper.class );
@BeanMapping(resultType = Apple.class)
@Mapping(target = "type", ignore = true)