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 342b2d69a..c3216070e 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 @@ -375,7 +375,7 @@ public class PropertyMapping extends ModelElement { else if ( result.getType() == TYPE_CONVERTED || result.getType() == TYPE_CONVERTED_MAPPED || result.getType() == MAPPED_TYPE_CONVERTED - || (result.getType() == DIRECT && targetType.isPrimitive()) ) { + || (result.getType() == DIRECT && targetType.isPrimitive() ) ) { // for primitive types null check is not possible at all, but a conversion needs // a null check. result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); @@ -522,9 +522,18 @@ public class PropertyMapping extends ModelElement { if ( propertyEntries.isEmpty() ) { return sourceParam.getType(); } + else if ( propertyEntries.size() == 1 ) { + return last( propertyEntries ).getType(); + } else { - PropertyEntry lastPropertyEntry = last( propertyEntries ); - return lastPropertyEntry.getType(); + Type sourceType = last( propertyEntries ).getType(); + if ( sourceType.isPrimitive() && !targetType.isPrimitive() ) { + // Handle null's. If the forged method needs to be mapped to an object, the forged method must be + // able to return null. So in that case primitive types are mapped to their corresponding wrapped + // type. The source type becomes the wrapped type in that case. + sourceType = ctx.getTypeFactory().getWrappedType( sourceType ); + } + return sourceType; } } @@ -544,18 +553,17 @@ public class PropertyMapping extends ModelElement { } // nested property given as dot path else { - PropertyEntry lastPropertyEntry = last( propertyEntries ); - // copy mapper configuration from the source method, its the same mapper MapperConfiguration config = method.getMapperConfiguration(); + // forge a method from the parameter type to the last entry type. String forgedName = Strings.joinAndCamelize( sourceReference.getElementNames() ); forgedName = Strings.getSaveVariableName( forgedName, ctx.getNamesOfMappingsToGenerate() ); ForgedMethod methodRef = new ForgedMethod( forgedName, sourceReference.getParameter().getType(), - lastPropertyEntry.getType(), + getSourceType(), config, method.getExecutable() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 17cd2ccff..ad52cd7b6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -124,6 +124,15 @@ public class TypeFactory { return null != elementUtils.getTypeElement( canonicalName ); } + public Type getWrappedType(Type type ) { + Type result = type; + if ( type.isPrimitive() ) { + PrimitiveType typeMirror = (PrimitiveType) type.getTypeMirror(); + result = getType( typeUtils.boxedClass( typeMirror ) ); + } + return result; + } + public Type getType(TypeElement typeElement) { return getType( typeElement.asType() ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Issue931Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Issue931Test.java new file mode 100644 index 000000000..3861ff4a4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Issue931Test.java @@ -0,0 +1,47 @@ +/** + * Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._931; + +import static org.assertj.core.api.Assertions.assertThat; + +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; + +/** + * Verifies that source.nested == null, leads to target.id == null + * + * @author Sjaak Derksen + */ +@IssueKey( "931" ) +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses( { Source.class, Nested.class, Target.class, SourceTargetMapper.class } ) +public class Issue931Test { + @Test + public void shouldMapNestedNullToNull() { + + Source source = new Source(); + Target target = SourceTargetMapper.MAPPER.toTarget( source ); + + assertThat( target.getId() ).isNull(); + assertThat( target.getIdStr() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Nested.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Nested.java new file mode 100644 index 000000000..c88d88dde --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Nested.java @@ -0,0 +1,33 @@ +/** + * Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._931; + +public class Nested { + + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Source.java new file mode 100644 index 000000000..30715c253 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Source.java @@ -0,0 +1,33 @@ +/** + * Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._931; + +public class Source { + + private Nested nested; + + public Nested getNested() { + return nested; + } + + public void setNested(Nested nested) { + this.nested = nested; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/SourceTargetMapper.java new file mode 100644 index 000000000..658710b8c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/SourceTargetMapper.java @@ -0,0 +1,36 @@ +/** + * Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._931; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SourceTargetMapper { + + SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class ); + + @Mappings({ + @Mapping(target = "id", source = "nested.id"), + @Mapping(target = "idStr", source = "nested.id")}) + Target toTarget(Source s); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Target.java new file mode 100644 index 000000000..7a4e43442 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Target.java @@ -0,0 +1,42 @@ +/** + * Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._931; + +public class Target { + + private Long id; + private String idStr; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getIdStr() { + return idStr; + } + + public void setIdStr(String idStr) { + this.idStr = idStr; + } + +}