diff --git a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java index 4fed8e94f..e0725ba96 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java @@ -73,6 +73,7 @@ public class BeanMappingMethod extends MappingMethod { private MappingBuilderContext ctx; private SourceMethod method; private Map unprocessedTargetProperties; + private Set targetProperties; private final List propertyMappings = new ArrayList(); private final Set unprocessedSourceParameters = new HashSet(); private List qualifiers; @@ -89,6 +90,7 @@ public class BeanMappingMethod extends MappingMethod { this.method = sourceMethod; CollectionMappingStrategyPrism cms = sourceMethod.getMapperConfiguration().getCollectionMappingStrategy(); Map accessors = method.getResultType().getTargetAccessors( cms ); + this.targetProperties = accessors.keySet(); this.unprocessedTargetProperties = new HashMap( accessors ); for ( Parameter sourceParameter : method.getSourceParameters() ) { unprocessedSourceParameters.add( sourceParameter ); @@ -168,6 +170,10 @@ public class BeanMappingMethod extends MappingMethod { ); } + /** + * Sources the given mappings as per the dependency relationships given via {@code dependsOn()}. If a cycle is + * detected, an error is reported. + */ private void sortPropertyMappingsByDependencies() { final GraphAnalyzer graphAnalyzer = new GraphAnalyzer(); @@ -242,6 +248,20 @@ public class BeanMappingMethod extends MappingMethod { errorOccurred = true; } + // unknown properties given via dependsOn()? + for ( String dependency : mapping.getDependsOn() ) { + if ( !targetProperties.contains( dependency ) ) { + ctx.getMessager().printMessage( + method.getExecutable(), + mapping.getMirror(), + mapping.getDependsOnAnnotationValue(), + Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON, + dependency + ); + errorOccurred = true; + } + } + // check the mapping options // its an ignored property mapping if ( mapping.isIgnored() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java index 30e4173f5..493a5271f 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java @@ -62,6 +62,8 @@ public class Mapping { private final AnnotationMirror mirror; private final AnnotationValue sourceAnnotationValue; private final AnnotationValue targetAnnotationValue; + private final AnnotationValue dependsOnAnnotationValue; + private SourceReference sourceReference; public static Map> fromMappingsPrism(MappingsPrism mappingsAnnotation, @@ -136,6 +138,7 @@ public class Mapping { mappingPrism.mirror, mappingPrism.values.source(), mappingPrism.values.target(), + mappingPrism.values.dependsOn(), resultType, dependsOn ); @@ -146,6 +149,7 @@ public class Mapping { String dateFormat, List qualifiers, boolean isIgnored, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, + AnnotationValue dependsOnAnnotationValue, TypeMirror resultType, List dependsOn) { this.sourceName = sourceName; this.constant = constant; @@ -157,6 +161,7 @@ public class Mapping { this.mirror = mirror; this.sourceAnnotationValue = sourceAnnotationValue; this.targetAnnotationValue = targetAnnotationValue; + this.dependsOnAnnotationValue = dependsOnAnnotationValue; this.resultType = resultType; this.dependsOn = dependsOn; } @@ -243,6 +248,10 @@ public class Mapping { return targetAnnotationValue; } + public AnnotationValue getDependsOnAnnotationValue() { + return dependsOnAnnotationValue; + } + public SourceReference getSourceReference() { return sourceReference; } @@ -301,6 +310,7 @@ public class Mapping { mirror, sourceAnnotationValue, targetAnnotationValue, + dependsOnAnnotationValue, null, Collections.emptyList() ); @@ -326,6 +336,7 @@ public class Mapping { mirror, sourceAnnotationValue, targetAnnotationValue, + dependsOnAnnotationValue, resultType, dependsOn ); diff --git a/processor/src/main/java/org/mapstruct/ap/util/Message.java b/processor/src/main/java/org/mapstruct/ap/util/Message.java index 356a74dce..1a9541c5c 100644 --- a/processor/src/main/java/org/mapstruct/ap/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/util/Message.java @@ -36,6 +36,7 @@ public enum Message { BEANMAPPING_UNMAPPED_TARGETS_WARNING( "Unmapped target %s.", Diagnostic.Kind.WARNING ), BEANMAPPING_UNMAPPED_TARGETS_ERROR( "Unmapped target %s." ), BEANMAPPING_CYCLE_BETWEEN_PROPERTIES( "Cycle(s) between properties given via dependsOn(): %s." ), + BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ), PROPERTYMAPPING_MAPPING_NOT_FOUND( "Can't map %s to \"%s %s\". Consider to declare/implement a mapping method: \"%s map(%s value)\"." ), PROPERTYMAPPING_DUPLICATE_TARGETS( "Target property \"%s\" must not be mapped more than once." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/dependency/AddressMapperWithUnknownPropertyInDependsOn.java b/processor/src/test/java/org/mapstruct/ap/test/dependency/AddressMapperWithUnknownPropertyInDependsOn.java new file mode 100644 index 000000000..d40906035 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/dependency/AddressMapperWithUnknownPropertyInDependsOn.java @@ -0,0 +1,34 @@ +/** + * Copyright 2012-2015 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.dependency; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface AddressMapperWithUnknownPropertyInDependsOn { + + AddressMapperWithUnknownPropertyInDependsOn INSTANCE = Mappers.getMapper( + AddressMapperWithUnknownPropertyInDependsOn.class + ); + + @Mapping(target = "lastName", dependsOn = "doesnotexist") + PersonDto personToDto(Person person); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java b/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java index 22aa99dcf..85b04272e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java @@ -83,4 +83,20 @@ public class OrderingTest { ) public void shouldReportErrorIfDependenciesContainCycle() { } + + @Test + @IssueKey("304") + @WithClasses(AddressMapperWithUnknownPropertyInDependsOn.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = AddressMapperWithUnknownPropertyInDependsOn.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 32, + messageRegExp = "\"doesnotexist\" is no property of the method return type" + ) + } + ) + public void shouldReportErrorIfPropertiyGivenInDependsOnDoesNotExist() { + } }