diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 652083406..d0f65ce97 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -17,6 +17,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -367,6 +371,15 @@ public class Type extends ModelElement implements Comparable { return componentType != null; } + private boolean isType(Class type) { + return type.getName().equals( getFullyQualifiedName() ); + } + + private boolean isOptionalType() { + return isType( Optional.class ) || isType( OptionalInt.class ) || isType( OptionalDouble.class ) || + isType( OptionalLong.class ); + } + public boolean isTypeVar() { return (typeMirror.getKind() == TypeKind.TYPEVAR); } @@ -1166,6 +1179,10 @@ public class Type extends ModelElement implements Comparable { * FTL. */ public String getNull() { + if ( isOptionalType() ) { + return createReferenceName() + ".empty()"; + } + if ( !isPrimitive() || isArrayType() ) { return "null"; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapper.java new file mode 100644 index 000000000..7397c2ccd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapper.java @@ -0,0 +1,20 @@ +/* + * 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.optional.nullvalue; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Dennis Melzer + */ +@Mapper +public interface OptionalDefaultMapper { + + OptionalDefaultMapper INSTANCE = Mappers.getMapper( OptionalDefaultMapper.class ); + + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapperTest.java new file mode 100644 index 000000000..78482831b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapperTest.java @@ -0,0 +1,53 @@ +/* + * 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.optional.nullvalue; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Dennis Melzer + */ +@WithClasses({ + OptionalDefaultMapper.class, + Source.class, + Target.class +}) +@IssueKey("3852") +public class OptionalDefaultMapperTest { + + @ProcessorTest + public void shouldOptionalNotNull() { + Source source = new Source( null, null, null, null, null, null, null ); + Target target = OptionalDefaultMapper.INSTANCE.map( source ); + + assertThat( target.getSomeString() ).isEmpty(); + assertThat( target.getSomeInteger() ).isEmpty(); + assertThat( target.getSomeDouble() ).isEmpty(); + assertThat( target.getSomeBoolean() ).isEmpty(); + assertThat( target.getSomeIntValue() ).isEmpty(); + assertThat( target.getSomeDoubleValue() ).isEmpty(); + assertThat( target.getSomeLongValue() ).isEmpty(); + } + + @ProcessorTest + public void shouldMapOptional() { + Source source = new Source( "someString", 10, 11D, Boolean.TRUE, 10, 100D, 200L ); + Target target = OptionalDefaultMapper.INSTANCE.map( source ); + + assertThat( target.getSomeString() ).contains( "someString" ); + assertThat( target.getSomeInteger() ).contains( 10 ); + assertThat( target.getSomeDouble() ).contains( 11D ); + assertThat( target.getSomeBoolean() ).contains( Boolean.TRUE ); + assertThat( target.getSomeIntValue() ).hasValue( 10 ); + assertThat( target.getSomeDoubleValue() ).hasValue( 100 ); + assertThat( target.getSomeLongValue() ).hasValue( 200 ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java new file mode 100644 index 000000000..158a01d3b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java @@ -0,0 +1,29 @@ +/* + * 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.optional.nullvalue; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.ap.test.nestedproperties.simple._target.TargetObject; +import org.mapstruct.ap.test.nestedproperties.simple.source.SourceRoot; +import org.mapstruct.factory.Mappers; + +/** + * @author Dennis Melzer + */ +@Mapper( + nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, + injectionStrategy = InjectionStrategy.CONSTRUCTOR, unmappedTargetPolicy = ReportingPolicy.IGNORE +) +public interface SimpleConstructorMapper { + + SimpleConstructorMapper MAPPER = Mappers.getMapper( SimpleConstructorMapper.class ); + + TargetObject toTargetObject(SourceRoot sourceRoot); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Source.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Source.java new file mode 100644 index 000000000..9746d4a15 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Source.java @@ -0,0 +1,61 @@ +/* + * 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.optional.nullvalue; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +public class Source { + + private final String someString; + private final Integer someInteger; + private final Double someDouble; + private final Boolean someBoolean; + private final Integer someIntValue; + private final Double someDoubleValue; + private final Long someLongValue; + + public Source(String someString, Integer someInteger, Double someDouble, Boolean someBoolean, Integer someIntValue, + Double someDoubleValue, Long someLongValue) { + this.someString = someString; + this.someInteger = someInteger; + this.someDouble = someDouble; + this.someBoolean = someBoolean; + this.someIntValue = someIntValue; + this.someDoubleValue = someDoubleValue; + this.someLongValue = someLongValue; + } + + public Optional getSomeString() { + return Optional.ofNullable( someString ); + } + + public Optional getSomeInteger() { + return Optional.ofNullable( someInteger ); + } + + public Optional getSomeDouble() { + return Optional.ofNullable( someDouble ); + } + + public Optional getSomeBoolean() { + return Optional.ofNullable( someBoolean ); + } + + public OptionalDouble getSomeDoubleValue() { + return someDouble != null ? OptionalDouble.of( someDoubleValue ) : OptionalDouble.empty(); + } + + public OptionalInt getSomeIntValue() { + return someIntValue != null ? OptionalInt.of( someIntValue ) : OptionalInt.empty(); + } + + public OptionalLong getSomeLongValue() { + return someLongValue != null ? OptionalLong.of( someLongValue ) : OptionalLong.empty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Target.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Target.java new file mode 100644 index 000000000..5fd5d1730 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Target.java @@ -0,0 +1,63 @@ +/* + * 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.optional.nullvalue; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class Target { + + private final Optional someString; + private final Optional someInteger; + private final Optional someDouble; + private final Optional someBoolean; + private final OptionalInt someIntValue; + private final OptionalDouble someDoubleValue; + private final OptionalLong someLongValue; + + public Target(Optional someString, Optional someInteger, Optional someDouble, + Optional someBoolean, OptionalInt someIntValue, OptionalDouble someDoubleValue, + OptionalLong someLongValue) { + this.someString = someString; + this.someInteger = someInteger; + this.someDouble = someDouble; + this.someBoolean = someBoolean; + this.someIntValue = someIntValue; + this.someDoubleValue = someDoubleValue; + this.someLongValue = someLongValue; + } + + public Optional getSomeString() { + return someString; + } + + public Optional getSomeInteger() { + return someInteger; + } + + public Optional getSomeDouble() { + return someDouble; + } + + public Optional getSomeBoolean() { + return someBoolean; + } + + public OptionalLong getSomeLongValue() { + return someLongValue; + } + + public OptionalDouble getSomeDoubleValue() { + return someDoubleValue; + } + + public OptionalInt getSomeIntValue() { + return someIntValue; + } +}