#1821 nullpointer due to @BeanMapping via inheritance (#1822)

This commit is contained in:
Sjaak Derksen 2019-09-22 19:25:43 +02:00 committed by GitHub
parent 1c4bbba442
commit ade4f4d7e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 199 additions and 101 deletions

View File

@ -42,7 +42,6 @@ import org.mapstruct.ap.internal.model.source.MappingOptions;
import org.mapstruct.ap.internal.model.source.Method;
import org.mapstruct.ap.internal.model.source.SelectionParameters;
import org.mapstruct.ap.internal.model.source.SourceMethod;
import org.mapstruct.ap.internal.prism.BeanMappingPrism;
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
import org.mapstruct.ap.internal.prism.ReportingPolicyPrism;
@ -393,7 +392,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
if ( resultType.isAbstract() ) {
ctx.getMessager().printMessage(
method.getExecutable(),
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
method.getMappingOptions().getBeanMapping().getMirror(),
BEANMAPPING_ABSTRACT,
resultType,
method.getResultType()
@ -403,7 +402,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
else if ( !resultType.isAssignableTo( method.getResultType() ) ) {
ctx.getMessager().printMessage(
method.getExecutable(),
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
method.getMappingOptions().getBeanMapping().getMirror(),
BEANMAPPING_NOT_ASSIGNABLE,
resultType,
method.getResultType()
@ -413,7 +412,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
else if ( !resultType.hasEmptyAccessibleConstructor() ) {
ctx.getMessager().printMessage(
method.getExecutable(),
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
method.getMappingOptions().getBeanMapping().getMirror(),
Message.GENERAL_NO_SUITABLE_CONSTRUCTOR,
resultType
);

View File

@ -6,11 +6,14 @@
package org.mapstruct.ap.internal.model.source;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.util.Types;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.prism.BeanMappingPrism;
import org.mapstruct.ap.internal.prism.BuilderPrism;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
@ -36,57 +39,101 @@ public class BeanMapping {
private final BuilderPrism builder;
private final NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy;
private final AnnotationMirror mirror;
/**
* creates a mapping for inheritance. Will set ignoreByDefault to false.
* creates a mapping for inheritance. Will set
* - ignoreByDefault to false.
* - resultType to null
*
* @param map
* @return
* @return new mapping
*/
public static BeanMapping forInheritance( BeanMapping map ) {
public static BeanMapping forInheritance(BeanMapping beanMapping) {
return new BeanMapping(
map.selectionParameters,
map.nullValueMappingStrategy,
map.nullValuePropertyMappingStrategy,
map.nullValueCheckStrategy,
map.reportingPolicy,
SelectionParameters.forInheritance( beanMapping.selectionParameters ),
beanMapping.nullValueMappingStrategy,
beanMapping.nullValuePropertyMappingStrategy,
beanMapping.nullValueCheckStrategy,
beanMapping.reportingPolicy,
false,
map.ignoreUnmappedSourceProperties,
map.builder
beanMapping.ignoreUnmappedSourceProperties,
beanMapping.builder,
beanMapping.mirror
);
}
public static BeanMapping fromPrism(BeanMappingPrism beanMapping, ExecutableElement method,
FormattingMessager messager, Types typeUtils) {
public static class Builder {
if ( beanMapping == null ) {
private BeanMappingPrism prism;
private ExecutableElement method;
private FormattingMessager messager;
private Types typeUtils;
private TypeFactory typeFactory;
public Builder beanMappingPrism(BeanMappingPrism beanMappingPrism) {
this.prism = beanMappingPrism;
return this;
}
public Builder method(ExecutableElement method) {
this.method = method;
return this;
}
public Builder messager(FormattingMessager messager) {
this.messager = messager;
return this;
}
public Builder typeUtils(Types typeUtils) {
this.typeUtils = typeUtils;
return this;
}
public Builder typeFactory(TypeFactory typeFactory) {
this.typeFactory = typeFactory;
return this;
}
public BeanMapping build() {
if ( prism == null ) {
return null;
}
boolean resultTypeIsDefined = !TypeKind.VOID.equals( beanMapping.resultType().getKind() );
Objects.requireNonNull( method );
Objects.requireNonNull( messager );
Objects.requireNonNull( method );
Objects.requireNonNull( typeUtils );
Objects.requireNonNull( typeFactory );
boolean resultTypeIsDefined = !TypeKind.VOID.equals( prism.resultType().getKind() );
NullValueMappingStrategyPrism nullValueMappingStrategy =
null == beanMapping.values.nullValueMappingStrategy()
null == prism.values.nullValueMappingStrategy()
? null
: NullValueMappingStrategyPrism.valueOf( beanMapping.nullValueMappingStrategy() );
: NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() );
NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy =
null == beanMapping.values.nullValuePropertyMappingStrategy()
null == prism.values.nullValuePropertyMappingStrategy()
? null
: NullValuePropertyMappingStrategyPrism.valueOf( beanMapping.nullValuePropertyMappingStrategy() );
:
NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() );
NullValueCheckStrategyPrism nullValueCheckStrategy =
null == beanMapping.values.nullValueCheckStrategy()
null == prism.values.nullValueCheckStrategy()
? null
: NullValueCheckStrategyPrism.valueOf( beanMapping.nullValueCheckStrategy() );
: NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() );
boolean ignoreByDefault = beanMapping.ignoreByDefault();
boolean ignoreByDefault = prism.ignoreByDefault();
BuilderPrism builderMapping = null;
if ( beanMapping.values.builder() != null ) {
builderMapping = beanMapping.builder();
if ( prism.values.builder() != null ) {
builderMapping = prism.builder();
}
if ( !resultTypeIsDefined && beanMapping.qualifiedBy().isEmpty() && beanMapping.qualifiedByName().isEmpty()
&& beanMapping.ignoreUnmappedSourceProperties().isEmpty()
if ( !resultTypeIsDefined && prism.qualifiedBy().isEmpty() &&
prism.qualifiedByName().isEmpty()
&& prism.ignoreUnmappedSourceProperties().isEmpty()
&& ( nullValueMappingStrategy == null ) && ( nullValuePropertyMappingStrategy == null )
&& ( nullValueCheckStrategy == null ) && !ignoreByDefault && builderMapping == null ) {
@ -94,9 +141,9 @@ public class BeanMapping {
}
SelectionParameters cmp = new SelectionParameters(
beanMapping.qualifiedBy(),
beanMapping.qualifiedByName(),
resultTypeIsDefined ? beanMapping.resultType() : null,
prism.qualifiedBy(),
prism.qualifiedByName(),
resultTypeIsDefined ? prism.resultType() : null,
typeUtils
);
@ -108,15 +155,18 @@ public class BeanMapping {
nullValueCheckStrategy,
null,
ignoreByDefault,
beanMapping.ignoreUnmappedSourceProperties(),
builderMapping
prism.ignoreUnmappedSourceProperties(),
builderMapping,
prism.mirror
);
}
}
private BeanMapping(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms,
NullValuePropertyMappingStrategyPrism nvpms, NullValueCheckStrategyPrism nvcs,
ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault,
List<String> ignoreUnmappedSourceProperties, BuilderPrism builder) {
List<String> ignoreUnmappedSourceProperties, BuilderPrism builder,
AnnotationMirror mirror) {
this.selectionParameters = selectionParameters;
this.nullValueMappingStrategy = nvms;
this.nullValuePropertyMappingStrategy = nvpms;
@ -125,6 +175,7 @@ public class BeanMapping {
this.ignoreByDefault = ignoreByDefault;
this.ignoreUnmappedSourceProperties = ignoreUnmappedSourceProperties;
this.builder = builder;
this.mirror = mirror;
}
public SelectionParameters getSelectionParameters() {
@ -155,6 +206,10 @@ public class BeanMapping {
return ignoreUnmappedSourceProperties;
}
public AnnotationMirror getMirror() {
return mirror;
}
/**
* derives the builder prism given the options and configuration
* @param method containing mandatory configuration and the mapping options (optionally containing a beanmapping)

View File

@ -1,36 +0,0 @@
/*
* 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.internal.model.source;
/**
* Represents the mapping between one enum constant and another.
*
* @author Gunnar Morling
*/
public class EnumMapping {
private final String source;
private final String target;
public EnumMapping(String source, String target) {
this.source = source;
this.target = target;
}
/**
* @return the name of the constant in the source enum.
*/
public String getSource() {
return source;
}
/**
* @return the name of the constant in the target enum.
*/
public String getTarget() {
return target;
}
}

View File

@ -26,6 +26,23 @@ public class SelectionParameters {
private final Types typeUtils;
private final SourceRHS sourceRHS;
/**
* Returns new selection parameters
*
* ResultType is not inherited.
*
* @param selectionParameters
* @return
*/
public static SelectionParameters forInheritance(SelectionParameters selectionParameters) {
return new SelectionParameters(
selectionParameters.qualifiers,
selectionParameters.qualifyingNames,
null,
selectionParameters.typeUtils
);
}
public SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames, TypeMirror resultType,
Types typeUtils) {
this( qualifiers, qualifyingNames, resultType, typeUtils, null );

View File

@ -254,7 +254,14 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
.setMapMapping(
MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) )
.setBeanMapping(
BeanMapping.fromPrism( BeanMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) )
new BeanMapping.Builder()
.beanMappingPrism( BeanMappingPrism.getInstanceOn( method ) )
.messager( messager )
.method( method )
.typeUtils( typeUtils )
.typeFactory( typeFactory )
.build()
)
.setValueMappings( getValueMappings( method ) )
.setTypeUtils( typeUtils )
.setTypeFactory( typeFactory )

View File

@ -0,0 +1,32 @@
/*
* 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._1821;
import org.mapstruct.BeanMapping;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface Issue1821Mapper {
Issue1821Mapper INSTANCE = Mappers.getMapper( Issue1821Mapper.class );
@BeanMapping( resultType = Target.class )
Target map(Source source);
@InheritConfiguration( name = "map" )
ExtendedTarget mapExtended(Source source);
class Target {
}
class ExtendedTarget extends Target {
}
class Source {
}
}

View File

@ -0,0 +1,24 @@
/*
* 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._1821;
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;
@IssueKey("1797")
@RunWith( AnnotationProcessorTestRunner.class)
@WithClasses( Issue1821Mapper.class )
public class Issue1821Test {
@Test
public void shouldNotGiveNullPtr() {
Issue1821Mapper.INSTANCE.map( new Issue1821Mapper.Source() );
}
}