#981 Refactoring, move SourceRHS init to start of property build.

This commit is contained in:
sjaakd 2016-12-03 15:20:26 +01:00
parent 0ec9729801
commit 81a4cb360d
22 changed files with 187 additions and 178 deletions

View File

@ -105,12 +105,11 @@ public class IterableMappingMethod extends MappingMethod {
Assignment assignment = ctx.getMappingResolver().getTargetAssignment( Assignment assignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
"collection element",
targetElementType, targetElementType,
null, // there is no targetPropertyName null, // there is no targetPropertyName
formattingParameters, formattingParameters,
selectionParameters, selectionParameters,
new SourceRHS( loopVariableName, sourceElementType, new HashSet<String>() ), new SourceRHS( loopVariableName, sourceElementType, new HashSet<String>(), "collection element" ),
false false
); );

View File

@ -108,12 +108,11 @@ public class MapMappingMethod extends MappingMethod {
Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment( Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
"map key",
keyTargetType, keyTargetType,
null, // there is no targetPropertyName null, // there is no targetPropertyName
keyFormattingParameters, keyFormattingParameters,
keySelectionParameters, keySelectionParameters,
new SourceRHS( "entry.getKey()", keySourceType, new HashSet<String>() ), new SourceRHS( "entry.getKey()", keySourceType, new HashSet<String>(), "map key" ),
false false
); );
@ -134,12 +133,11 @@ public class MapMappingMethod extends MappingMethod {
Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment( Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
"map value",
valueTargetType, valueTargetType,
null, // there is no targetPropertyName null, // there is no targetPropertyName
valueFormattingParameters, valueFormattingParameters,
valueSelectionParameters, valueSelectionParameters,
new SourceRHS( "entry.getValue()", valueSourceType, new HashSet<String>() ), new SourceRHS( "entry.getValue()", valueSourceType, new HashSet<String>(), "map value" ),
false false
); );

View File

