#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,87 +39,134 @@ 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 ) {
return 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;
}
boolean resultTypeIsDefined = !TypeKind.VOID.equals( beanMapping.resultType().getKind() );
NullValueMappingStrategyPrism nullValueMappingStrategy =
null == beanMapping.values.nullValueMappingStrategy()
? null
: NullValueMappingStrategyPrism.valueOf( beanMapping.nullValueMappingStrategy() );
NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy =
null == beanMapping.values.nullValuePropertyMappingStrategy()
? null
: NullValuePropertyMappingStrategyPrism.valueOf( beanMapping.nullValuePropertyMappingStrategy() );
NullValueCheckStrategyPrism nullValueCheckStrategy =
null == beanMapping.values.nullValueCheckStrategy()
? null
: NullValueCheckStrategyPrism.valueOf( beanMapping.nullValueCheckStrategy() );
boolean ignoreByDefault = beanMapping.ignoreByDefault();
BuilderPrism builderMapping = null;
if ( beanMapping.values.builder() != null ) {
builderMapping = beanMapping.builder();
public Builder method(ExecutableElement method) {
this.method = method;
return this;
}
if ( !resultTypeIsDefined && beanMapping.qualifiedBy().isEmpty() && beanMapping.qualifiedByName().isEmpty()
&& beanMapping.ignoreUnmappedSourceProperties().isEmpty()
&& ( nullValueMappingStrategy == null ) && ( nullValuePropertyMappingStrategy == null )
&& ( nullValueCheckStrategy == null ) && !ignoreByDefault && builderMapping == null ) {
messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS );
public Builder messager(FormattingMessager messager) {
this.messager = messager;
return this;
}
SelectionParameters cmp = new SelectionParameters(
beanMapping.qualifiedBy(),
beanMapping.qualifiedByName(),
resultTypeIsDefined ? beanMapping.resultType() : null,
typeUtils
);
public Builder typeUtils(Types typeUtils) {
this.typeUtils = typeUtils;
return this;
}
//TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support?
return new BeanMapping(
cmp,
nullValueMappingStrategy,
nullValuePropertyMappingStrategy,
nullValueCheckStrategy,
null,
ignoreByDefault,
beanMapping.ignoreUnmappedSourceProperties(),
builderMapping
);
public Builder typeFactory(TypeFactory typeFactory) {
this.typeFactory = typeFactory;
return this;
}
public BeanMapping build() {
if ( prism == null ) {
return null;
}
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 == prism.values.nullValueMappingStrategy()
? null
: NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() );
NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy =
null == prism.values.nullValuePropertyMappingStrategy()
? null
:
NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() );
NullValueCheckStrategyPrism nullValueCheckStrategy =
null == prism.values.nullValueCheckStrategy()
? null
: NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() );
boolean ignoreByDefault = prism.ignoreByDefault();
BuilderPrism builderMapping = null;
if ( prism.values.builder() != null ) {
builderMapping = prism.builder();
}
if ( !resultTypeIsDefined && prism.qualifiedBy().isEmpty() &&
prism.qualifiedByName().isEmpty()
&& prism.ignoreUnmappedSourceProperties().isEmpty()
&& ( nullValueMappingStrategy == null ) && ( nullValuePropertyMappingStrategy == null )
&& ( nullValueCheckStrategy == null ) && !ignoreByDefault && builderMapping == null ) {
messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS );
}
SelectionParameters cmp = new SelectionParameters(
prism.qualifiedBy(),
prism.qualifiedByName(),
resultTypeIsDefined ? prism.resultType() : null,
typeUtils
);
//TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support?
return new BeanMapping(
cmp,
nullValueMappingStrategy,
nullValuePropertyMappingStrategy,
nullValueCheckStrategy,
null,
ignoreByDefault,
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() );
}
}