diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml
index 3d58490e4..3da240ac0 100644
--- a/build-config/src/main/resources/build-config/checkstyle.xml
+++ b/build-config/src/main/resources/build-config/checkstyle.xml
@@ -90,7 +90,9 @@
-
+
+
+
diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java
index 19c3fd867..39d111aa6 100644
--- a/core/src/main/java/org/mapstruct/Mapper.java
+++ b/core/src/main/java/org/mapstruct/Mapper.java
@@ -39,4 +39,12 @@ public @interface Mapper {
* @return The mapper types used by this mapper.
*/
Class>[] uses() default { };
+
+ /**
+ * How unmapped properties of the target type of a mapping should be
+ * reported.
+ *
+ * @return The reporting policy for unmapped target properties.
+ */
+ ReportingPolicy unmappedTargetPolicy() default ReportingPolicy.WARN;
}
diff --git a/core/src/main/java/org/mapstruct/ReportingPolicy.java b/core/src/main/java/org/mapstruct/ReportingPolicy.java
new file mode 100644
index 000000000..ffff65dcd
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/ReportingPolicy.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2012-2013 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;
+
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * Policy for reporting issues occurring during the generation of a mapper
+ * implementation.
+ *
+ * @author Gunnar Morling
+ */
+public enum ReportingPolicy {
+
+ /**
+ * No report will be created for the given issue.
+ */
+ IGNORE,
+
+ /**
+ * A report with {@link Kind#WARNING} will be created for the given issue.
+ */
+ WARN,
+
+ /**
+ * A report with {@link Kind#ERROR} will be created for the given issue,
+ * causing the compilation to fail.
+ */
+ ERROR;
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java b/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java
index 7a9f6c133..d3fa131b3 100644
--- a/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java
+++ b/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java
@@ -20,8 +20,8 @@ package org.mapstruct.ap;
import java.beans.Introspector;
import java.io.IOException;
+import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -32,6 +32,7 @@ import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
@@ -50,6 +51,7 @@ import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MappingMethod;
import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.model.PropertyMapping;
+import org.mapstruct.ap.model.ReportingPolicy;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.source.MappedProperty;
import org.mapstruct.ap.model.source.Mapping;
@@ -57,6 +59,7 @@ import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.Parameter;
import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Filters;
+import org.mapstruct.ap.util.Strings;
import org.mapstruct.ap.util.TypeUtil;
import org.mapstruct.ap.writer.ModelWriter;
@@ -133,8 +136,14 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
}
private Mapper retrieveModel(TypeElement element) {
- List methods = retrieveMethods( null, element );
- List mappings = getMappings( methods );
+ //1.) build up "source" model
+ List methods = retrieveMethods( element, true );
+
+ //2.) build up aggregated "target" model
+ List mappings = getMappings(
+ methods,
+ ReportingPolicy.valueOf( MapperPrism.getInstanceOn( element ).unmappedTargetPolicy() )
+ );
List usedMapperTypes = getUsedMapperTypes( element );
Mapper mapper = new Mapper(
@@ -149,7 +158,8 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
return mapper;
}
- private List getMappings(List methods) {
+ private List getMappings(List methods,
+ ReportingPolicy unmappedTargetPolicy) {
Conversions conversions = new Conversions( elementUtils, typeUtils, typeUtil );
List mappings = new ArrayList();
@@ -181,8 +191,13 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
}
List propertyMappings = new ArrayList();
+ Set mappedSourceProperties = new HashSet();
+ Set mappedTargetProperties = new HashSet();
for ( MappedProperty property : method.getMappedProperties() ) {
+ mappedSourceProperties.add( property.getSourceName() );
+ mappedTargetProperties.add( property.getTargetName() );
+
Method propertyMappingMethod = getPropertyMappingMethod( methods, property );
Method reversePropertyMappingMethod = getReversePropertyMappingMethod( methods, property );
Conversion conversion = conversions.getConversion( property.getSourceType(), property.getTargetType() );
@@ -229,6 +244,23 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
boolean isIterableMapping = method.getSourceType().isIterableType() && method.getTargetType()
.isIterableType();
+ if ( mappingMethod.isGenerationRequired() && !isIterableMapping ) {
+ reportErrorForUnmappedTargetPropertiesIfRequired(
+ method.getExecutable(),
+ unmappedTargetPolicy,
+ method.getTargetProeprties(),
+ mappedTargetProperties
+ );
+ }
+ if ( reverseMappingMethod != null && reverseMappingMethod.isGenerationRequired() && !isIterableMapping ) {
+ reportErrorForUnmappedTargetPropertiesIfRequired(
+ rawReverseMappingMethod.getExecutable(),
+ unmappedTargetPolicy,
+ method.getSourceProperties(),
+ mappedSourceProperties
+ );
+ }
+
String toConversionString = null;
String fromConversionString = null;
@@ -262,6 +294,25 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
return mappings;
}
+ private void reportErrorForUnmappedTargetPropertiesIfRequired(ExecutableElement method,
+ ReportingPolicy unmappedTargetPolicy,
+ Set targetProperties,
+ Set mappedTargetProperties) {
+
+ if ( targetProperties.size() > mappedTargetProperties.size() && unmappedTargetPolicy.requiresReport() ) {
+ targetProperties.removeAll( mappedTargetProperties );
+ printMessage(
+ unmappedTargetPolicy,
+ MessageFormat.format(
+ "Unmapped target {0,choice,1#property|1 {
return null;
}
- private List retrieveMethods(Type declaringMapper, Element element) {
+ /**
+ * Retrieves the mapping methods declared by the given mapper type.
+ *
+ * @param element The type of interest
+ * @param implementationRequired Whether an implementation of this type must be generated or
+ * not. {@code true} if the type is the currently processed
+ * mapper interface, {@code false} if the given type is one
+ * referred to via {@code Mapper#uses()}.
+ *
+ * @return All mapping methods declared by the given type
+ */
+ private List retrieveMethods(TypeElement element, boolean implementationRequired) {
List methods = new ArrayList();
+ MapperPrism mapperPrism = implementationRequired ? MapperPrism.getInstanceOn( element ) : null;
+
+ //TODO Extract to separate method
for ( ExecutableElement method : methodsIn( element.getEnclosedElements() ) ) {
Parameter parameter = retrieveParameter( method );
Type returnType = retrieveReturnType( method );
+ Element returnTypeElement = typeUtils.asElement( method.getReturnType() );
+ Element parameterElement = typeUtils.asElement( method.getParameters().get( 0 ).asType() );
+
boolean mappingErroneous = false;
- if ( declaringMapper == null ) {
+ if ( implementationRequired ) {
if ( parameter.getType().isIterableType() && !returnType.isIterableType() ) {
reportError( "Can't generate mapping method from iterable type to non-iterable type.", method );
mappingErroneous = true;
@@ -410,30 +478,48 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
}
}
- //retrieve property mappings if an implementation for the method needs to be generated
- List properties = declaringMapper == null ? retrieveMappedProperties( method ) : Collections
- .emptyList();
+ //add method with property mappings if an implementation needs to be generated
+ if ( implementationRequired ) {
+ Set sourceProperties = Executables.getPropertyNames(
+ Filters.getterMethodsIn( parameterElement.getEnclosedElements() )
+ );
+ Set targetProperties = Executables.getPropertyNames(
+ Filters.setterMethodsIn( returnTypeElement.getEnclosedElements() )
+ );
- methods.add(
- new Method(
- declaringMapper,
- method,
- parameter.getName(),
- parameter.getType(),
- returnType,
- properties
- )
- );
+ methods.add(
+ Method.forMethodRequiringImplementation(
+ method,
+ parameter.getName(),
+ parameter.getType(),
+ returnType,
+ sourceProperties,
+ targetProperties,
+ retrieveMappedProperties( method, sourceProperties, targetProperties )
+ )
+ );
+ }
+ //otherwise add reference to existing mapper method
+ else {
+ methods.add(
+ Method.forReferencedMethod(
+ typeUtil.getType( typeUtils.getDeclaredType( element ) ),
+ method,
+ parameter.getName(),
+ parameter.getType(),
+ returnType
+ )
+ );
+ }
}
- MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
-
- if ( mapperPrism != null ) {
+ //Add all methods of used mappers in order to reference them in the aggregated model
+ if ( implementationRequired ) {
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
methods.addAll(
retrieveMethods(
- typeUtil.retrieveType( usedMapper ),
- ( (DeclaredType) usedMapper ).asElement()
+ (TypeElement) ( (DeclaredType) usedMapper ).asElement(),
+ false
)
);
}
@@ -448,10 +534,14 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
* method.
*
* @param method The method of interest
+ * @param targetProperties
+ * @param sourceProperties
*
* @return All mapped properties for the given method
*/
- private List retrieveMappedProperties(ExecutableElement method) {
+ private List retrieveMappedProperties(ExecutableElement method, Set sourceProperties,
+ Set targetProperties) {
+
Map mappings = getMappings( method );
TypeElement returnTypeElement = (TypeElement) typeUtils.asElement( method.getReturnType() );
@@ -472,7 +562,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
elementUtils.getAllMembers( returnTypeElement )
);
- reportErrorIfMappedPropertiesDontExist( method, mappings, sourceGetters, targetSetters );
+ reportErrorIfMappedPropertiesDontExist( method, sourceProperties, targetProperties, mappings );
for ( ExecutableElement getterMethod : sourceGetters ) {
String sourcePropertyName = Executables.getPropertyName( getterMethod );
@@ -509,12 +599,9 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
return properties;
}
- private void reportErrorIfMappedPropertiesDontExist(ExecutableElement method, Map mappings,
- List sourceGetters,
- List targetSetters) {
-
- Set sourcePropertyNames = Executables.getPropertyNames( sourceGetters );
- Set targetPropertyNames = Executables.getPropertyNames( targetSetters );
+ private void reportErrorIfMappedPropertiesDontExist(ExecutableElement method, Set sourcePropertyNames,
+ Set targetPropertyNames,
+ Map mappings) {
for ( Mapping mappedProperty : mappings.values() ) {
if ( !sourcePropertyNames.contains( mappedProperty.getSourceName() ) ) {
@@ -594,4 +681,11 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
.printMessage( Kind.ERROR, message, element, annotationMirror, annotationValue );
mappingErroneous = true;
}
+
+ private void printMessage(ReportingPolicy reportingPolicy, String message, Element element) {
+ processingEnvironment.getMessager().printMessage( reportingPolicy.getDiagnosticKind(), message, element );
+ if ( reportingPolicy.failsBuild() ) {
+ mappingErroneous = true;
+ }
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/ReportingPolicy.java b/processor/src/main/java/org/mapstruct/ap/model/ReportingPolicy.java
new file mode 100644
index 000000000..90e5a26da
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/model/ReportingPolicy.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2012-2013 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.model;
+
+import javax.tools.Diagnostic;
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * Possible issue reporting policies. Duplicates the enum of the same name from
+ * the core module as this can't be referenced here.
+ *
+ * @author Gunnar Morling
+ */
+public enum ReportingPolicy {
+
+ IGNORE( null, false, false ), WARN( Kind.WARNING, true, false ), ERROR( Kind.ERROR, true, true );
+
+ private final Diagnostic.Kind diagnosticKind;
+ private final boolean requiresReport;
+ private final boolean failsBuild;
+
+ private ReportingPolicy(Diagnostic.Kind diagnosticKind, boolean requiresReport, boolean failsBuild) {
+ this.requiresReport = requiresReport;
+ this.diagnosticKind = diagnosticKind;
+ this.failsBuild = failsBuild;
+ }
+
+ public Diagnostic.Kind getDiagnosticKind() {
+ return diagnosticKind;
+ }
+
+ public boolean requiresReport() {
+ return requiresReport;
+ }
+
+ public boolean failsBuild() {
+ return failsBuild;
+ }
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java
index 4d9bf9c2a..470efbab0 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java
@@ -18,7 +18,9 @@
*/
package org.mapstruct.ap.model.source;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.model.Type;
@@ -36,18 +38,64 @@ public class Method {
private final String parameterName;
private final Type sourceType;
private final Type targetType;
+ private Set sourceProperties;
+ private Set targetProeprties;
private final List mappedProperties;
- public Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType,
- Type targetType, List mappedProperties) {
+ public static Method forMethodRequiringImplementation(ExecutableElement executable, String parameterName,
+ Type sourceType,
+ Type targetType, Set sourceProperties,
+ Set targetProperties,
+ List mappedProperties) {
+
+ return new Method(
+ null,
+ executable,
+ parameterName,
+ sourceType,
+ targetType,
+ sourceProperties,
+ targetProperties,
+ mappedProperties
+ );
+ }
+
+ public static Method forReferencedMethod(Type declaringMapper, ExecutableElement executable, String parameterName,
+ Type sourceType,
+ Type targetType) {
+
+ return new Method(
+ declaringMapper,
+ executable,
+ parameterName,
+ sourceType,
+ targetType,
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptyList()
+ );
+ }
+
+ private Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType,
+ Type targetType, Set sourceProperties, Set targetProperties,
+ List mappedProperties) {
this.declaringMapper = declaringMapper;
this.executable = executable;
this.parameterName = parameterName;
this.sourceType = sourceType;
this.targetType = targetType;
+ this.sourceProperties = sourceProperties;
+ this.targetProeprties = targetProperties;
this.mappedProperties = mappedProperties;
}
+ /**
+ * Returns the mapper type declaring this method if it is not declared by
+ * the mapper interface currently processed but by another mapper imported
+ * via {@code Mapper#users()}.
+ *
+ * @return The declaring mapper type
+ */
public Type getDeclaringMapper() {
return declaringMapper;
}
@@ -72,6 +120,14 @@ public class Method {
return targetType;
}
+ public Set getSourceProperties() {
+ return sourceProperties;
+ }
+
+ public Set getTargetProeprties() {
+ return targetProeprties;
+ }
+
public List getMappedProperties() {
return mappedProperties;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/util/Executables.java b/processor/src/main/java/org/mapstruct/ap/util/Executables.java
index 2c92f8f00..f772dcc42 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/Executables.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/Executables.java
@@ -22,7 +22,6 @@ import java.beans.Introspector;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeKind;
diff --git a/processor/src/main/java/org/mapstruct/ap/util/Strings.java b/processor/src/main/java/org/mapstruct/ap/util/Strings.java
index e23e189cd..3e82924ac 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/Strings.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/Strings.java
@@ -18,6 +18,11 @@
*/
package org.mapstruct.ap.util;
+/**
+ * Helper class for dealing with strings.
+ *
+ * @author Gunnar Morling
+ */
public class Strings {
private Strings() {
@@ -26,4 +31,22 @@ public class Strings {
public static String capitalize(String name) {
return name == null ? null : name.substring( 0, 1 ).toUpperCase() + name.substring( 1 );
}
+
+ public static String join(Iterable> iterable, String separator) {
+ StringBuilder sb = new StringBuilder();
+ boolean isFirst = true;
+
+ for ( Object object : iterable ) {
+ if ( !isFirst ) {
+ sb.append( separator );
+ }
+ else {
+ isFirst = false;
+ }
+
+ sb.append( object );
+ }
+
+ return sb.toString();
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java
index 9fcbcf501..ba2ebee0c 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java
@@ -45,6 +45,10 @@ public class ErroneousMappingsTest extends MapperTestBase {
kind = Kind.ERROR,
line = 27,
messageRegExp = ".*Unknown property \"bar\" in return type.*"),
+ @Diagnostic(type = ErroneousMapper.class,
+ kind = Kind.WARNING,
+ line = 28,
+ messageRegExp = "Unmapped target property: \"foo\""),
@Diagnostic(type = ErroneousMapper.class,
kind = Kind.ERROR,
line = 30,
diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/Source.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/Source.java
new file mode 100644
index 000000000..5c2d6d673
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/Source.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2012-2013 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.unmappedtarget;
+
+public class Source {
+
+ private Long foo;
+
+ private String qux;
+
+ public Long getFoo() {
+ return foo;
+ }
+
+ public void setFoo(Long foo) {
+ this.foo = foo;
+ }
+
+ public String getQux() {
+ return qux;
+ }
+
+ public void setQux(String qux) {
+ this.qux = qux;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/SourceTargetMapper.java
new file mode 100644
index 000000000..fc44e8f22
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/SourceTargetMapper.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2012-2013 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.unmappedtarget;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mappers;
+
+@Mapper
+public interface SourceTargetMapper {
+
+ SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
+
+ Target sourceToTarget(Source source);
+
+ Source targetToSource(Target target);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/StrictSourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/StrictSourceTargetMapper.java
new file mode 100644
index 000000000..43cb49346
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/StrictSourceTargetMapper.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2012-2013 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.unmappedtarget;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mappers;
+import org.mapstruct.ReportingPolicy;
+
+@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
+public interface StrictSourceTargetMapper {
+
+ StrictSourceTargetMapper INSTANCE = Mappers.getMapper( StrictSourceTargetMapper.class );
+
+ Target sourceToTarget(Source source);
+
+ Source targetToSource(Target target);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/Target.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/Target.java
new file mode 100644
index 000000000..36793719e
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/Target.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2012-2013 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.unmappedtarget;
+
+public class Target {
+
+ private Long foo;
+ private int bar;
+
+ public Long getFoo() {
+ return foo;
+ }
+
+ public void setFoo(Long foo) {
+ this.foo = foo;
+ }
+
+ public int getBar() {
+ return bar;
+ }
+
+ public void setBar(int bar) {
+ this.bar = bar;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedTargetTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedTargetTest.java
new file mode 100644
index 000000000..2cc4051d6
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedTargetTest.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright 2012-2013 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.unmappedtarget;
+
+import javax.tools.Diagnostic.Kind;
+
+import org.mapstruct.ap.testutil.IssueKey;
+import org.mapstruct.ap.testutil.MapperTestBase;
+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.testng.annotations.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ * Tests expected diagnostics for unmapped target properties.
+ *
+ * @author Gunnar Morling
+ */
+@IssueKey("35")
+public class UnmappedTargetTest extends MapperTestBase {
+
+ @Test
+ @WithClasses({ Source.class, Target.class, SourceTargetMapper.class })
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.SUCCEEDED,
+ diagnostics = {
+ @Diagnostic(type = SourceTargetMapper.class,
+ kind = Kind.WARNING,
+ line = 29,
+ messageRegExp = "Unmapped target property: \"bar\""),
+ @Diagnostic(type = SourceTargetMapper.class,
+ kind = Kind.WARNING,
+ line = 31,
+ messageRegExp = "Unmapped target property: \"qux\"")
+ }
+ )
+ public void shouldLeaveUnmappedTargetPropertyUnset() {
+ Source source = new Source();
+ source.setFoo( 42L );
+
+ Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+
+ assertThat( target ).isNotNull();
+ assertThat( target.getFoo() ).isEqualTo( 42L );
+ assertThat( target.getBar() ).isEqualTo( 0 );
+ }
+
+ @Test
+ @WithClasses({ Source.class, Target.class, StrictSourceTargetMapper.class })
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic(type = StrictSourceTargetMapper.class,
+ kind = Kind.ERROR,
+ line = 30,
+ messageRegExp = "Unmapped target property: \"bar\""),
+ @Diagnostic(type = StrictSourceTargetMapper.class,
+ kind = Kind.ERROR,
+ line = 32,
+ messageRegExp = "Unmapped target property: \"qux\"")
+ }
+ )
+ public void shouldRaiseErrorDueToUnsetTargetProperty() {
+ }
+}