@ -75,7 +75,6 @@ public class MappingBuilderContext {
* returns a parameter assignment * returns a parameter assignment
* *
* @param mappingMethod target mapping method * @param mappingMethod target mapping method
* @param mappedElement used for error messages
* @param targetType return type to match * @param targetType return type to match
* @param targetPropertyName name of the target property * @param targetPropertyName name of the target property
* @param formattingParameters used for formatting dates and numbers * @param formattingParameters used for formatting dates and numbers
@ -91,9 +90,8 @@ public class MappingBuilderContext {
* <li>null, no assignment found</li> * <li>null, no assignment found</li>
* </ol> * </ol>
*/ */
@SuppressWarnings("checkstyle:parameternumber") Assignment getTargetAssignment(Method mappingMethod, Type targetType, String targetPropertyName,
Assignment getTargetAssignment(Method mappingMethod, String mappedElement, Type targetType, FormattingParameters formattingParameters,
String targetPropertyName, FormattingParameters formattingParameters,
SelectionParameters selectionParameters, SourceRHS sourceRHS, SelectionParameters selectionParameters, SourceRHS sourceRHS,
boolean preferUpdateMethods); boolean preferUpdateMethods);

View File

@ -127,6 +127,11 @@ public class MethodReference extends MappingMethod implements Assignment {
return assignment.getSourceReference(); return assignment.getSourceReference();
} }
@Override
public String getSourcePresenceCheckerReference() {
return assignment.getSourcePresenceCheckerReference();
}
@Override @Override
public Type getSourceType() { public Type getSourceType() {
return assignment.getSourceType(); return assignment.getSourceType();

View File

@ -203,6 +203,7 @@ public class PropertyMapping extends ModelElement {
// initial properties // initial properties
private String defaultValue; private String defaultValue;
private SourceReference sourceReference; private SourceReference sourceReference;
private SourceRHS rightHandSide;
private FormattingParameters formattingParameters; private FormattingParameters formattingParameters;
private SelectionParameters selectionParameters; private SelectionParameters selectionParameters;
@ -228,14 +229,10 @@ public class PropertyMapping extends ModelElement {
public PropertyMapping build() { public PropertyMapping build() {
// handle source // handle source
String sourceElement = getSourceElement(); this.rightHandSide = getSourceRHS( sourceReference );
Type sourceType = getSourceType(); rightHandSide.setUseElementAsSourceTypeForMatching(
SourceRHS sourceRHS = getSourceRHS(); targetWriteAccessorType == TargetWriteAccessorType.ADDER );
if ( targetWriteAccessorType == TargetWriteAccessorType.ADDER && sourceType.isCollectionType() ) { Type sourceType = rightHandSide.getSourceType();
// handle adder, if source is collection then use iterator element type as source type.
sourceType = sourceType.getTypeParameters().get( 0 );
sourceRHS = new SourceRHS( sourceRHS.getSourceReference(), sourceType, existingVariableNames );
}
// all the tricky cases will be excluded for the time being. // all the tricky cases will be excluded for the time being.
boolean preferUpdateMethods; boolean preferUpdateMethods;
@ -248,22 +245,21 @@ public class PropertyMapping extends ModelElement {
Assignment assignment = ctx.getMappingResolver().getTargetAssignment( Assignment assignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
sourceElement,
targetType, targetType,
targetPropertyName, targetPropertyName,
formattingParameters, formattingParameters,
selectionParameters, selectionParameters,
sourceRHS, rightHandSide,
preferUpdateMethods preferUpdateMethods
); );
// No mapping found. Try to forge a mapping // No mapping found. Try to forge a mapping
if ( assignment == null ) { if ( assignment == null ) {
if ( (sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType() ) { if ( (sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType() ) {
assignment = forgeIterableMapping( sourceType, targetType, sourceRHS, method.getExecutable() ); assignment = forgeIterableMapping( sourceType, targetType, rightHandSide, method.getExecutable() );
} }
else if ( sourceType.isMapType() && targetType.isMapType() ) { else if ( sourceType.isMapType() && targetType.isMapType() ) {
assignment = forgeMapMapping( sourceType, targetType, sourceRHS, method.getExecutable() ); assignment = forgeMapMapping( sourceType, targetType, rightHandSide, method.getExecutable() );
} }
} }
@ -282,30 +278,30 @@ public class PropertyMapping extends ModelElement {
ctx.getMessager().printMessage( ctx.getMessager().printMessage(
method.getExecutable(), method.getExecutable(),
Message.PROPERTYMAPPING_MAPPING_NOT_FOUND, Message.PROPERTYMAPPING_MAPPING_NOT_FOUND,
sourceElement, rightHandSide.getSourceErrorMessagePart(),
targetType, targetType,
targetPropertyName, targetPropertyName,
targetType, targetType,
getSourceType() /* original source type */ rightHandSide.getSourceType() /* original source type */
); );
} }
return new PropertyMapping( return new PropertyMapping(
targetPropertyName, targetPropertyName,
sourceReference.getParameter().getName(), rightHandSide.getSourceParameterName(),
targetWriteAccessor.getSimpleName().toString(), targetWriteAccessor.getSimpleName().toString(),
ValueProvider.of( targetReadAccessor ), ValueProvider.of( targetReadAccessor ),
targetType, targetType,
localTargetVarName, localTargetVarName,
assignment, assignment,
dependsOn, dependsOn,
getDefaultValueAssignment() getDefaultValueAssignment( assignment )
); );
} }
private Assignment getDefaultValueAssignment() { private Assignment getDefaultValueAssignment( Assignment rhs ) {
if ( defaultValue != null if ( defaultValue != null
&& ( !getSourceType().isPrimitive() || getSourcePresenceCheckerRef() != null) ) { && ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null) ) {
// cannot check on null source if source is primitive unless it has a presenche checker // cannot check on null source if source is primitive unless it has a presenche checker
PropertyMapping build = new ConstantMappingBuilder() PropertyMapping build = new ConstantMappingBuilder()
.constantExpression( '"' + defaultValue + '"' ) .constantExpression( '"' + defaultValue + '"' )
@ -340,23 +336,23 @@ public class PropertyMapping extends ModelElement {
} }
private Assignment assignToPlainViaSetter(Type sourceType, Type targetType, Assignment rightHandSide) { private Assignment assignToPlainViaSetter(Type sourceType, Type targetType, Assignment rhs) {
Assignment result; Assignment result;
if ( rightHandSide.isUpdateMethod() ) { if ( rhs.isUpdateMethod() ) {
if ( targetReadAccessor == null ) { if ( targetReadAccessor == null ) {
ctx.getMessager().printMessage( method.getExecutable(), ctx.getMessager().printMessage( method.getExecutable(),
Message.PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE, Message.PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE,
targetPropertyName ); targetPropertyName );
} }
Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null );
result = new UpdateWrapper( rightHandSide, method.getThrownTypes(), factoryMethod, result = new UpdateWrapper( rhs, method.getThrownTypes(), factoryMethod,
targetType, isFieldAssignment() targetType, isFieldAssignment()
); );
} }
else { else {
result = new SetterWrapper( rightHandSide, method.getThrownTypes(), isFieldAssignment() ); result = new SetterWrapper( rhs, method.getThrownTypes(), isFieldAssignment() );
} }
// if the sourceReference is the bean mapping method parameter itself, no null check is required // if the sourceReference is the bean mapping method parameter itself, no null check is required
@ -367,20 +363,20 @@ public class PropertyMapping extends ModelElement {
// add a null / presence checked when required // add a null / presence checked when required
if ( sourceType.isPrimitive() ) { if ( sourceType.isPrimitive() ) {
if ( getSourcePresenceCheckerRef() != null ) { if ( rhs.getSourcePresenceCheckerReference() != null ) {
result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); result = new NullCheckWrapper( result );
} }
} }
else { else {
if ( result.isUpdateMethod() ) { if ( result.isUpdateMethod() ) {
result = new UpdateNullCheckWrapper( result, getSourcePresenceCheckerRef(), isFieldAssignment() ); result = new UpdateNullCheckWrapper( result, isFieldAssignment() );
} }
else if ( getSourcePresenceCheckerRef() != null ) { else if ( rhs.getSourcePresenceCheckerReference() != null ) {
result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); result = new NullCheckWrapper( result );
} }
else if ( ALWAYS.equals( method.getMapperConfiguration().getNullValueCheckStrategy() ) ) { else if ( ALWAYS.equals( method.getMapperConfiguration().getNullValueCheckStrategy() ) ) {
result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); result = new NullCheckWrapper( result );
useLocalVarWhenNested( result ); useLocalVarWhenNested( result );
} }
else if ( result.getType() == TYPE_CONVERTED else if ( result.getType() == TYPE_CONVERTED
@ -389,7 +385,7 @@ public class PropertyMapping extends ModelElement {
|| (result.getType() == DIRECT && targetType.isPrimitive() ) ) { || (result.getType() == DIRECT && targetType.isPrimitive() ) ) {
// for primitive types null check is not possible at all, but a conversion needs // for primitive types null check is not possible at all, but a conversion needs
// a null check. // a null check.
result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); result = new NullCheckWrapper( result );
useLocalVarWhenNested( result ); useLocalVarWhenNested( result );
} }
} }
@ -410,13 +406,13 @@ public class PropertyMapping extends ModelElement {
Assignment result = rightHandSide; Assignment result = rightHandSide;
if ( getSourceType().isCollectionType() ) { if ( result.getSourceType().isCollectionType() ) {
result = new AdderWrapper( result, method.getThrownTypes(), isFieldAssignment() ); result = new AdderWrapper( result, method.getThrownTypes(), isFieldAssignment() );
} }
else { else {
// Possibly adding null to a target collection. So should be surrounded by an null check. // Possibly adding null to a target collection. So should be surrounded by an null check.
result = new SetterWrapper( result, method.getThrownTypes(), isFieldAssignment() ); result = new SetterWrapper( result, method.getThrownTypes(), isFieldAssignment() );
result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); result = new NullCheckWrapper( result );
} }
return result; return result;
} }
@ -450,7 +446,6 @@ public class PropertyMapping extends ModelElement {
result = new SetterWrapperForCollectionsAndMaps( result = new SetterWrapperForCollectionsAndMaps(
result, result,
method.getThrownTypes(), method.getThrownTypes(),
getSourcePresenceCheckerRef(),
existingVariableNames, existingVariableNames,
targetType, targetType,
ALWAYS == method.getMapperConfiguration().getNullValueCheckStrategy(), ALWAYS == method.getMapperConfiguration().getNullValueCheckStrategy(),
@ -463,7 +458,6 @@ public class PropertyMapping extends ModelElement {
// target accessor is getter, so wrap the setter in getter map/ collection handling // target accessor is getter, so wrap the setter in getter map/ collection handling
result = new GetterWrapperForCollectionsAndMaps( result, result = new GetterWrapperForCollectionsAndMaps( result,
method.getThrownTypes(), method.getThrownTypes(),
getSourcePresenceCheckerRef(),
existingVariableNames, existingVariableNames,
targetType, targetType,
isFieldAssignment() isFieldAssignment()
@ -484,19 +478,34 @@ public class PropertyMapping extends ModelElement {
existingVariableNames, existingVariableNames,
isFieldAssignment() isFieldAssignment()
); );
return new NullCheckWrapper( assignment, getSourcePresenceCheckerRef() ); return new NullCheckWrapper( assignment );
} }
private Type getSourceType() { private SourceRHS getSourceRHS( SourceReference sourceReference ) {
Parameter sourceParam = sourceReference.getParameter(); Parameter sourceParam = sourceReference.getParameter();
List<PropertyEntry> propertyEntries = sourceReference.getPropertyEntries(); List<PropertyEntry> propertyEntries = sourceReference.getPropertyEntries();
// parameter reference
if ( propertyEntries.isEmpty() ) { if ( propertyEntries.isEmpty() ) {
return sourceParam.getType(); return new SourceRHS( sourceParam.getName(),
sourceParam.getType(),
existingVariableNames,
sourceReference.toString()
);
} }
// simple property
else if ( propertyEntries.size() == 1 ) { else if ( propertyEntries.size() == 1 ) {
return last( propertyEntries ).getType(); PropertyEntry propertyEntry = propertyEntries.get( 0 );
String sourceRef = sourceParam.getName() + "." + ValueProvider.of( propertyEntry.getReadAccessor() );
return new SourceRHS( sourceParam.getName(),
sourceRef,
getSourcePresenceCheckerRef( sourceReference ),
propertyEntry.getType(),
existingVariableNames,
sourceReference.toString()
);
} }
// nested property given as dot path
else { else {
Type sourceType = last( propertyEntries ).getType(); Type sourceType = last( propertyEntries ).getType();
if ( sourceType.isPrimitive() && !targetType.isPrimitive() ) { if ( sourceType.isPrimitive() && !targetType.isPrimitive() ) {
@ -505,39 +514,18 @@ public class PropertyMapping extends ModelElement {
// type. The source type becomes the wrapped type in that case. // type. The source type becomes the wrapped type in that case.
sourceType = ctx.getTypeFactory().getWrappedType( sourceType ); sourceType = ctx.getTypeFactory().getWrappedType( sourceType );
} }
return sourceType;
}
}
private SourceRHS getSourceRHS() {
Parameter sourceParam = sourceReference.getParameter();
List<PropertyEntry> propertyEntries = sourceReference.getPropertyEntries();
// parameter reference
if ( propertyEntries.isEmpty() ) {
return new SourceRHS( sourceParam.getName(), getSourceType(), existingVariableNames );
}
// simple property
else if ( propertyEntries.size() == 1 ) {
PropertyEntry propertyEntry = propertyEntries.get( 0 );
String sourceRef = sourceParam.getName() + "." + ValueProvider.of( propertyEntry.getReadAccessor() );
return new SourceRHS( sourceRef, propertyEntry.getType(), existingVariableNames );
}
// nested property given as dot path
else {
// copy mapper configuration from the source method, its the same mapper // copy mapper configuration from the source method, its the same mapper
MapperConfiguration config = method.getMapperConfiguration(); MapperConfiguration config = method.getMapperConfiguration();
// forge a method from the parameter type to the last entry type. // forge a method from the parameter type to the last entry type.
String forgedName = Strings.joinAndCamelize( sourceReference.getElementNames() ); String forgedName = Strings.joinAndCamelize( sourceReference.getElementNames() );
forgedName = Strings.getSaveVariableName( forgedName, ctx.getNamesOfMappingsToGenerate() ); forgedName = Strings.getSaveVariableName( forgedName, ctx.getNamesOfMappingsToGenerate() );
ForgedMethod methodRef = new ForgedMethod( ForgedMethod methodRef = new ForgedMethod( forgedName,
forgedName, sourceReference.getParameter().getType(),
sourceReference.getParameter().getType(), sourceType,
getSourceType(), config,
config, method.getExecutable()
method.getExecutable()
); );
NestedPropertyMappingMethod.Builder builder = new NestedPropertyMappingMethod.Builder(); NestedPropertyMappingMethod.Builder builder = new NestedPropertyMappingMethod.Builder();
NestedPropertyMappingMethod nestedPropertyMapping = builder NestedPropertyMappingMethod nestedPropertyMapping = builder
@ -553,36 +541,24 @@ public class PropertyMapping extends ModelElement {
forgedName = ctx.getExistingMappingMethod( nestedPropertyMapping ).getName(); forgedName = ctx.getExistingMappingMethod( nestedPropertyMapping ).getName();
} }
String sourceRef = forgedName + "( " + sourceParam.getName() + " )"; String sourceRef = forgedName + "( " + sourceParam.getName() + " )";
return new SourceRHS( sourceRef, getSourceType(), existingVariableNames ); SourceRHS sourceRhs = new SourceRHS( sourceParam.getName(),
sourceRef,
} getSourcePresenceCheckerRef( sourceReference ),
} sourceType,
existingVariableNames,
private String getSourceElement() { sourceReference.toString()
Parameter sourceParam = sourceReference.getParameter();
List<PropertyEntry> propertyEntries = sourceReference.getPropertyEntries();
if ( propertyEntries.isEmpty() ) {
return String.format( "parameter \"%s %s\"", sourceParam.getType(), sourceParam.getName() );
}
else if ( propertyEntries.size() == 1 ) {
PropertyEntry propertyEntry = propertyEntries.get( 0 );
return String.format( "property \"%s %s\"", propertyEntry.getType(), propertyEntry.getName() );
}
else {
PropertyEntry lastPropertyEntry = propertyEntries.get( propertyEntries.size() - 1 );
return String.format(
"property \"%s %s\"",
lastPropertyEntry.getType(),
Strings.join( sourceReference.getElementNames(), "." )
); );
return sourceRhs;
} }
} }
private String getSourcePresenceCheckerRef() { private String getSourcePresenceCheckerRef( SourceReference sourceReference ) {
String sourcePresenceChecker = null; String sourcePresenceChecker = null;
if ( !sourceReference.getPropertyEntries().isEmpty() ) { if ( !sourceReference.getPropertyEntries().isEmpty() ) {
Parameter sourceParam = sourceReference.getParameter(); Parameter sourceParam = sourceReference.getParameter();
// TODO is first correct here?? shouldn't it be last since the remainer is checked
// in the forged method?
PropertyEntry propertyEntry = first( sourceReference.getPropertyEntries() ); PropertyEntry propertyEntry = first( sourceReference.getPropertyEntries() );
if ( propertyEntry.getPresenceChecker() != null ) { if ( propertyEntry.getPresenceChecker() != null ) {
sourcePresenceChecker = sourceParam.getName() sourcePresenceChecker = sourceParam.getName()
@ -699,19 +675,18 @@ public class PropertyMapping extends ModelElement {
public PropertyMapping build() { public PropertyMapping build() {
// source // source
String mappedElement = "constant '" + constantExpression + "'"; String sourceErrorMessagePart = "constant '" + constantExpression + "'";
Type sourceType = ctx.getTypeFactory().getType( String.class ); Type sourceType = ctx.getTypeFactory().getType( String.class );
Assignment assignment = null; Assignment assignment = null;
if ( !targetType.isEnumType() ) { if ( !targetType.isEnumType() ) {
assignment = ctx.getMappingResolver().getTargetAssignment( assignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
mappedElement,
targetType, targetType,
targetPropertyName, targetPropertyName,
formattingParameters, formattingParameters,
selectionParameters, selectionParameters,
new SourceRHS( constantExpression, sourceType, existingVariableNames ), new SourceRHS( constantExpression, sourceType, existingVariableNames, sourceErrorMessagePart ),
method.getMappingTargetParameter() != null method.getMappingTargetParameter() != null
); );
} }
@ -780,7 +755,8 @@ public class PropertyMapping extends ModelElement {
// String String quotation marks. // String String quotation marks.
String enumExpression = constantExpression.substring( 1, constantExpression.length() - 1 ); String enumExpression = constantExpression.substring( 1, constantExpression.length() - 1 );
if ( targetType.getEnumConstants().contains( enumExpression ) ) { if ( targetType.getEnumConstants().contains( enumExpression ) ) {
assignment = new SourceRHS( enumExpression, targetType, existingVariableNames ); String sourceErrorMessagePart = "constant '" + constantExpression + "'";
assignment = new SourceRHS( enumExpression, targetType, existingVariableNames, sourceErrorMessagePart );
assignment = new EnumConstantWrapper( assignment, targetType ); assignment = new EnumConstantWrapper( assignment, targetType );
} }
else { else {
@ -806,7 +782,7 @@ public class PropertyMapping extends ModelElement {
} }
public PropertyMapping build() { public PropertyMapping build() {
Assignment assignment = new SourceRHS( javaExpression, null, existingVariableNames ); Assignment assignment = new SourceRHS( javaExpression, null, existingVariableNames, "" );
if ( Executables.isSetterMethod( targetWriteAccessor ) || if ( Executables.isSetterMethod( targetWriteAccessor ) ||
Executables.isFieldAccessor( targetWriteAccessor ) ) { Executables.isFieldAccessor( targetWriteAccessor ) ) {

View File

@ -30,6 +30,8 @@ import org.mapstruct.ap.internal.util.Strings;
/** /**
* SourceRHS Assignment. Right Hand Side (RHS), source part of the assignment. * SourceRHS Assignment. Right Hand Side (RHS), source part of the assignment.
* *
* This class contains all information on the source side of an assignment needed for common use in the mapping.
*
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class SourceRHS extends ModelElement implements Assignment { public class SourceRHS extends ModelElement implements Assignment {
@ -38,11 +40,24 @@ public class SourceRHS extends ModelElement implements Assignment {
private final Type sourceType; private final Type sourceType;
private String sourceLocalVarName; private String sourceLocalVarName;
private final Set<String> existingVariableNames; private final Set<String> existingVariableNames;
private final String sourceErrorMessagePart;
private final String sourcePresenceCheckerReference;
private boolean useElementAsSourceTypeForMatching = false;
private final String sourceParameterName;
public SourceRHS(String sourceReference, Type sourceType, Set<String> existingVariableNames ) { public SourceRHS(String sourceReference, Type sourceType, Set<String> existingVariableNames,
String sourceErrorMessagePart ) {
this( null, sourceReference, null, sourceType, existingVariableNames, sourceErrorMessagePart );
}
public SourceRHS(String sourceParameterName, String sourceReference, String sourcePresenceCheckerReference,
Type sourceType, Set<String> existingVariableNames, String sourceErrorMessagePart ) {
this.sourceReference = sourceReference; this.sourceReference = sourceReference;
this.sourceType = sourceType; this.sourceType = sourceType;
this.existingVariableNames = existingVariableNames; this.existingVariableNames = existingVariableNames;
this.sourceErrorMessagePart = sourceErrorMessagePart;
this.sourcePresenceCheckerReference = sourcePresenceCheckerReference;
this.sourceParameterName = sourceParameterName;
} }
@Override @Override
@ -50,6 +65,11 @@ public class SourceRHS extends ModelElement implements Assignment {
return sourceReference; return sourceReference;
} }
@Override
public String getSourcePresenceCheckerReference() {
return sourcePresenceCheckerReference;
}
@Override @Override
public Type getSourceType() { public Type getSourceType() {
return sourceType; return sourceType;
@ -99,4 +119,32 @@ public class SourceRHS extends ModelElement implements Assignment {
public String toString() { public String toString() {
return sourceReference; return sourceReference;
} }
public String getSourceErrorMessagePart() {
return sourceErrorMessagePart;
}
/**
* The source type that is to be used when resolving the mapping from source to target.
*
* @return the source type to be used in the matching process.
*/
public Type getSourceTypeForMatching() {
return useElementAsSourceTypeForMatching && sourceType.isCollectionType() ?
sourceType.getTypeParameters().get( 0 ) : sourceType;
}
/**
* For collection type, use element as source type to find a suitable mapping method.
*
* @param useElementAsSourceTypeForMatching
*/
public void setUseElementAsSourceTypeForMatching(boolean useElementAsSourceTypeForMatching) {
this.useElementAsSourceTypeForMatching = useElementAsSourceTypeForMatching;
}
public String getSourceParameterName() {
return sourceParameterName;
}
} }

View File

@ -86,6 +86,11 @@ public class TypeConversion extends ModelElement implements Assignment {
return assignment.getSourceReference(); return assignment.getSourceReference();
} }
@Override
public String getSourcePresenceCheckerReference() {
return assignment.getSourcePresenceCheckerReference();
}
@Override @Override
public Type getSourceType() { public Type getSourceType() {
return assignment.getSourceType(); return assignment.getSourceType();

View File

@ -38,8 +38,8 @@ public class AdderWrapper extends AssignmentWrapper {
public AdderWrapper( Assignment decoratedAssignment, List<Type> thrownTypesToExclude, boolean fieldAssignment ) { public AdderWrapper( Assignment decoratedAssignment, List<Type> thrownTypesToExclude, boolean fieldAssignment ) {
super( decoratedAssignment, fieldAssignment ); super( decoratedAssignment, fieldAssignment );
this.thrownTypesToExclude = thrownTypesToExclude; this.thrownTypesToExclude = thrownTypesToExclude;
this.sourceIteratorName = String desiredName = decoratedAssignment.getSourceType().getTypeParameters().get( 0 ).getName();
decoratedAssignment.createLocalVarName( decoratedAssignment.getSourceType().getName() ); this.sourceIteratorName = decoratedAssignment.createLocalVarName( desiredName );
decoratedAssignment.setSourceLocalVarName( sourceIteratorName ); decoratedAssignment.setSourceLocalVarName( sourceIteratorName );
} }
@ -61,7 +61,7 @@ public class AdderWrapper extends AssignmentWrapper {
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
Set<Type> imported = new HashSet<Type>(); Set<Type> imported = new HashSet<Type>();
imported.addAll( super.getImportTypes() ); imported.addAll( super.getImportTypes() );
imported.add( getSourceType() ); imported.add( getSourceType().getTypeParameters().get( 0 ) );
return imported; return imported;
} }

View File

@ -77,6 +77,13 @@ public interface Assignment {
*/ */
String getSourceReference(); String getSourceReference();
/**
* the source presence checker reference
*
* @return source reference
*/
String getSourcePresenceCheckerReference();
/** /**
* the source type used in the matching process * the source type used in the matching process
* *

View File

@ -63,6 +63,11 @@ public abstract class AssignmentWrapper extends ModelElement implements Assignme
return decoratedAssignment.getSourceReference(); return decoratedAssignment.getSourceReference();
} }
@Override
public String getSourcePresenceCheckerReference() {
return decoratedAssignment.getSourcePresenceCheckerReference();
}
@Override @Override
public Type getSourceType() { public Type getSourceType() {
return decoratedAssignment.getSourceType(); return decoratedAssignment.getSourceType();

View File

@ -39,17 +39,14 @@ import org.mapstruct.ap.internal.model.common.Type;
public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAndMaps { public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAndMaps {
/** /**
* constructor for property mapping
*
* @param decoratedAssignment * @param decoratedAssignment
* @param thrownTypesToExclude * @param thrownTypesToExclude
* @param sourcePresenceChecker
* @param existingVariableNames * @param existingVariableNames
* @param targetType * @param targetType
* @param fieldAssignment
*/ */
public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment,
List<Type> thrownTypesToExclude, List<Type> thrownTypesToExclude,
String sourcePresenceChecker,
Set<String> existingVariableNames, Set<String> existingVariableNames,
Type targetType, Type targetType,
boolean fieldAssignment) { boolean fieldAssignment) {
@ -57,28 +54,10 @@ public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd
super( super(
decoratedAssignment, decoratedAssignment,
thrownTypesToExclude, thrownTypesToExclude,
sourcePresenceChecker,
existingVariableNames, existingVariableNames,
targetType, targetType,
fieldAssignment fieldAssignment
); );
} }
/**
* constructor for e.g. constants and expressions
*
* @param decoratedAssignment
* @param thrownTypesToExclude
* @param existingVariableNames
* @param targetType
*/
public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment,
List<Type> thrownTypesToExclude,
Set<String> existingVariableNames,
Type targetType,
boolean fieldAssignment) {
super( decoratedAssignment, thrownTypesToExclude, null, existingVariableNames, targetType, fieldAssignment );
}
} }

View File

@ -25,14 +25,8 @@ package org.mapstruct.ap.internal.model.assignment;
*/ */
public class NullCheckWrapper extends AssignmentWrapper { public class NullCheckWrapper extends AssignmentWrapper {
private final String sourcePresenceChecker; public NullCheckWrapper( Assignment decoratedAssignment ) {
public NullCheckWrapper( Assignment decoratedAssignment, String sourcePresenceChecker ) {
super( decoratedAssignment, false ); super( decoratedAssignment, false );
this.sourcePresenceChecker = sourcePresenceChecker;
} }
public String getSourcePresenceChecker() {
return sourcePresenceChecker;
}
} }

View File

@ -47,7 +47,6 @@ public class SetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd
public SetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, public SetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment,
List<Type> thrownTypesToExclude, List<Type> thrownTypesToExclude,
String sourcePresenceChecker,
Set<String> existingVariableNames, Set<String> existingVariableNames,
Type targetType, Type targetType,
boolean allwaysIncludeNullCheck, boolean allwaysIncludeNullCheck,
@ -57,7 +56,6 @@ public class SetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd
super( super(
decoratedAssignment, decoratedAssignment,
thrownTypesToExclude, thrownTypesToExclude,
sourcePresenceChecker,
existingVariableNames, existingVariableNames,
targetType, targetType,
fieldAssignment fieldAssignment
@ -97,8 +95,4 @@ public class SetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd
return "java.util.EnumSet".equals( targetType.getFullyQualifiedName() ); return "java.util.EnumSet".equals( targetType.getFullyQualifiedName() );
} }
public boolean isFieldAssignment() {
return fieldAssignment;
}
} }

View File

@ -26,16 +26,8 @@ package org.mapstruct.ap.internal.model.assignment;
*/ */
public class UpdateNullCheckWrapper extends AssignmentWrapper { public class UpdateNullCheckWrapper extends AssignmentWrapper {
private final String sourcePresenceChecker; public UpdateNullCheckWrapper(Assignment decoratedAssignment, boolean fieldAssignment) {
public UpdateNullCheckWrapper(Assignment decoratedAssignment, String sourcePresenceChecker,
boolean fieldAssignment) {
super( decoratedAssignment, fieldAssignment ); super( decoratedAssignment, fieldAssignment );
this.sourcePresenceChecker = sourcePresenceChecker;
}
public String getSourcePresenceChecker() {
return sourcePresenceChecker;
} }
} }

View File

@ -35,13 +35,11 @@ import org.mapstruct.ap.internal.util.Strings;
public class WrapperForCollectionsAndMaps extends AssignmentWrapper { public class WrapperForCollectionsAndMaps extends AssignmentWrapper {
private final List<Type> thrownTypesToExclude; private final List<Type> thrownTypesToExclude;
private final String sourcePresenceChecker;
private final String localVarName; private final String localVarName;
private final Type localVarType; private final Type localVarType;
public WrapperForCollectionsAndMaps(Assignment decoratedAssignment, public WrapperForCollectionsAndMaps(Assignment decoratedAssignment,
List<Type> thrownTypesToExclude, List<Type> thrownTypesToExclude,
String sourcePresenceChecker,
Set<String> existingVariableNames, Set<String> existingVariableNames,
Type targetType, Type targetType,
boolean fieldAssignment) { boolean fieldAssignment) {
@ -49,7 +47,6 @@ public class WrapperForCollectionsAndMaps extends AssignmentWrapper {
super( decoratedAssignment, fieldAssignment ); super( decoratedAssignment, fieldAssignment );
this.thrownTypesToExclude = thrownTypesToExclude; this.thrownTypesToExclude = thrownTypesToExclude;
this.sourcePresenceChecker = sourcePresenceChecker;
if ( getType() == AssignmentType.DIRECT && getSourceType() != null ) { if ( getType() == AssignmentType.DIRECT && getSourceType() != null ) {
this.localVarType = getSourceType(); this.localVarType = getSourceType();
} }
@ -83,10 +80,6 @@ public class WrapperForCollectionsAndMaps extends AssignmentWrapper {
return imported; return imported;
} }
public String getSourcePresenceChecker() {
return sourcePresenceChecker;
}
public String getLocalVarName() { public String getLocalVarName() {
return localVarName; return localVarName;
} }

View File

@ -296,4 +296,25 @@ public class SourceReference {
return new SourceReference( replacement, propertyEntries, isValid ); return new SourceReference( replacement, propertyEntries, isValid );
} }
@Override
public String toString() {
if ( propertyEntries.isEmpty() ) {
return String.format( "parameter \"%s %s\"", getParameter().getType(), getParameter().getName() );
}
else if ( propertyEntries.size() == 1 ) {
PropertyEntry propertyEntry = propertyEntries.get( 0 );
return String.format( "property \"%s %s\"", propertyEntry.getType(), propertyEntry.getName() );
}
else {
PropertyEntry lastPropertyEntry = propertyEntries.get( propertyEntries.size() - 1 );
return String.format(
"property \"%s %s\"",
lastPropertyEntry.getType(),
Strings.join( getElementNames(), "." )
);
}
}
} }

View File

@ -104,10 +104,9 @@ public class MappingResolverImpl implements MappingResolver {
} }
@Override @Override
@SuppressWarnings("checkstyle:parameternumber") public Assignment getTargetAssignment(Method mappingMethod, Type targetType, String targetPropertyName,
public Assignment getTargetAssignment(Method mappingMethod, String mappedElement, FormattingParameters formattingParameters, SelectionParameters selectionParameters, SourceRHS sourceRHS,
Type targetType, String targetPropertyName, FormattingParameters formattingParameters, boolean preferUpdateMapping) {
SelectionParameters selectionParameters, SourceRHS sourceRHS, boolean preferUpdateMapping) {
SelectionCriteria criteria = SelectionCriteria criteria =
new SelectionCriteria( selectionParameters, targetPropertyName, preferUpdateMapping, false ); new SelectionCriteria( selectionParameters, targetPropertyName, preferUpdateMapping, false );
@ -122,14 +121,13 @@ public class MappingResolverImpl implements MappingResolver {
ResolvingAttempt attempt = new ResolvingAttempt( ResolvingAttempt attempt = new ResolvingAttempt(
sourceModel, sourceModel,
mappingMethod, mappingMethod,
mappedElement,
dateFormat, dateFormat,
numberFormat, numberFormat,
sourceRHS, sourceRHS,
criteria criteria
); );
return attempt.getTargetAssignment( sourceRHS.getSourceType(), targetType ); return attempt.getTargetAssignment( sourceRHS.getSourceTypeForMatching(), targetType );
} }
@Override @Override
@ -143,7 +141,7 @@ public class MappingResolverImpl implements MappingResolver {
SelectionCriteria criteria = new SelectionCriteria( selectionParameters, null, false, true ); SelectionCriteria criteria = new SelectionCriteria( selectionParameters, null, false, true );
ResolvingAttempt attempt = new ResolvingAttempt( sourceModel, mappingMethod, null, null, null, null, criteria ); ResolvingAttempt attempt = new ResolvingAttempt( sourceModel, mappingMethod, null, null, null, criteria );
List<SourceMethod> matchingSourceMethods = attempt.getMatches( sourceModel, null, targetType ); List<SourceMethod> matchingSourceMethods = attempt.getMatches( sourceModel, null, targetType );
@ -199,7 +197,6 @@ public class MappingResolverImpl implements MappingResolver {
private class ResolvingAttempt { private class ResolvingAttempt {
private final Method mappingMethod; private final Method mappingMethod;
private final String mappedElement;
private final List<SourceMethod> methods; private final List<SourceMethod> methods;
private final String dateFormat; private final String dateFormat;
private final String numberFormat; private final String numberFormat;
@ -212,12 +209,10 @@ public class MappingResolverImpl implements MappingResolver {
// so this set must be cleared. // so this set must be cleared.
private final Set<VirtualMappingMethod> virtualMethodCandidates; private final Set<VirtualMappingMethod> virtualMethodCandidates;
private ResolvingAttempt(List<SourceMethod> sourceModel, Method mappingMethod, String mappedElement, private ResolvingAttempt(List<SourceMethod> sourceModel, Method mappingMethod, String dateFormat,
String dateFormat, String numberFormat, SourceRHS sourceRHS, SelectionCriteria criteria) { String numberFormat, SourceRHS sourceRHS, SelectionCriteria criteria) {
this.mappingMethod = mappingMethod; this.mappingMethod = mappingMethod;
this.mappedElement = mappedElement;
this.methods = filterPossibleCandidateMethods( sourceModel ); this.methods = filterPossibleCandidateMethods( sourceModel );
this.dateFormat = dateFormat; this.dateFormat = dateFormat;
this.numberFormat = numberFormat; this.numberFormat = numberFormat;
@ -520,10 +515,10 @@ public class MappingResolverImpl implements MappingResolver {
// into the target type // into the target type
if ( candidates.size() > 1 ) { if ( candidates.size() > 1 ) {
if ( mappedElement != null ) { if ( sourceRHS.getSourceErrorMessagePart() != null ) {
messager.printMessage( mappingMethod.getExecutable(), messager.printMessage( mappingMethod.getExecutable(),
Message.GENERAL_AMBIGIOUS_MAPPING_METHOD, Message.GENERAL_AMBIGIOUS_MAPPING_METHOD,
mappedElement, sourceRHS.getSourceErrorMessagePart(),
returnType, returnType,
Strings.join( candidates, ", " ) Strings.join( candidates, ", " )
); );

View File

@ -21,7 +21,7 @@
<#import "../macro/CommonMacros.ftl" as lib> <#import "../macro/CommonMacros.ftl" as lib>
<@lib.handleExceptions> <@lib.handleExceptions>
if ( ${sourceReference} != null ) { if ( ${sourceReference} != null ) {
for ( <@includeModel object=sourceType/> ${sourceIteratorName} : ${sourceReference} ) { for ( <@includeModel object=sourceType.typeParameters[0]/> ${sourceIteratorName} : ${sourceReference} ) {
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>; ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>;
} }
} }

View File

@ -25,7 +25,7 @@ if ( ${sourceLocalVarName} != null ) {
<@_assignment object=assignment defaultValue=ext.defaultValueAssignment/> <@_assignment object=assignment defaultValue=ext.defaultValueAssignment/>
} }
<#else> <#else>
if ( <#if sourcePresenceChecker?? >${sourcePresenceChecker}<#else>${sourceReference} != null</#if> ) { if ( <#if sourcePresenceCheckerReference?? >${sourcePresenceCheckerReference}<#else>${sourceReference} != null</#if> ) {
<@_assignment object=assignment defaultValue=ext.defaultValueAssignment/> <@_assignment object=assignment defaultValue=ext.defaultValueAssignment/>
} }
</#if> </#if>

View File

@ -26,7 +26,7 @@
${ext.targetBeanName}.${ext.targetReadAccessorName}.clear(); ${ext.targetBeanName}.${ext.targetReadAccessorName}.clear();
${ext.targetBeanName}.${ext.targetReadAccessorName}.<#if ext.targetType.collectionType>addAll<#else>putAll</#if>( ${localVarName} ); ${ext.targetBeanName}.${ext.targetReadAccessorName}.<#if ext.targetType.collectionType>addAll<#else>putAll</#if>( ${localVarName} );
</@lib.handleNullCheck> </@lib.handleNullCheck>
<#if !ext.defaultValueAssignment?? && !sourcePresenceChecker?? && !allwaysIncludeNullCheck>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleNullCheck --> <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && !allwaysIncludeNullCheck>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleNullCheck -->
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>null</@lib.handleWrite>; ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>null</@lib.handleWrite>;
} }
</#if> </#if>
@ -55,4 +55,4 @@
<#else> <#else>
new <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/></#if>( ${localVarName} ) new <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/></#if>( ${localVarName} )
</#if> </#if>
</@compress></#macro> </@compress></#macro>

View File

@ -19,7 +19,7 @@
--> -->
<#import '../macro/CommonMacros.ftl' as lib> <#import '../macro/CommonMacros.ftl' as lib>
if ( <#if sourcePresenceChecker?? >${sourcePresenceChecker}<#else>${sourceReference} != null</#if> ) { if ( <#if sourcePresenceCheckerReference?? >${sourcePresenceCheckerReference}<#else>${sourceReference} != null</#if> ) {
<@includeModel object=assignment <@includeModel object=assignment
targetBeanName=ext.targetBeanName targetBeanName=ext.targetBeanName
existingInstanceMapping=ext.existingInstanceMapping existingInstanceMapping=ext.existingInstanceMapping

View File

@ -28,8 +28,8 @@
present. present.
--> -->
<#macro handleNullCheck> <#macro handleNullCheck>
<#if sourcePresenceChecker??> <#if sourcePresenceCheckerReference??>
if ( ${sourcePresenceChecker} ) { if ( ${sourcePresenceCheckerReference} ) {
<@includeModel object=localVarType/> ${localVarName} = <@lib.handleAssignment/>; <@includeModel object=localVarType/> ${localVarName} = <@lib.handleAssignment/>;
<#nested> <#nested>
} }