mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#35 Raising warning in case of unmapped target properties
This commit is contained in:
parent
895a715727
commit
bf5a4303b6
@ -90,7 +90,9 @@
|
|||||||
<module name="AvoidStarImport"/>
|
<module name="AvoidStarImport"/>
|
||||||
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
|
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
|
||||||
<module name="RedundantImport"/>
|
<module name="RedundantImport"/>
|
||||||
<module name="UnusedImports"/>
|
<module name="UnusedImports">
|
||||||
|
<property name="processJavadoc" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
|
||||||
<!-- Checks for Size Violations. -->
|
<!-- Checks for Size Violations. -->
|
||||||
|
@ -39,4 +39,12 @@ public @interface Mapper {
|
|||||||
* @return The mapper types used by this mapper.
|
* @return The mapper types used by this mapper.
|
||||||
*/
|
*/
|
||||||
Class<?>[] uses() default { };
|
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;
|
||||||
}
|
}
|
||||||
|
46
core/src/main/java/org/mapstruct/ReportingPolicy.java
Normal file
46
core/src/main/java/org/mapstruct/ReportingPolicy.java
Normal file
@ -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;
|
||||||
|
}
|
@ -20,8 +20,8 @@ package org.mapstruct.ap;
|
|||||||
|
|
||||||
import java.beans.Introspector;
|
import java.beans.Introspector;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -32,6 +32,7 @@ import javax.annotation.processing.ProcessingEnvironment;
|
|||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.element.AnnotationValue;
|
import javax.lang.model.element.AnnotationValue;
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ElementVisitor;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.lang.model.element.VariableElement;
|
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.MappingMethod;
|
||||||
import org.mapstruct.ap.model.Options;
|
import org.mapstruct.ap.model.Options;
|
||||||
import org.mapstruct.ap.model.PropertyMapping;
|
import org.mapstruct.ap.model.PropertyMapping;
|
||||||
|
import org.mapstruct.ap.model.ReportingPolicy;
|
||||||
import org.mapstruct.ap.model.Type;
|
import org.mapstruct.ap.model.Type;
|
||||||
import org.mapstruct.ap.model.source.MappedProperty;
|
import org.mapstruct.ap.model.source.MappedProperty;
|
||||||
import org.mapstruct.ap.model.source.Mapping;
|
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.model.source.Parameter;
|
||||||
import org.mapstruct.ap.util.Executables;
|
import org.mapstruct.ap.util.Executables;
|
||||||
import org.mapstruct.ap.util.Filters;
|
import org.mapstruct.ap.util.Filters;
|
||||||
|
import org.mapstruct.ap.util.Strings;
|
||||||
import org.mapstruct.ap.util.TypeUtil;
|
import org.mapstruct.ap.util.TypeUtil;
|
||||||
import org.mapstruct.ap.writer.ModelWriter;
|
import org.mapstruct.ap.writer.ModelWriter;
|
||||||
|
|
||||||
@ -133,8 +136,14 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Mapper retrieveModel(TypeElement element) {
|
private Mapper retrieveModel(TypeElement element) {
|
||||||
List<Method> methods = retrieveMethods( null, element );
|
//1.) build up "source" model
|
||||||
List<BeanMapping> mappings = getMappings( methods );
|
List<Method> methods = retrieveMethods( element, true );
|
||||||
|
|
||||||
|
//2.) build up aggregated "target" model
|
||||||
|
List<BeanMapping> mappings = getMappings(
|
||||||
|
methods,
|
||||||
|
ReportingPolicy.valueOf( MapperPrism.getInstanceOn( element ).unmappedTargetPolicy() )
|
||||||
|
);
|
||||||
List<Type> usedMapperTypes = getUsedMapperTypes( element );
|
List<Type> usedMapperTypes = getUsedMapperTypes( element );
|
||||||
|
|
||||||
Mapper mapper = new Mapper(
|
Mapper mapper = new Mapper(
|
||||||
@ -149,7 +158,8 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BeanMapping> getMappings(List<Method> methods) {
|
private List<BeanMapping> getMappings(List<Method> methods,
|
||||||
|
ReportingPolicy unmappedTargetPolicy) {
|
||||||
Conversions conversions = new Conversions( elementUtils, typeUtils, typeUtil );
|
Conversions conversions = new Conversions( elementUtils, typeUtils, typeUtil );
|
||||||
|
|
||||||
List<BeanMapping> mappings = new ArrayList<BeanMapping>();
|
List<BeanMapping> mappings = new ArrayList<BeanMapping>();
|
||||||
@ -181,8 +191,13 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
||||||
|
Set<String> mappedSourceProperties = new HashSet<String>();
|
||||||
|
Set<String> mappedTargetProperties = new HashSet<String>();
|
||||||
|
|
||||||
for ( MappedProperty property : method.getMappedProperties() ) {
|
for ( MappedProperty property : method.getMappedProperties() ) {
|
||||||
|
mappedSourceProperties.add( property.getSourceName() );
|
||||||
|
mappedTargetProperties.add( property.getTargetName() );
|
||||||
|
|
||||||
Method propertyMappingMethod = getPropertyMappingMethod( methods, property );
|
Method propertyMappingMethod = getPropertyMappingMethod( methods, property );
|
||||||
Method reversePropertyMappingMethod = getReversePropertyMappingMethod( methods, property );
|
Method reversePropertyMappingMethod = getReversePropertyMappingMethod( methods, property );
|
||||||
Conversion conversion = conversions.getConversion( property.getSourceType(), property.getTargetType() );
|
Conversion conversion = conversions.getConversion( property.getSourceType(), property.getTargetType() );
|
||||||
@ -229,6 +244,23 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
boolean isIterableMapping = method.getSourceType().isIterableType() && method.getTargetType()
|
boolean isIterableMapping = method.getSourceType().isIterableType() && method.getTargetType()
|
||||||
.isIterableType();
|
.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 toConversionString = null;
|
||||||
String fromConversionString = null;
|
String fromConversionString = null;
|
||||||
|
|
||||||
@ -262,6 +294,25 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
return mappings;
|
return mappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reportErrorForUnmappedTargetPropertiesIfRequired(ExecutableElement method,
|
||||||
|
ReportingPolicy unmappedTargetPolicy,
|
||||||
|
Set<String> targetProperties,
|
||||||
|
Set<String> mappedTargetProperties) {
|
||||||
|
|
||||||
|
if ( targetProperties.size() > mappedTargetProperties.size() && unmappedTargetPolicy.requiresReport() ) {
|
||||||
|
targetProperties.removeAll( mappedTargetProperties );
|
||||||
|
printMessage(
|
||||||
|
unmappedTargetPolicy,
|
||||||
|
MessageFormat.format(
|
||||||
|
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
|
||||||
|
targetProperties.size(),
|
||||||
|
Strings.join( targetProperties, ", " )
|
||||||
|
),
|
||||||
|
method
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void reportErrorIfPropertyCanNotBeMapped(Method method, Method reverseMethod, MappedProperty property,
|
private void reportErrorIfPropertyCanNotBeMapped(Method method, Method reverseMethod, MappedProperty property,
|
||||||
Method propertyMappingMethod, Method reversePropertyMappingMethod,
|
Method propertyMappingMethod, Method reversePropertyMappingMethod,
|
||||||
Conversion conversion) {
|
Conversion conversion) {
|
||||||
@ -379,15 +430,32 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Method> 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<Method> retrieveMethods(TypeElement element, boolean implementationRequired) {
|
||||||
List<Method> methods = new ArrayList<Method>();
|
List<Method> methods = new ArrayList<Method>();
|
||||||
|
|
||||||
|
MapperPrism mapperPrism = implementationRequired ? MapperPrism.getInstanceOn( element ) : null;
|
||||||
|
|
||||||
|
//TODO Extract to separate method
|
||||||
for ( ExecutableElement method : methodsIn( element.getEnclosedElements() ) ) {
|
for ( ExecutableElement method : methodsIn( element.getEnclosedElements() ) ) {
|
||||||
Parameter parameter = retrieveParameter( method );
|
Parameter parameter = retrieveParameter( method );
|
||||||
Type returnType = retrieveReturnType( method );
|
Type returnType = retrieveReturnType( method );
|
||||||
|
Element returnTypeElement = typeUtils.asElement( method.getReturnType() );
|
||||||
|
Element parameterElement = typeUtils.asElement( method.getParameters().get( 0 ).asType() );
|
||||||
|
|
||||||
boolean mappingErroneous = false;
|
boolean mappingErroneous = false;
|
||||||
|
|
||||||
if ( declaringMapper == null ) {
|
if ( implementationRequired ) {
|
||||||
if ( parameter.getType().isIterableType() && !returnType.isIterableType() ) {
|
if ( parameter.getType().isIterableType() && !returnType.isIterableType() ) {
|
||||||
reportError( "Can't generate mapping method from iterable type to non-iterable type.", method );
|
reportError( "Can't generate mapping method from iterable type to non-iterable type.", method );
|
||||||
mappingErroneous = true;
|
mappingErroneous = true;
|
||||||
@ -410,30 +478,48 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//retrieve property mappings if an implementation for the method needs to be generated
|
//add method with property mappings if an implementation needs to be generated
|
||||||
List<MappedProperty> properties = declaringMapper == null ? retrieveMappedProperties( method ) : Collections
|
if ( implementationRequired ) {
|
||||||
.<MappedProperty>emptyList();
|
Set<String> sourceProperties = Executables.getPropertyNames(
|
||||||
|
Filters.getterMethodsIn( parameterElement.getEnclosedElements() )
|
||||||
|
);
|
||||||
|
Set<String> targetProperties = Executables.getPropertyNames(
|
||||||
|
Filters.setterMethodsIn( returnTypeElement.getEnclosedElements() )
|
||||||
|
);
|
||||||
|
|
||||||
methods.add(
|
methods.add(
|
||||||
new Method(
|
Method.forMethodRequiringImplementation(
|
||||||
declaringMapper,
|
method,
|
||||||
method,
|
parameter.getName(),
|
||||||
parameter.getName(),
|
parameter.getType(),
|
||||||
parameter.getType(),
|
returnType,
|
||||||
returnType,
|
sourceProperties,
|
||||||
properties
|
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 );
|
//Add all methods of used mappers in order to reference them in the aggregated model
|
||||||
|
if ( implementationRequired ) {
|
||||||
if ( mapperPrism != null ) {
|
|
||||||
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
|
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
|
||||||
methods.addAll(
|
methods.addAll(
|
||||||
retrieveMethods(
|
retrieveMethods(
|
||||||
typeUtil.retrieveType( usedMapper ),
|
(TypeElement) ( (DeclaredType) usedMapper ).asElement(),
|
||||||
( (DeclaredType) usedMapper ).asElement()
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -448,10 +534,14 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
* method.
|
* method.
|
||||||
*
|
*
|
||||||
* @param method The method of interest
|
* @param method The method of interest
|
||||||
|
* @param targetProperties
|
||||||
|
* @param sourceProperties
|
||||||
*
|
*
|
||||||
* @return All mapped properties for the given method
|
* @return All mapped properties for the given method
|
||||||
*/
|
*/
|
||||||
private List<MappedProperty> retrieveMappedProperties(ExecutableElement method) {
|
private List<MappedProperty> retrieveMappedProperties(ExecutableElement method, Set<String> sourceProperties,
|
||||||
|
Set<String> targetProperties) {
|
||||||
|
|
||||||
Map<String, Mapping> mappings = getMappings( method );
|
Map<String, Mapping> mappings = getMappings( method );
|
||||||
|
|
||||||
TypeElement returnTypeElement = (TypeElement) typeUtils.asElement( method.getReturnType() );
|
TypeElement returnTypeElement = (TypeElement) typeUtils.asElement( method.getReturnType() );
|
||||||
@ -472,7 +562,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
elementUtils.getAllMembers( returnTypeElement )
|
elementUtils.getAllMembers( returnTypeElement )
|
||||||
);
|
);
|
||||||
|
|
||||||
reportErrorIfMappedPropertiesDontExist( method, mappings, sourceGetters, targetSetters );
|
reportErrorIfMappedPropertiesDontExist( method, sourceProperties, targetProperties, mappings );
|
||||||
|
|
||||||
for ( ExecutableElement getterMethod : sourceGetters ) {
|
for ( ExecutableElement getterMethod : sourceGetters ) {
|
||||||
String sourcePropertyName = Executables.getPropertyName( getterMethod );
|
String sourcePropertyName = Executables.getPropertyName( getterMethod );
|
||||||
@ -509,12 +599,9 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportErrorIfMappedPropertiesDontExist(ExecutableElement method, Map<String, Mapping> mappings,
|
private void reportErrorIfMappedPropertiesDontExist(ExecutableElement method, Set<String> sourcePropertyNames,
|
||||||
List<ExecutableElement> sourceGetters,
|
Set<String> targetPropertyNames,
|
||||||
List<ExecutableElement> targetSetters) {
|
Map<String, Mapping> mappings) {
|
||||||
|
|
||||||
Set<String> sourcePropertyNames = Executables.getPropertyNames( sourceGetters );
|
|
||||||
Set<String> targetPropertyNames = Executables.getPropertyNames( targetSetters );
|
|
||||||
|
|
||||||
for ( Mapping mappedProperty : mappings.values() ) {
|
for ( Mapping mappedProperty : mappings.values() ) {
|
||||||
if ( !sourcePropertyNames.contains( mappedProperty.getSourceName() ) ) {
|
if ( !sourcePropertyNames.contains( mappedProperty.getSourceName() ) ) {
|
||||||
@ -594,4 +681,11 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
.printMessage( Kind.ERROR, message, element, annotationMirror, annotationValue );
|
.printMessage( Kind.ERROR, message, element, annotationMirror, annotationValue );
|
||||||
mappingErroneous = true;
|
mappingErroneous = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void printMessage(ReportingPolicy reportingPolicy, String message, Element element) {
|
||||||
|
processingEnvironment.getMessager().printMessage( reportingPolicy.getDiagnosticKind(), message, element );
|
||||||
|
if ( reportingPolicy.failsBuild() ) {
|
||||||
|
mappingErroneous = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.model.source;
|
package org.mapstruct.ap.model.source;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
|
||||||
import org.mapstruct.ap.model.Type;
|
import org.mapstruct.ap.model.Type;
|
||||||
@ -36,18 +38,64 @@ public class Method {
|
|||||||
private final String parameterName;
|
private final String parameterName;
|
||||||
private final Type sourceType;
|
private final Type sourceType;
|
||||||
private final Type targetType;
|
private final Type targetType;
|
||||||
|
private Set<String> sourceProperties;
|
||||||
|
private Set<String> targetProeprties;
|
||||||
private final List<MappedProperty> mappedProperties;
|
private final List<MappedProperty> mappedProperties;
|
||||||
|
|
||||||
public Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType,
|
public static Method forMethodRequiringImplementation(ExecutableElement executable, String parameterName,
|
||||||
Type targetType, List<MappedProperty> mappedProperties) {
|
Type sourceType,
|
||||||
|
Type targetType, Set<String> sourceProperties,
|
||||||
|
Set<String> targetProperties,
|
||||||
|
List<MappedProperty> 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.<String>emptySet(),
|
||||||
|
Collections.<String>emptySet(),
|
||||||
|
Collections.<MappedProperty>emptyList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType,
|
||||||
|
Type targetType, Set<String> sourceProperties, Set<String> targetProperties,
|
||||||
|
List<MappedProperty> mappedProperties) {
|
||||||
this.declaringMapper = declaringMapper;
|
this.declaringMapper = declaringMapper;
|
||||||
this.executable = executable;
|
this.executable = executable;
|
||||||
this.parameterName = parameterName;
|
this.parameterName = parameterName;
|
||||||
this.sourceType = sourceType;
|
this.sourceType = sourceType;
|
||||||
this.targetType = targetType;
|
this.targetType = targetType;
|
||||||
|
this.sourceProperties = sourceProperties;
|
||||||
|
this.targetProeprties = targetProperties;
|
||||||
this.mappedProperties = mappedProperties;
|
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() {
|
public Type getDeclaringMapper() {
|
||||||
return declaringMapper;
|
return declaringMapper;
|
||||||
}
|
}
|
||||||
@ -72,6 +120,14 @@ public class Method {
|
|||||||
return targetType;
|
return targetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getSourceProperties() {
|
||||||
|
return sourceProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getTargetProeprties() {
|
||||||
|
return targetProeprties;
|
||||||
|
}
|
||||||
|
|
||||||
public List<MappedProperty> getMappedProperties() {
|
public List<MappedProperty> getMappedProperties() {
|
||||||
return mappedProperties;
|
return mappedProperties;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import java.beans.Introspector;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.lang.model.element.Element;
|
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
|
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.util;
|
package org.mapstruct.ap.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for dealing with strings.
|
||||||
|
*
|
||||||
|
* @author Gunnar Morling
|
||||||
|
*/
|
||||||
public class Strings {
|
public class Strings {
|
||||||
|
|
||||||
private Strings() {
|
private Strings() {
|
||||||
@ -26,4 +31,22 @@ public class Strings {
|
|||||||
public static String capitalize(String name) {
|
public static String capitalize(String name) {
|
||||||
return name == null ? null : name.substring( 0, 1 ).toUpperCase() + name.substring( 1 );
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,10 @@ public class ErroneousMappingsTest extends MapperTestBase {
|
|||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 27,
|
line = 27,
|
||||||
messageRegExp = ".*Unknown property \"bar\" in return type.*"),
|
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,
|
@Diagnostic(type = ErroneousMapper.class,
|
||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 30,
|
line = 30,
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user