From b826304d4c05b16183e0d69582a4f81e9ab472f2 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Sun, 19 May 2013 20:38:01 +0200 Subject: [PATCH] #10 Raising error in case a property can not be mapped --- .../mapstruct/ap/MapperGenerationVisitor.java | 81 +++++++++++++++---- .../org/mapstruct/ap/model/source/Method.java | 19 ++--- .../main/resources/mapper-implementation.ftl | 2 +- .../ap/test/erronuous/ErronuousMapper.java | 29 +++++++ ...appedPropertiesWithDifferentTypesTest.java | 45 +++++++++++ .../mapstruct/ap/test/erronuous/Source.java | 32 ++++++++ .../mapstruct/ap/test/erronuous/Target.java | 32 ++++++++ 7 files changed, 214 insertions(+), 26 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erronuous/ErronuousMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erronuous/MappedPropertiesWithDifferentTypesTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erronuous/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erronuous/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java b/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java index 7c7683bf6..9c51222ca 100644 --- a/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java +++ b/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java @@ -69,6 +69,8 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 { private final TypeUtil typeUtil; private final Options options; + private boolean mappingErronuous = false; + public MapperGenerationVisitor(ProcessingEnvironment processingEnvironment, Options options) { this.processingEnvironment = processingEnvironment; this.typeUtils = processingEnvironment.getTypeUtils(); @@ -80,9 +82,11 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 { @Override public Void visitTypeAsInterface(TypeElement element, Void p) { Mapper model = retrieveModel( element ); - String sourceFileName = element.getQualifiedName() + IMPLEMENTATION_SUFFIX; - writeModelToSourceFile( sourceFileName, model ); + if ( !mappingErronuous ) { + String sourceFileName = element.getQualifiedName() + IMPLEMENTATION_SUFFIX; + writeModelToSourceFile( sourceFileName, model ); + } return null; } @@ -153,9 +157,17 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 { for ( MappedProperty property : method.getMappedProperties() ) { Method propertyMappingMethod = getPropertyMappingMethod( methods, property ); Method reversePropertyMappingMethod = getReversePropertyMappingMethod( methods, property ); - Conversion conversion = conversions.getConversion( property.getSourceType(), property.getTargetType() ); + reportErrorIfPropertyCanNotBeMapped( + method, + rawReverseMappingMethod, + property, + propertyMappingMethod, + reversePropertyMappingMethod, + conversion + ); + propertyMappings.add( new PropertyMapping( property.getSourceReadAccessorName(), @@ -222,6 +234,48 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 { return mappings; } + private void reportErrorIfPropertyCanNotBeMapped(Method method, Method reverseMethod, MappedProperty property, Method propertyMappingMethod, Method reversePropertyMappingMethod, Conversion conversion) { + if ( property.getSourceType().equals( property.getTargetType() ) || + propertyMappingMethod != null || + conversion != null || + ( property.getTargetType().isCollectionType() && property.getTargetType() + .getCollectionImplementationType() != null ) ) { + return; + } + + reportError( + String.format( + "Can't map property \"%s %s\" to \"%s %s\".", + property.getSourceType(), + property.getSourceName(), + property.getTargetType(), + property.getTargetName() + ), + method.getExecutable() + ); + + mappingErronuous = true; + + if ( reverseMethod == null || + reversePropertyMappingMethod != null || + conversion != null || + ( property.getSourceType().isCollectionType() && property.getSourceType() + .getCollectionImplementationType() == null ) ) { + return; + } + + reportError( + String.format( + "Can't map property \"%s %s\" to \"%s %s\".", + property.getTargetType(), + property.getTargetName(), + property.getSourceType(), + property.getSourceName() + ), + reverseMethod.getExecutable() + ); + } + private String getIterableConversionString(Conversions conversions, Type sourceElementType, Type targetElementType, boolean isToConversion) { Conversion conversion = conversions.getConversion( sourceElementType, targetElementType ); @@ -300,7 +354,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 { methods.add( new Method( declaringMapper, - method.getSimpleName().toString(), + method, parameter.getName(), parameter.getType(), returnType, @@ -310,21 +364,11 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 { if ( declaringMapper == null ) { if ( parameter.getType().isIterableType() && !returnType.isIterableType() ) { - processingEnvironment.getMessager() - .printMessage( - Kind.ERROR, - "Can't generate mapping method from iterable type to non-iterable ype.", - method - ); + reportError( "Can't generate mapping method from iterable type to non-iterable ype.", method ); } if ( !parameter.getType().isIterableType() && returnType.isIterableType() ) { - processingEnvironment.getMessager() - .printMessage( - Kind.ERROR, - "Can't generate mapping method from non-iterable type to iterable ype.", - method - ); + reportError( "Can't generate mapping method from non-iterable type to iterable ype.", method ); } } } @@ -434,4 +478,9 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 { private Type retrieveReturnType(ExecutableElement method) { return typeUtil.retrieveType( method.getReturnType() ); } + + private void reportError(String message, Element element) { + processingEnvironment.getMessager().printMessage( Kind.ERROR, message, element ); + mappingErronuous = true; + } } 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 532748be2..1836c48ef 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 @@ -19,6 +19,7 @@ package org.mapstruct.ap.model.source; import java.util.List; +import javax.lang.model.element.ExecutableElement; import org.mapstruct.ap.model.Type; @@ -26,19 +27,15 @@ import org.mapstruct.ap.model.Type; public class Method { private final Type declaringMapper; - private final String name; + private final ExecutableElement executable; private final String parameterName; private final Type sourceType; private final Type targetType; private final List mappedProperties; - public Method(String name, String parameterName, Type sourceType, Type targetType, List mappedProperties) { - this( null, name, parameterName, sourceType, targetType, mappedProperties ); - } - - public Method(Type declaringMapper, String name, String parameterName, Type sourceType, Type targetType, List mappedProperties) { + public Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType, Type targetType, List mappedProperties) { this.declaringMapper = declaringMapper; - this.name = name; + this.executable = executable; this.parameterName = parameterName; this.sourceType = sourceType; this.targetType = targetType; @@ -49,8 +46,12 @@ public class Method { return declaringMapper; } + public ExecutableElement getExecutable() { + return executable; + } + public String getName() { - return name; + return executable.getSimpleName().toString(); } public String getParameterName() { @@ -81,6 +82,6 @@ public class Method { @Override public String toString() { - return targetType + " " + name + "(" + sourceType + " " + parameterName + ")"; + return targetType + " " + getName() + "(" + sourceType + " " + parameterName + ")"; } } diff --git a/processor/src/main/resources/mapper-implementation.ftl b/processor/src/main/resources/mapper-implementation.ftl index e523bcd7c..62d081ce3 100644 --- a/processor/src/main/resources/mapper-implementation.ftl +++ b/processor/src/main/resources/mapper-implementation.ftl @@ -150,12 +150,12 @@ public class ${implementationName} implements ${interfaceName} { <#-- b) invoke mapping method --> <#elseif mappingMethod != ""> ${targetBeanName}.${targetAccessorName}( <#if mappingMethod.declaringMapper??>${mappingMethod.declaringMapper.name?uncap_first}.${mappingMethod.name}( ${sourceBeanName}.${sourceAccessorName}() ) ); + <#-- c) simply set --> <#else> <#if targetType.collectionType == true> if ( ${sourceBeanName}.${sourceAccessorName}() != null ) { ${targetBeanName}.${targetAccessorName}( new <#if targetType.collectionImplementationType??>${targetType.collectionImplementationType.name}<#else>${targetType.name}<#if targetType.elementType??><${targetType.elementType.name}>( ${sourceBeanName}.${sourceAccessorName}() ) ); } - <#-- c) simply set --> <#else> ${targetBeanName}.${targetAccessorName}( ${sourceBeanName}.${sourceAccessorName}() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/erronuous/ErronuousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/erronuous/ErronuousMapper.java new file mode 100644 index 000000000..8d0527601 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erronuous/ErronuousMapper.java @@ -0,0 +1,29 @@ +/** + * 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.erronuous; + +import org.mapstruct.Mapper; + +@Mapper +public interface ErronuousMapper { + + Target sourceToTarget(Source source); + + Source targetToSource(Target target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erronuous/MappedPropertiesWithDifferentTypesTest.java b/processor/src/test/java/org/mapstruct/ap/test/erronuous/MappedPropertiesWithDifferentTypesTest.java new file mode 100644 index 000000000..9e55a3096 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erronuous/MappedPropertiesWithDifferentTypesTest.java @@ -0,0 +1,45 @@ +/** + * 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.erronuous; + +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; + +@WithClasses({ ErronuousMapper.class, Source.class, Target.class }) +public class MappedPropertiesWithDifferentTypesTest extends MapperTestBase { + + @Test + @IssueKey("6") + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErronuousMapper.class, kind = Kind.ERROR, line = 26), + @Diagnostic(type = ErronuousMapper.class, kind = Kind.ERROR, line = 28) + } + ) + public void shouldFailToGenerateMappingFromListToString() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erronuous/Source.java b/processor/src/test/java/org/mapstruct/ap/test/erronuous/Source.java new file mode 100644 index 000000000..7ade6b879 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erronuous/Source.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.erronuous; + +public class Source { + + private boolean foo; + + public boolean isFoo() { + return foo; + } + + public void setFoo(boolean foo) { + this.foo = foo; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erronuous/Target.java b/processor/src/test/java/org/mapstruct/ap/test/erronuous/Target.java new file mode 100644 index 000000000..0eb284a31 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erronuous/Target.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.erronuous; + +public class Target { + + private int foo; + + public int getFoo() { + return foo; + } + + public void setFoo(int foo) { + this.foo = foo; + } +}