mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#65 Some clean-up in BeanMappingMethod
This commit is contained in:
parent
7041361b76
commit
d0db16072a
@ -20,11 +20,12 @@ package org.mapstruct.ap.model;
|
|||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
@ -45,11 +46,11 @@ import org.mapstruct.ap.util.MapperConfig;
|
|||||||
import org.mapstruct.ap.util.Strings;
|
import org.mapstruct.ap.util.Strings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one
|
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally
|
||||||
* bean sourceParameter to another, optionally configured by one or more
|
* configured by one or more {@link PropertyMapping}s.
|
||||||
* {@link PropertyMapping}s.
|
|
||||||
*
|
*
|
||||||
* @author Gunnar Morling
|
* @author Gunnar Morling
|
||||||
|
* @author Sjaak Derksen
|
||||||
*/
|
*/
|
||||||
public class BeanMappingMethod extends MappingMethod {
|
public class BeanMappingMethod extends MappingMethod {
|
||||||
|
|
||||||
@ -62,11 +63,9 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
|
|
||||||
private MappingBuilderContext ctx;
|
private MappingBuilderContext ctx;
|
||||||
private SourceMethod method;
|
private SourceMethod method;
|
||||||
|
private Map<String, ExecutableElement> unprocessedTargetProperties;
|
||||||
private final Map<String, TargetProperty> remainingTargetProperties = new HashMap<String, TargetProperty>();
|
|
||||||
private final List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
private final List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
||||||
|
|
||||||
|
|
||||||
public Builder mappingContext(MappingBuilderContext mappingContext) {
|
public Builder mappingContext(MappingBuilderContext mappingContext) {
|
||||||
this.ctx = mappingContext;
|
this.ctx = mappingContext;
|
||||||
return this;
|
return this;
|
||||||
@ -74,14 +73,11 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
|
|
||||||
public Builder souceMethod(SourceMethod sourceMethod) {
|
public Builder souceMethod(SourceMethod sourceMethod) {
|
||||||
this.method = sourceMethod;
|
this.method = sourceMethod;
|
||||||
|
this.unprocessedTargetProperties = initTargetPropertyAccessors();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BeanMappingMethod build() {
|
public BeanMappingMethod build() {
|
||||||
|
|
||||||
// init all non ignored targetAccessors
|
|
||||||
initTargetPropertyAccessors();
|
|
||||||
|
|
||||||
// map properties with mapping
|
// map properties with mapping
|
||||||
boolean mappingErrorOccured = handleDefinedSourceMappings();
|
boolean mappingErrorOccured = handleDefinedSourceMappings();
|
||||||
if ( mappingErrorOccured ) {
|
if ( mappingErrorOccured ) {
|
||||||
@ -94,7 +90,6 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
// report errors on unmapped properties
|
// report errors on unmapped properties
|
||||||
reportErrorForUnmappedTargetPropertiesIfRequired();
|
reportErrorForUnmappedTargetPropertiesIfRequired();
|
||||||
|
|
||||||
|
|
||||||
MethodReference factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
|
MethodReference factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
|
||||||
return new BeanMappingMethod( method, propertyMappings, factoryMethod );
|
return new BeanMappingMethod( method, propertyMappings, factoryMethod );
|
||||||
}
|
}
|
||||||
@ -102,8 +97,7 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
/**
|
/**
|
||||||
* This method builds the list of target accessors.
|
* This method builds the list of target accessors.
|
||||||
*/
|
*/
|
||||||
private void initTargetPropertyAccessors() {
|
private Map<String, ExecutableElement> initTargetPropertyAccessors() {
|
||||||
|
|
||||||
// fetch settings from element to implement
|
// fetch settings from element to implement
|
||||||
CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy();
|
CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy();
|
||||||
|
|
||||||
@ -112,8 +106,9 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
candidates.addAll( method.getResultType().getSetters() );
|
candidates.addAll( method.getResultType().getSetters() );
|
||||||
candidates.addAll( method.getResultType().getAlternativeTargetAccessors() );
|
candidates.addAll( method.getResultType().getAlternativeTargetAccessors() );
|
||||||
|
|
||||||
for ( ExecutableElement candidate : candidates ) {
|
Map<String, ExecutableElement> targetProperties = new HashMap<String, ExecutableElement>();
|
||||||
|
|
||||||
|
for ( ExecutableElement candidate : candidates ) {
|
||||||
String targetPropertyName = Executables.getPropertyName( candidate );
|
String targetPropertyName = Executables.getPropertyName( candidate );
|
||||||
|
|
||||||
// A target access is in general a setter method on the target object. However, in case of collections,
|
// A target access is in general a setter method on the target object. However, in case of collections,
|
||||||
@ -143,23 +138,23 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remainingTargetProperties.put( targetPropertyName, new TargetProperty(targetPropertyName, candidate ) );
|
targetProperties.put( targetPropertyName, candidate );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return targetProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over all defined mapping methods (@Mappings, @Mapping), either direct or via
|
* Iterates over all defined mapping methods ({@code @Mapping(s)}), either directly given or inherited from the
|
||||||
*
|
* inverse mapping method.
|
||||||
* @InheritInverseConfiguration.
|
* <p>
|
||||||
*
|
* If a match is found between a defined source (constant, expression, ignore or source) the mapping is removed
|
||||||
* If a match is found between a defined source (constant, expression, ignore or source, the mapping is
|
* from the remaining target properties.
|
||||||
* removed from the remain target properties.
|
* <p>
|
||||||
*
|
* It is furthermore checked whether the given mappings are correct. When an error occurs, the method continues
|
||||||
* This method should check if the mappings are defined correctly. When an error occurs, the method continues in
|
* in search of more problems.
|
||||||
* search of more problems.
|
|
||||||
*/
|
*/
|
||||||
private boolean handleDefinedSourceMappings() {
|
private boolean handleDefinedSourceMappings() {
|
||||||
|
|
||||||
boolean errorOccurred = false;
|
boolean errorOccurred = false;
|
||||||
|
|
||||||
Set<String> handledTargets = new HashSet<String>();
|
Set<String> handledTargets = new HashSet<String>();
|
||||||
@ -170,7 +165,7 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
PropertyMapping propertyMapping = null;
|
PropertyMapping propertyMapping = null;
|
||||||
|
|
||||||
// fetch the target property
|
// fetch the target property
|
||||||
TargetProperty targetProperty = remainingTargetProperties.get( mapping.getTargetName() );
|
ExecutableElement targetProperty = unprocessedTargetProperties.get( mapping.getTargetName() );
|
||||||
if ( targetProperty == null ) {
|
if ( targetProperty == null ) {
|
||||||
ctx.getMessager().printMessage(
|
ctx.getMessager().printMessage(
|
||||||
Diagnostic.Kind.ERROR,
|
Diagnostic.Kind.ERROR,
|
||||||
@ -184,7 +179,6 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
errorOccurred = true;
|
errorOccurred = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// check the mapping options
|
// check the mapping options
|
||||||
// its an ignored property mapping
|
// its an ignored property mapping
|
||||||
if ( mapping.isIgnored() ) {
|
if ( mapping.isIgnored() ) {
|
||||||
@ -206,8 +200,8 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
propertyMapping = new PropertyMappingBuilder()
|
propertyMapping = new PropertyMappingBuilder()
|
||||||
.mappingContext( ctx )
|
.mappingContext( ctx )
|
||||||
.souceMethod( method )
|
.souceMethod( method )
|
||||||
.targetAccessor( targetProperty.getAccessor() )
|
.targetAccessor( targetProperty )
|
||||||
.targetPropertyName( targetProperty.getName() )
|
.targetPropertyName( mapping.getTargetName() )
|
||||||
.sourceReference( sourceRef )
|
.sourceReference( sourceRef )
|
||||||
.qualifiers( mapping.getQualifiers() )
|
.qualifiers( mapping.getQualifiers() )
|
||||||
.dateFormat( mapping.getDateFormat() )
|
.dateFormat( mapping.getDateFormat() )
|
||||||
@ -218,7 +212,6 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
else {
|
else {
|
||||||
errorOccurred = true;
|
errorOccurred = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// its a constant
|
// its a constant
|
||||||
@ -228,7 +221,7 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
.mappingContext( ctx )
|
.mappingContext( ctx )
|
||||||
.sourceMethod( method )
|
.sourceMethod( method )
|
||||||
.constantExpression( "\"" + mapping.getConstant() + "\"" )
|
.constantExpression( "\"" + mapping.getConstant() + "\"" )
|
||||||
.targetAccessor( targetProperty.getAccessor() )
|
.targetAccessor( targetProperty )
|
||||||
.dateFormat( mapping.getDateFormat() )
|
.dateFormat( mapping.getDateFormat() )
|
||||||
.qualifiers( mapping.getQualifiers() )
|
.qualifiers( mapping.getQualifiers() )
|
||||||
.build();
|
.build();
|
||||||
@ -242,7 +235,7 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
.mappingContext( ctx )
|
.mappingContext( ctx )
|
||||||
.souceMethod( method )
|
.souceMethod( method )
|
||||||
.javaExpression( mapping.getJavaExpression() )
|
.javaExpression( mapping.getJavaExpression() )
|
||||||
.targetAccessor( targetProperty.getAccessor() )
|
.targetAccessor( targetProperty )
|
||||||
.build();
|
.build();
|
||||||
handledTargets.add( mapping.getTargetName() );
|
handledTargets.add( mapping.getTargetName() );
|
||||||
}
|
}
|
||||||
@ -256,25 +249,26 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ( String handledTarget : handledTargets ) {
|
for ( String handledTarget : handledTargets ) {
|
||||||
// In order to avoid: "Unknown property <> in return sourceParameter" in case of duplicate
|
// In order to avoid: "Unknown property foo in return type" in case of duplicate
|
||||||
// target mappings
|
// target mappings
|
||||||
remainingTargetProperties.remove( handledTarget );
|
unprocessedTargetProperties.remove( handledTarget );
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorOccurred;
|
return errorOccurred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over all target properties and all source parameters.
|
* Iterates over all target properties and all source parameters.
|
||||||
*
|
* <p>
|
||||||
* When a property name match occurs, the remainder will be checked for duplicates. Matches will
|
* When a property name match occurs, the remainder will be checked for duplicates. Matches will be removed from
|
||||||
* be removed from the set of remaining target properties.
|
* the set of remaining target properties.
|
||||||
*/
|
*/
|
||||||
private void applyPropertyNameBasedMapping() {
|
private void applyPropertyNameBasedMapping() {
|
||||||
|
Iterator<Entry<String, ExecutableElement>> targetProperties =
|
||||||
|
unprocessedTargetProperties.entrySet().iterator();
|
||||||
|
|
||||||
Collection<TargetProperty> targetProperties = remainingTargetProperties.values();
|
while ( targetProperties.hasNext() ) {
|
||||||
for ( TargetProperty targetProperty : new ArrayList<TargetProperty>( targetProperties ) ) {
|
Entry<String, ExecutableElement> targetProperty = targetProperties.next();
|
||||||
|
|
||||||
PropertyMapping propertyMapping = null;
|
PropertyMapping propertyMapping = null;
|
||||||
if ( propertyMapping == null ) {
|
if ( propertyMapping == null ) {
|
||||||
@ -283,7 +277,7 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
PropertyMapping newPropertyMapping = null;
|
PropertyMapping newPropertyMapping = null;
|
||||||
for ( ExecutableElement sourceAccessor : sourceParameter.getType().getGetters() ) {
|
for ( ExecutableElement sourceAccessor : sourceParameter.getType().getGetters() ) {
|
||||||
String sourcePropertyName = Executables.getPropertyName( sourceAccessor );
|
String sourcePropertyName = Executables.getPropertyName( sourceAccessor );
|
||||||
if ( sourcePropertyName.equals( targetProperty.getName() ) ) {
|
if ( sourcePropertyName.equals( targetProperty.getKey() ) ) {
|
||||||
|
|
||||||
Mapping mapping = method.getSingleMappingByTargetPropertyName( sourcePropertyName );
|
Mapping mapping = method.getSingleMappingByTargetPropertyName( sourcePropertyName );
|
||||||
|
|
||||||
@ -297,8 +291,8 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
newPropertyMapping = new PropertyMappingBuilder()
|
newPropertyMapping = new PropertyMappingBuilder()
|
||||||
.mappingContext( ctx )
|
.mappingContext( ctx )
|
||||||
.souceMethod( method )
|
.souceMethod( method )
|
||||||
.targetAccessor( targetProperty.getAccessor() )
|
.targetAccessor( targetProperty.getValue() )
|
||||||
.targetPropertyName( targetProperty.getName() )
|
.targetPropertyName( targetProperty.getKey() )
|
||||||
.sourceReference( sourceRef )
|
.sourceReference( sourceRef )
|
||||||
.qualifiers( mapping != null ? mapping.getQualifiers() : null )
|
.qualifiers( mapping != null ? mapping.getQualifiers() : null )
|
||||||
.dateFormat( mapping != null ? mapping.getDateFormat() : null )
|
.dateFormat( mapping != null ? mapping.getDateFormat() : null )
|
||||||
@ -312,7 +306,7 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
ctx.getMessager().printMessage(
|
ctx.getMessager().printMessage(
|
||||||
Diagnostic.Kind.ERROR,
|
Diagnostic.Kind.ERROR,
|
||||||
"Several possible source properties for target property \""
|
"Several possible source properties for target property \""
|
||||||
+ targetProperty.getName()
|
+ targetProperty.getKey()
|
||||||
+ "\".",
|
+ "\".",
|
||||||
method.getExecutable()
|
method.getExecutable()
|
||||||
);
|
);
|
||||||
@ -326,7 +320,7 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
|
|
||||||
if ( propertyMapping != null ) {
|
if ( propertyMapping != null ) {
|
||||||
propertyMappings.add( propertyMapping );
|
propertyMappings.add( propertyMapping );
|
||||||
remainingTargetProperties.remove( targetProperty.getName() );
|
targetProperties.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,9 +331,9 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
* be returned. If that is not set either, the default value from {@code Mapper#unmappedTargetPolicy()} will be
|
* be returned. If that is not set either, the default value from {@code Mapper#unmappedTargetPolicy()} will be
|
||||||
* returned.
|
* returned.
|
||||||
*
|
*
|
||||||
* @param element The sourceParameter declaring the generated mapper sourceParameter
|
* @param element The type declaring the generated mapper type
|
||||||
*
|
*
|
||||||
* @return The effective policy for reporting unmapped getReturnType properties.
|
* @return The effective policy for reporting unmapped target properties.
|
||||||
*/
|
*/
|
||||||
private ReportingPolicy getEffectiveUnmappedTargetPolicy() {
|
private ReportingPolicy getEffectiveUnmappedTargetPolicy() {
|
||||||
MapperConfig mapperSettings = MapperConfig.getInstanceOn( ctx.getMapperTypeElement() );
|
MapperConfig mapperSettings = MapperConfig.getInstanceOn( ctx.getMapperTypeElement() );
|
||||||
@ -367,14 +361,14 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
// fetch settings from element to implement
|
// fetch settings from element to implement
|
||||||
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy();
|
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy();
|
||||||
|
|
||||||
if ( !remainingTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
|
if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
|
||||||
|
|
||||||
ctx.getMessager().printMessage(
|
ctx.getMessager().printMessage(
|
||||||
unmappedTargetPolicy.getDiagnosticKind(),
|
unmappedTargetPolicy.getDiagnosticKind(),
|
||||||
MessageFormat.format(
|
MessageFormat.format(
|
||||||
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
|
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
|
||||||
remainingTargetProperties.size(),
|
unprocessedTargetProperties.size(),
|
||||||
Strings.join( remainingTargetProperties.keySet(), ", " )
|
Strings.join( unprocessedTargetProperties.keySet(), ", " )
|
||||||
),
|
),
|
||||||
method.getExecutable()
|
method.getExecutable()
|
||||||
);
|
);
|
||||||
@ -388,7 +382,6 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
super( method );
|
super( method );
|
||||||
this.propertyMappings = propertyMappings;
|
this.propertyMappings = propertyMappings;
|
||||||
|
|
||||||
|
|
||||||
// intialize constant mappings as all mappings, but take out the ones that can be contributed to a
|
// intialize constant mappings as all mappings, but take out the ones that can be contributed to a
|
||||||
// parameter mapping.
|
// parameter mapping.
|
||||||
this.mappingsByParameter = new HashMap<String, List<PropertyMapping>>();
|
this.mappingsByParameter = new HashMap<String, List<PropertyMapping>>();
|
||||||
@ -432,25 +425,4 @@ public class BeanMappingMethod extends MappingMethod {
|
|||||||
public MethodReference getFactoryMethod() {
|
public MethodReference getFactoryMethod() {
|
||||||
return this.factoryMethod;
|
return this.factoryMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class TargetProperty {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final ExecutableElement accessor;
|
|
||||||
|
|
||||||
public TargetProperty( String name, ExecutableElement accessor ) {
|
|
||||||
this.name = name;
|
|
||||||
this.accessor = accessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExecutableElement getAccessor() {
|
|
||||||
return accessor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ package org.mapstruct.ap.model;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.mapstruct.ap.model.common.Parameter;
|
import org.mapstruct.ap.model.common.Parameter;
|
||||||
import org.mapstruct.ap.model.common.Type;
|
import org.mapstruct.ap.model.common.Type;
|
||||||
import org.mapstruct.ap.model.source.Method;
|
import org.mapstruct.ap.model.source.Method;
|
||||||
@ -30,7 +31,7 @@ import org.mapstruct.ap.util.Strings;
|
|||||||
/**
|
/**
|
||||||
* This method is used to convert the nested properties as listed in propertyEntries into a method
|
* This method is used to convert the nested properties as listed in propertyEntries into a method
|
||||||
* that creates a mapping from the start of this list to the end of the list.
|
* that creates a mapping from the start of this list to the end of the list.
|
||||||
*
|
* <p>
|
||||||
* So, say that the start of the list is of TypeA and the end of the list is of TypeB than the forged method
|
* So, say that the start of the list is of TypeA and the end of the list is of TypeB than the forged method
|
||||||
* will create a forged mapping method: TypeB methodName( TypeA in ).
|
* will create a forged mapping method: TypeB methodName( TypeA in ).
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user