diff --git a/core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java b/core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java index 9f698633c..d7f6b7bf1 100644 --- a/core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java +++ b/core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java @@ -29,17 +29,6 @@ package org.mapstruct; public enum NullValueCheckStrategy { /** - * This option includes a null check. When: - *

- *

    - *
  1. a source value is directly assigned to a target
  2. - *
  3. a source value assigned to a target by calling a type conversion on the target first
  4. - *
- *

- * NOTE: mapping methods (generated or hand written) are excluded from this null check. They are intended to - * handle a null source value as 'valid' input. - * - *//** * This option includes a null check. When: *

*

    @@ -56,6 +45,6 @@ public enum NullValueCheckStrategy { /** * This option always includes a null check. */ - ALLWAYS, + ALWAYS, } diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc index bf39f7351..b2c91d712 100644 --- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc +++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc @@ -1512,6 +1512,33 @@ However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETU The strategy works in a hierarchical fashion. Setting `nullValueMappingStrategy` on mapping method level will override `@Mapper#nullValueMappingStrategy`, and `@Mapper#nullValueMappingStrategy` will override `@MappingConfig#nullValueMappingStrategy`. + +[[checking-source-property-for-null-arguments]] +=== Controlling checking result for 'null' properties in bean mapping + +MapStruct offers control over when to generate a `null` check. By default (`nullValueCheckStrategy = NullValueMappingStrategy.ON_IMPLICIT_CONVERSION`) a `null` check will be generated for: + +* direct setting of source value to target value when target is primitive and is source not. +* applying type conversion and then: +.. calling the setter on the target. +.. calling another type conversion and subsequently calling the setter on the target. +.. calling a mapping method and subsequently calling the setter on the target. + +First calling a mapping method on the source property is not protected by a null check. Therefor generated mapping methods will do a null check prior to carrying out mapping on a source property. Handwritten mapping methods must take care of null value checking. They have the possibility to add 'meaning' to `null`. For instance: mapping `null` to a default value. + +The option `nullValueCheckStrategy = NullValueMappingStrategy.ALWAYS` will always include a null check when source is non primitive, unless a source presence checker is defined on the source bean. + +The strategy works in a hierarchical fashion. `@Mapper#nullValueMappingStrategy` will override `@MappingConfig#nullValueMappingStrategy`. + +[[source-presence-check]] +=== Source presence checking +Some frameworks generate bean properties that have a source presence checker. Often this is in the form of a method `hasXYZ`, `XYZ` being a property on the source bean in a bean mapping method. MapStruct will call this `hasXYZ` instead of performing a `null` check when it finds such `hasXYZ` method. + +[TIP] +==== +The source presence checker name can be changed in the MapStruct service provider interface (SPI). It can also be deactivated in this way. +==== + [[exceptions]] === Exceptions diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index b13753571..42f2ac0d3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -23,7 +23,6 @@ import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentTy import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.MAPPED_TYPE_CONVERTED; import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED; import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED_MAPPED; -import static org.mapstruct.ap.internal.prism.NullValueCheckStrategy.ALLWAYS; import java.util.Arrays; import java.util.Collections; @@ -58,6 +57,7 @@ import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.MapperConfiguration; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; +import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS; /** * Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to @@ -344,7 +344,7 @@ public class PropertyMapping extends ModelElement { else if ( getSourcePresenceCheckerRef() != null ) { result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); } - else if ( ALLWAYS.equals( method.getMapperConfiguration().getNullValueCheckStrategy() ) ) { + else if ( ALWAYS.equals( method.getMapperConfiguration().getNullValueCheckStrategy() ) ) { result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); } else if ( result.getType() == TYPE_CONVERTED diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategy.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategyPrism.java similarity index 94% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategy.java rename to processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategyPrism.java index b9917b8f8..d59862ca7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategyPrism.java @@ -24,8 +24,8 @@ package org.mapstruct.ap.internal.prism; * * @author Sean Huang */ -public enum NullValueCheckStrategy { +public enum NullValueCheckStrategyPrism { ON_IMPLICIT_CONVERSION, - ALLWAYS; + ALWAYS; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java b/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java index 5bf917a98..d52b0d8e0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java @@ -32,7 +32,7 @@ import org.mapstruct.ap.internal.prism.MapperConfigPrism; import org.mapstruct.ap.internal.prism.MapperPrism; import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategy; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; /** * Provides an aggregated view to the settings given via {@link org.mapstruct.Mapper} and @@ -136,12 +136,12 @@ public class MapperConfiguration { } } - public NullValueCheckStrategy getNullValueCheckStrategy() { + public NullValueCheckStrategyPrism getNullValueCheckStrategy() { if ( mapperConfigPrism != null && mapperPrism.values.nullValueCheckStrategy() == null ) { - return NullValueCheckStrategy.valueOf( mapperConfigPrism.nullValueCheckStrategy() ); + return NullValueCheckStrategyPrism.valueOf( mapperConfigPrism.nullValueCheckStrategy() ); } else { - return NullValueCheckStrategy.valueOf( mapperPrism.nullValueCheckStrategy() ); + return NullValueCheckStrategyPrism.valueOf( mapperPrism.nullValueCheckStrategy() ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java index cee061e00..9c127a3aa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java @@ -27,7 +27,7 @@ import org.mapstruct.factory.Mappers; * * @author Sjaak Derksen */ -@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALLWAYS ) +@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS ) public abstract class RockFestivalMapper { public static final RockFestivalMapper INSTANCE = Mappers.getMapper( RockFestivalMapper.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java index 9cb829432..790ad035d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java @@ -24,7 +24,7 @@ import org.mapstruct.NullValueCheckStrategy; * * @author Sjaak Derksen */ -@MapperConfig( nullValueCheckStrategy = NullValueCheckStrategy.ALLWAYS ) +@MapperConfig( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS ) public interface RockFestivalMapperConfig { }