Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,12 @@ private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs) {
return true;
}

if ( rhs.getSourceType().isOptionalType() ) {
// If the source is an Optional then we need a presence check before calling Optional#get(),
// otherwise the local var assignment throws NoSuchElementException for an empty Optional.
return true;
}

if ( nvcs == ALWAYS ) {
// NullValueCheckStrategy is ALWAYS -> do a null check
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@
<#--
macro: handleLocalVarNullCheck

purpose: macro surrounds nested with either a source presence checker or a null check. It always uses
a local variable. Note that the local variable assignemnt is inside the IF statement for the
source presence check. Note also, that the else clause contains the default variable assignment if
present.
purpose: macro surrounds nested with either a source presence checker, an Optional#isPresent() check
(when the source is an Optional, so the local var assignment doesn't call Optional#get() on an
empty Optional), or a null check. It always uses a local variable. Note that the local variable
assignemnt is inside the IF statement for the source presence check. Note also, that the else
clause contains the default variable assignment if present.

requires: caller to implement String:getNullCheckLocalVarName()
caller to implement Type:getNullCheckLocalVarType()
Expand All @@ -73,6 +74,11 @@
<#nested>
</#if>
}
<#elseif sourceType.optionalType>
if ( <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference}</#if>.isPresent() ) {
<@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>;
<#nested>
}
<#else>
<@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>;
if ( ${nullCheckLocalVarName} != null ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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._4111;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface Issue4111Mapper {

Issue4111Mapper INSTANCE = Mappers.getMapper( Issue4111Mapper.class );

Target toTarget(Source source);

class Source {
private Optional<String> name = Optional.empty();
private Optional<Map<String, String>> attributes = Optional.empty();
private Optional<List<String>> tags = Optional.empty();

public Optional<String> getName() {
return name;
}

public void setName(Optional<String> name) {
this.name = name;
}

public Optional<Map<String, String>> getAttributes() {
return attributes;
}

public void setAttributes(Optional<Map<String, String>> attributes) {
this.attributes = attributes;
}

public Optional<List<String>> getTags() {
return tags;
}

public void setTags(Optional<List<String>> tags) {
this.tags = tags;
}
}

class Target {
private String name;
private Map<String, String> attributes;
private List<String> tags;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Map<String, String> getAttributes() {
return attributes;
}

public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}

public List<String> getTags() {
return tags;
}

public void setTags(List<String> tags) {
this.tags = tags;
}
}
}
Original file line number Diff line number Diff line change
@@ -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.bugs._4111;

import java.util.Collections;
import java.util.Optional;

import org.junit.jupiter.api.extension.RegisterExtension;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.GeneratedSource;

import static org.assertj.core.api.Assertions.assertThat;

@IssueKey("4111")
@WithClasses(Issue4111Mapper.class)
class Issue4111Test {

@RegisterExtension
final GeneratedSource generatedSource = new GeneratedSource();

@ProcessorTest
void emptyOptionalMapAndCollectionShouldNotThrow() {
Issue4111Mapper.Source source = new Issue4111Mapper.Source();
source.setName( Optional.empty() );
source.setAttributes( Optional.empty() );
source.setTags( Optional.empty() );

Issue4111Mapper.Target target = Issue4111Mapper.INSTANCE.toTarget( source );

assertThat( target.getName() ).isNull();
assertThat( target.getAttributes() ).isNull();
assertThat( target.getTags() ).isNull();
}

@ProcessorTest
void presentOptionalMapAndCollectionShouldBeMapped() {
Issue4111Mapper.Source source = new Issue4111Mapper.Source();
source.setName( Optional.of( "foo" ) );
source.setAttributes( Optional.of( Collections.singletonMap( "k", "v" ) ) );
source.setTags( Optional.of( Collections.singletonList( "tag" ) ) );

Issue4111Mapper.Target target = Issue4111Mapper.INSTANCE.toTarget( source );

assertThat( target.getName() ).isEqualTo( "foo" );
assertThat( target.getAttributes() ).containsEntry( "k", "v" );
assertThat( target.getTags() ).containsExactly( "tag" );
}
}
Loading