#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.PropertyMapping.PropertyMappingBuilder;
import org.mapstruct.ap.model.common.Parameter; import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type; 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.Mapping;
import org.mapstruct.ap.model.source.SourceMethod; import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.model.source.SourceReference; import org.mapstruct.ap.model.source.SourceReference;
import org.mapstruct.ap.option.ReportingPolicy; import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.BeanMappingPrism;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.NullValueMappingPrism; import org.mapstruct.ap.prism.NullValueMappingPrism;
import org.mapstruct.ap.util.Executables; import org.mapstruct.ap.util.Executables;
@ -59,6 +61,7 @@ public class BeanMappingMethod extends MappingMethod {
private final List<PropertyMapping> constantMappings; private final List<PropertyMapping> constantMappings;
private final MethodReference factoryMethod; private final MethodReference factoryMethod;
private final boolean mapNullToDefault; private final boolean mapNullToDefault;
private final Type resultType;
public static class Builder { public static class Builder {
@ -106,7 +109,26 @@ public class BeanMappingMethod extends MappingMethod {
MapperConfig.getInstanceOn( ctx.getMapperTypeElement() ).isMapToDefault( prism ); MapperConfig.getInstanceOn( ctx.getMapperTypeElement() ).isMapToDefault( prism );
MethodReference factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, method.getResultType() ); 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, private BeanMappingMethod(SourceMethod method,
List<PropertyMapping> propertyMappings, List<PropertyMapping> propertyMappings,
MethodReference factoryMethod, MethodReference factoryMethod,
boolean mapNullToDefault) { boolean mapNullToDefault,
Type resultType ) {
super( method ); super( method );
this.propertyMappings = propertyMappings; this.propertyMappings = propertyMappings;
@ -449,6 +472,7 @@ public class BeanMappingMethod extends MappingMethod {
} }
this.factoryMethod = factoryMethod; this.factoryMethod = factoryMethod;
this.mapNullToDefault = mapNullToDefault; this.mapNullToDefault = mapNullToDefault;
this.resultType = resultType;
} }
public List<PropertyMapping> getPropertyMappings() { public List<PropertyMapping> getPropertyMappings() {
@ -467,6 +491,16 @@ public class BeanMappingMethod extends MappingMethod {
return mapNullToDefault; return mapNullToDefault;
} }
@Override
public Type getResultType() {
if ( resultType == null ) {
return super.getResultType();
}
else {
return resultType;
}
}
@Override @Override
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
Set<Type> types = super.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 class Apple extends Fruit {
public Apple() {
super( "constructed-by-constructor" );
}
public Apple(String type) { public Apple(String type) {
super( 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 @Test
@WithClasses( { ConflictingFruitFactory.class, TargetTypeSelectingFruitMapper.class, Banana.class } ) @WithClasses( { ConflictingFruitFactory.class, ResultTypeSelectingFruitMapper.class, Banana.class } )
public void testForkedInheritanceHierarchyButDefinedTargetType() { public void testResultTypeBasedFactoryMethodSelection() {
FruitDto fruitDto = new FruitDto( null ); FruitDto fruitDto = new FruitDto( null );
Fruit fruit = TargetTypeSelectingFruitMapper.INSTANCE.map( fruitDto ); Fruit fruit = ResultTypeSelectingFruitMapper.INSTANCE.map( fruitDto );
assertThat( fruit ).isNotNull(); assertThat( fruit ).isNotNull();
assertThat( fruit.getType() ).isEqualTo( "apple" ); 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 @Test
@IssueKey("433") @IssueKey("433")
@WithClasses( { @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 * @author Sjaak Derksen
*/ */
@Mapper(uses = ConflictingFruitFactory.class) @Mapper(uses = ConflictingFruitFactory.class)
public interface ResultTypeSelectingFruitMapper {
public interface TargetTypeSelectingFruitMapper { ResultTypeSelectingFruitMapper INSTANCE = Mappers.getMapper( ResultTypeSelectingFruitMapper.class );
TargetTypeSelectingFruitMapper INSTANCE = Mappers.getMapper( TargetTypeSelectingFruitMapper.class );
@BeanMapping(resultType = Apple.class) @BeanMapping(resultType = Apple.class)
@Mapping(target = "type", ignore = true) @Mapping(target = "type", ignore = true)