diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index a41204c30..bf9d91cad 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -255,17 +255,31 @@ public class BeanMappingMethod extends MappingMethod { resultPropertyName = first( targetRef.getPropertyEntries() ).getName(); } - if (!unprocessedTargetProperties.containsKey( resultPropertyName ) ) { - boolean hasReadAccessor = + if ( !unprocessedTargetProperties.containsKey( resultPropertyName ) ) { + boolean hasReadAccessor = method.getResultType().getPropertyReadAccessors().containsKey( mapping.getTargetName() ); - ctx.getMessager().printMessage( method.getExecutable(), - mapping.getMirror(), - mapping.getSourceAnnotationValue(), - hasReadAccessor ? Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE : + + if ( hasReadAccessor ) { + if ( !mapping.isIgnored() ) { + ctx.getMessager().printMessage( + method.getExecutable(), + mapping.getMirror(), + mapping.getSourceAnnotationValue(), + Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE, + mapping.getTargetName() ); + errorOccurred = true; + } + } + else { + ctx.getMessager().printMessage( + method.getExecutable(), + mapping.getMirror(), + mapping.getSourceAnnotationValue(), Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE, - mapping.getTargetName() - ); - errorOccurred = true; + mapping.getTargetName() ); + errorOccurred = true; + } + continue; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/ErroneousIssue1029Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/ErroneousIssue1029Mapper.java new file mode 100644 index 000000000..479c72df2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/ErroneousIssue1029Mapper.java @@ -0,0 +1,101 @@ +/** + * Copyright 2012-2017 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._1029; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Map; +import java.util.TreeMap; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; + +/** + * @author Andreas Gudian + */ +@Mapper +public interface ErroneousIssue1029Mapper { + + /** + * reports unmapped properties 'lastUpdated' and 'computedMapping' + */ + Deck toDeck(DeckForm form); + + /** + * reports unmapped property 'lastUpdated'. Property 'outdated' is read-only anyway, but can still be ignored + * explicitly without raising any errors. + */ + @Mappings({ + @Mapping(target = "outdated", ignore = true), + @Mapping(target = "computedMapping", ignore = true) + }) + Deck toDeckWithSomeIgnores(DeckForm form); + + /** + * reports unknown property 'unknownProp' as error. + */ + @Mapping(target = "unknownProp", ignore = true) + Deck toDeckWithUnknownProperty(DeckForm form); + + class DeckForm { + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + } + + class Deck { + private Long id; + + private LocalDateTime lastUpdated; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public LocalDateTime getLastUpdated() { + return lastUpdated; + } + + public void setLastUpdated(LocalDateTime lastUpdated) { + this.lastUpdated = lastUpdated; + } + + // COMPUTED getters + + public boolean isOutdated() { + long daysBetween = ChronoUnit.DAYS.between( lastUpdated, LocalDateTime.now() ); + return daysBetween > 30; + } + + public Map getComputedMapping() { + return new TreeMap(); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java new file mode 100644 index 000000000..0b2adeed6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java @@ -0,0 +1,53 @@ +/** + * Copyright 2012-2017 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._1029; + +import javax.tools.Diagnostic.Kind; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * Verifies that read-only properties can be explicitly mentioned as {@code ignored=true} without raising an error. + * + * @author Andreas Gudian + */ +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses(ErroneousIssue1029Mapper.class) +@IssueKey("1029") +public class Issue1029Test { + + @Test + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(kind = Kind.WARNING, line = 39, type = ErroneousIssue1029Mapper.class, + messageRegExp = "Unmapped target properties: \"lastUpdated, computedMapping\"\\."), + @Diagnostic(kind = Kind.WARNING, line = 49, type = ErroneousIssue1029Mapper.class, + messageRegExp = "Unmapped target property: \"lastUpdated\"\\."), + @Diagnostic(kind = Kind.ERROR, line = 54, type = ErroneousIssue1029Mapper.class, + messageRegExp = "Unknown property \"unknownProp\" in return type\\.") + }) + public void reportsProperWarningsAndError() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/ErroneousTargetHasNoWriteAccessorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/ErroneousTargetHasNoWriteAccessorMapper.java index 0e2ee9635..eec8c64de 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/ErroneousTargetHasNoWriteAccessorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/ErroneousTargetHasNoWriteAccessorMapper.java @@ -32,7 +32,7 @@ public interface ErroneousTargetHasNoWriteAccessorMapper { ErroneousTargetHasNoWriteAccessorMapper INSTANCE = Mappers.getMapper( ErroneousTargetHasNoWriteAccessorMapper.class ); - @Mapping( target = "hasTallons", ignore = true ) + @Mapping(target = "hasClaws", constant = "true") PreditorDto preditorToDto( Preditor preditor ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java index 7eeec4a19..78ffc09f6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java @@ -18,9 +18,10 @@ */ package org.mapstruct.ap.test.ignore; -import javax.tools.Diagnostic.Kind; import static org.assertj.core.api.Assertions.assertThat; +import javax.tools.Diagnostic.Kind; + import org.junit.Test; import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; @@ -88,9 +89,9 @@ public class IgnorePropertyTest { @Diagnostic(type = ErroneousTargetHasNoWriteAccessorMapper.class, kind = Kind.ERROR, line = 35, - messageRegExp = "Property \"hasTallons\" has no write accessor\\.") + messageRegExp = "Property \"hasClaws\" has no write accessor\\.") } ) - public void shouldGiveWringingOnUnmapped() { + public void shouldGiveErrorOnMappingForReadOnlyProp() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/Preditor.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/Preditor.java index 449b5b272..6a7deeb3e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/Preditor.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/Preditor.java @@ -24,14 +24,13 @@ package org.mapstruct.ap.test.ignore; */ public class Preditor { - private boolean hasTallons; + private boolean hasClaws; - public boolean isHasTallons() { - return hasTallons; + public boolean isHasClaws() { + return hasClaws; } - public void setHasTallons(boolean hasTallons) { - this.hasTallons = hasTallons; + public void setHasClaws(boolean hasClaws) { + this.hasClaws = hasClaws; } - } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/PreditorDto.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/PreditorDto.java index 816f96aed..e20d6f9f1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/PreditorDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/PreditorDto.java @@ -24,10 +24,10 @@ package org.mapstruct.ap.test.ignore; */ public class PreditorDto { - private boolean hasTallons; + private boolean hasClaws; - public boolean isHasTallons() { - return hasTallons; + public boolean isHasClaws() { + return hasClaws; } }