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() { + } +}