mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
parent
f5ee2c6729
commit
6c838e6e0c
@ -37,7 +37,7 @@ public abstract class AbstractMappingMethodBuilder<B extends AbstractMappingMeth
|
|||||||
}
|
}
|
||||||
|
|
||||||
String name = getName( sourceType, targetType );
|
String name = getName( sourceType, targetType );
|
||||||
name = Strings.getSafeVariableName( name, ctx.getNamesOfMappingsToGenerate() );
|
name = Strings.getSafeVariableName( name, ctx.getReservedNames() );
|
||||||
ForgedMethodHistory history = null;
|
ForgedMethodHistory history = null;
|
||||||
if ( method instanceof ForgedMethod ) {
|
if ( method instanceof ForgedMethod ) {
|
||||||
history = ( (ForgedMethod) method ).getHistory();
|
history = ( (ForgedMethod) method ).getHistory();
|
||||||
|
@ -19,6 +19,7 @@ import org.mapstruct.ap.internal.model.common.Type;
|
|||||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||||
|
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||||
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
@ -82,18 +83,22 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
|||||||
new HashSet<>(),
|
new HashSet<>(),
|
||||||
errorMessagePart
|
errorMessagePart
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SelectionCriteria criteria = SelectionCriteria.forMappingMethods( selectionParameters,
|
||||||
|
callingContextTargetPropertyName,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
Assignment assignment = ctx.getMappingResolver().getTargetAssignment(
|
Assignment assignment = ctx.getMappingResolver().getTargetAssignment(
|
||||||
method,
|
method,
|
||||||
targetElementType,
|
targetElementType,
|
||||||
callingContextTargetPropertyName,
|
|
||||||
formattingParameters,
|
formattingParameters,
|
||||||
selectionParameters,
|
criteria,
|
||||||
sourceRHS,
|
sourceRHS,
|
||||||
false,
|
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( assignment == null ) {
|
if ( assignment == null && !criteria.hasQualfiers() ) {
|
||||||
assignment = forgeMapping( sourceRHS, sourceElementType, targetElementType );
|
assignment = forgeMapping( sourceRHS, sourceElementType, targetElementType );
|
||||||
if ( assignment != null ) {
|
if ( assignment != null ) {
|
||||||
ctx.getMessager().note( 2, Message.ITERABLEMAPPING_CREATE_ELEMENT_NOTE, assignment );
|
ctx.getMessager().note( 2, Message.ITERABLEMAPPING_CREATE_ELEMENT_NOTE, assignment );
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.model;
|
package org.mapstruct.ap.internal.model;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -22,10 +20,13 @@ import org.mapstruct.ap.internal.model.common.Type;
|
|||||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||||
|
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||||
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
|
|
||||||
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
|
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
|
||||||
* values are mapped either by a {@link TypeConversion} or another mapping method if required.
|
* values are mapped either by a {@link TypeConversion} or another mapping method if required.
|
||||||
@ -86,18 +87,20 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
|||||||
Type keyTargetType = resultTypeParams.get( 0 ).getTypeBound();
|
Type keyTargetType = resultTypeParams.get( 0 ).getTypeBound();
|
||||||
|
|
||||||
SourceRHS keySourceRHS = new SourceRHS( "entry.getKey()", keySourceType, new HashSet<>(), "map key" );
|
SourceRHS keySourceRHS = new SourceRHS( "entry.getKey()", keySourceType, new HashSet<>(), "map key" );
|
||||||
|
|
||||||
|
SelectionCriteria keyCriteria =
|
||||||
|
SelectionCriteria.forMappingMethods( keySelectionParameters, null, false );
|
||||||
|
|
||||||
Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment(
|
Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment(
|
||||||
method,
|
method,
|
||||||
keyTargetType,
|
keyTargetType,
|
||||||
null, // there is no targetPropertyName
|
|
||||||
keyFormattingParameters,
|
keyFormattingParameters,
|
||||||
keySelectionParameters,
|
keyCriteria,
|
||||||
keySourceRHS,
|
keySourceRHS,
|
||||||
false,
|
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( keyAssignment == null ) {
|
if ( keyAssignment == null && !keyCriteria.hasQualfiers( ) ) {
|
||||||
keyAssignment = forgeMapping( keySourceRHS, keySourceType, keyTargetType );
|
keyAssignment = forgeMapping( keySourceRHS, keySourceType, keyTargetType );
|
||||||
if ( keyAssignment != null ) {
|
if ( keyAssignment != null ) {
|
||||||
ctx.getMessager().note( 2, Message.MAPMAPPING_CREATE_KEY_NOTE, keyAssignment );
|
ctx.getMessager().note( 2, Message.MAPMAPPING_CREATE_KEY_NOTE, keyAssignment );
|
||||||
@ -133,14 +136,16 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
|||||||
|
|
||||||
SourceRHS valueSourceRHS = new SourceRHS( "entry.getValue()", valueSourceType, new HashSet<>(),
|
SourceRHS valueSourceRHS = new SourceRHS( "entry.getValue()", valueSourceType, new HashSet<>(),
|
||||||
"map value" );
|
"map value" );
|
||||||
|
|
||||||
|
SelectionCriteria valueCriteria =
|
||||||
|
SelectionCriteria.forMappingMethods( valueSelectionParameters, null, false );
|
||||||
|
|
||||||
Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment(
|
Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment(
|
||||||
method,
|
method,
|
||||||
valueTargetType,
|
valueTargetType,
|
||||||
null, // there is no targetPropertyName
|
|
||||||
valueFormattingParameters,
|
valueFormattingParameters,
|
||||||
valueSelectionParameters,
|
valueCriteria,
|
||||||
valueSourceRHS,
|
valueSourceRHS,
|
||||||
false,
|
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -154,7 +159,7 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( valueAssignment == null ) {
|
if ( valueAssignment == null && !valueCriteria.hasQualfiers( ) ) {
|
||||||
valueAssignment = forgeMapping( valueSourceRHS, valueSourceType, valueTargetType );
|
valueAssignment = forgeMapping( valueSourceRHS, valueSourceType, valueTargetType );
|
||||||
if ( valueAssignment != null ) {
|
if ( valueAssignment != null ) {
|
||||||
ctx.getMessager().note( 2, Message.MAPMAPPING_CREATE_VALUE_NOTE, valueAssignment );
|
ctx.getMessager().note( 2, Message.MAPMAPPING_CREATE_VALUE_NOTE, valueAssignment );
|
||||||
@ -221,6 +226,7 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
|||||||
protected boolean shouldUsePropertyNamesInHistory() {
|
protected boolean shouldUsePropertyNamesInHistory() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapMappingMethod(Method method, Collection<String> existingVariableNames, Assignment keyAssignment,
|
private MapMappingMethod(Method method, Collection<String> existingVariableNames, Assignment keyAssignment,
|
||||||
|
@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.model;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -23,8 +24,8 @@ import org.mapstruct.ap.internal.model.common.Type;
|
|||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
|
||||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||||
|
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||||
import org.mapstruct.ap.internal.option.Options;
|
import org.mapstruct.ap.internal.option.Options;
|
||||||
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
||||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||||
@ -76,11 +77,10 @@ public class MappingBuilderContext {
|
|||||||
*
|
*
|
||||||
* @param mappingMethod target mapping method
|
* @param mappingMethod target mapping method
|
||||||
* @param targetType return type to match
|
* @param targetType return type to match
|
||||||
* @param targetPropertyName name of the target property
|
|
||||||
* @param formattingParameters used for formatting dates and numbers
|
* @param formattingParameters used for formatting dates and numbers
|
||||||
* @param selectionParameters parameters used in the selection process
|
* @param criteria parameters criteria in the selection process
|
||||||
* @param sourceRHS source information
|
* @param sourceRHS source information
|
||||||
* @param preferUpdateMethods selection should prefer update methods when present.
|
* @param positionHint the mirror for reporting problems
|
||||||
*
|
*
|
||||||
* @return an assignment to a method parameter, which can either be:
|
* @return an assignment to a method parameter, which can either be:
|
||||||
* <ol>
|
* <ol>
|
||||||
@ -90,10 +90,10 @@ public class MappingBuilderContext {
|
|||||||
* <li>null, no assignment found</li>
|
* <li>null, no assignment found</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*/
|
*/
|
||||||
Assignment getTargetAssignment(Method mappingMethod, Type targetType, String targetPropertyName,
|
Assignment getTargetAssignment(Method mappingMethod, Type targetType,
|
||||||
FormattingParameters formattingParameters,
|
FormattingParameters formattingParameters,
|
||||||
SelectionParameters selectionParameters, SourceRHS sourceRHS,
|
SelectionCriteria criteria, SourceRHS sourceRHS,
|
||||||
boolean preferUpdateMethods, AnnotationMirror mirror);
|
AnnotationMirror positionHint);
|
||||||
|
|
||||||
Set<SupportingMappingMethod> getUsedSupportedMappings();
|
Set<SupportingMappingMethod> getUsedSupportedMappings();
|
||||||
}
|
}
|
||||||
@ -191,12 +191,18 @@ public class MappingBuilderContext {
|
|||||||
return mappingsToGenerate;
|
return mappingsToGenerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getNamesOfMappingsToGenerate() {
|
public List<String> getReservedNames() {
|
||||||
List<String> nameList = new ArrayList<>();
|
Set<String> nameSet = new HashSet<>();
|
||||||
for ( MappingMethod method : mappingsToGenerate ) {
|
for ( MappingMethod method : mappingsToGenerate ) {
|
||||||
nameList.add( method.getName() );
|
nameSet.add( method.getName() );
|
||||||
}
|
}
|
||||||
return nameList;
|
// add existing names
|
||||||
|
for ( SourceMethod method : sourceModel) {
|
||||||
|
if ( method.isAbstract() ) {
|
||||||
|
nameSet.add( method.getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>( nameSet );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MappingMethod getExistingMappingMethod(MappingMethod newMappingMethod) {
|
public MappingMethod getExistingMappingMethod(MappingMethod newMappingMethod) {
|
||||||
|
@ -36,6 +36,7 @@ import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods;
|
|||||||
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
||||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||||
import org.mapstruct.ap.internal.model.source.SourceReference;
|
import org.mapstruct.ap.internal.model.source.SourceReference;
|
||||||
|
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||||
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
|
||||||
@ -310,24 +311,27 @@ public class PropertyMapping extends ModelElement {
|
|||||||
preferUpdateMethods = method.getMappingTargetParameter() != null;
|
preferUpdateMethods = method.getMappingTargetParameter() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectionCriteria criteria = SelectionCriteria.forMappingMethods( selectionParameters,
|
||||||
|
targetPropertyName,
|
||||||
|
preferUpdateMethods
|
||||||
|
);
|
||||||
|
|
||||||
// forge a method instead of resolving one when there are mapping options.
|
// forge a method instead of resolving one when there are mapping options.
|
||||||
Assignment assignment = null;
|
Assignment assignment = null;
|
||||||
if ( forgeMethodWithMappingOptions == null ) {
|
if ( forgeMethodWithMappingOptions == null ) {
|
||||||
assignment = ctx.getMappingResolver().getTargetAssignment(
|
assignment = ctx.getMappingResolver().getTargetAssignment(
|
||||||
method,
|
method,
|
||||||
targetType,
|
targetType,
|
||||||
targetPropertyName,
|
|
||||||
formattingParameters,
|
formattingParameters,
|
||||||
selectionParameters,
|
criteria,
|
||||||
rightHandSide,
|
rightHandSide,
|
||||||
preferUpdateMethods,
|
|
||||||
positionHint
|
positionHint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Type sourceType = rightHandSide.getSourceType();
|
Type sourceType = rightHandSide.getSourceType();
|
||||||
// No mapping found. Try to forge a mapping
|
// No mapping found. Try to forge a mapping
|
||||||
if ( assignment == null ) {
|
if ( assignment == null && !criteria.hasQualfiers() ) {
|
||||||
if ( (sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType() ) {
|
if ( (sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType() ) {
|
||||||
assignment = forgeIterableMapping( sourceType, targetType, rightHandSide, method.getExecutable() );
|
assignment = forgeIterableMapping( sourceType, targetType, rightHandSide, method.getExecutable() );
|
||||||
}
|
}
|
||||||
@ -595,7 +599,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
|
|
||||||
// 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.getSafeVariableName( forgedName, ctx.getNamesOfMappingsToGenerate() );
|
forgedName = Strings.getSafeVariableName( forgedName, ctx.getReservedNames() );
|
||||||
ForgedMethod methodRef = new ForgedMethod(
|
ForgedMethod methodRef = new ForgedMethod(
|
||||||
forgedName,
|
forgedName,
|
||||||
sourceReference.getParameter().getType(),
|
sourceReference.getParameter().getType(),
|
||||||
@ -699,7 +703,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
private ForgedMethod prepareForgedMethod(Type sourceType, Type targetType, SourceRHS source,
|
private ForgedMethod prepareForgedMethod(Type sourceType, Type targetType, SourceRHS source,
|
||||||
ExecutableElement element, String suffix) {
|
ExecutableElement element, String suffix) {
|
||||||
String name = getName( sourceType, targetType );
|
String name = getName( sourceType, targetType );
|
||||||
name = Strings.getSafeVariableName( name, ctx.getNamesOfMappingsToGenerate() );
|
name = Strings.getSafeVariableName( name, ctx.getReservedNames() );
|
||||||
|
|
||||||
// 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();
|
||||||
@ -751,7 +755,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String name = getName( sourceType, targetType );
|
String name = getName( sourceType, targetType );
|
||||||
name = Strings.getSafeVariableName( name, ctx.getNamesOfMappingsToGenerate() );
|
name = Strings.getSafeVariableName( name, ctx.getReservedNames() );
|
||||||
|
|
||||||
List<Parameter> parameters = new ArrayList<>( method.getContextParameters() );
|
List<Parameter> parameters = new ArrayList<>( method.getContextParameters() );
|
||||||
Type returnType;
|
Type returnType;
|
||||||
@ -873,16 +877,19 @@ public class PropertyMapping extends ModelElement {
|
|||||||
}
|
}
|
||||||
Type sourceType = ctx.getTypeFactory().getTypeForLiteral( baseForLiteral );
|
Type sourceType = ctx.getTypeFactory().getTypeForLiteral( baseForLiteral );
|
||||||
|
|
||||||
|
SelectionCriteria criteria = SelectionCriteria.forMappingMethods( selectionParameters,
|
||||||
|
targetPropertyName,
|
||||||
|
method.getMappingTargetParameter() != null
|
||||||
|
);
|
||||||
|
|
||||||
Assignment assignment = null;
|
Assignment assignment = null;
|
||||||
if ( !targetType.isEnumType() ) {
|
if ( !targetType.isEnumType() ) {
|
||||||
assignment = ctx.getMappingResolver().getTargetAssignment(
|
assignment = ctx.getMappingResolver().getTargetAssignment(
|
||||||
method,
|
method,
|
||||||
targetType,
|
targetType,
|
||||||
targetPropertyName,
|
|
||||||
formattingParameters,
|
formattingParameters,
|
||||||
selectionParameters,
|
criteria,
|
||||||
new SourceRHS( constantExpression, sourceType, existingVariableNames, sourceErrorMessagePart ),
|
new SourceRHS( constantExpression, sourceType, existingVariableNames, sourceErrorMessagePart ),
|
||||||
method.getMappingTargetParameter() != null,
|
|
||||||
positionHint
|
positionHint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -138,12 +138,7 @@ public class QualifierSelector implements MethodSelector {
|
|||||||
matches.add( candidate );
|
matches.add( candidate );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( !matches.isEmpty() ) {
|
return matches;
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,10 @@ public class SelectionCriteria {
|
|||||||
this.preferUpdateMapping = preferUpdateMapping;
|
this.preferUpdateMapping = preferUpdateMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasQualfiers() {
|
||||||
|
return !qualifiedByNames.isEmpty() || !qualifiers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public static SelectionCriteria forMappingMethods(SelectionParameters selectionParameters,
|
public static SelectionCriteria forMappingMethods(SelectionParameters selectionParameters,
|
||||||
String targetPropertyName, boolean preferUpdateMapping) {
|
String targetPropertyName, boolean preferUpdateMapping) {
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ import org.mapstruct.ap.internal.model.common.SourceRHS;
|
|||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
|
||||||
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMappingMethods;
|
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMappingMethods;
|
||||||
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod;
|
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod;
|
||||||
import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
|
import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
|
||||||
@ -95,12 +94,10 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Assignment getTargetAssignment(Method mappingMethod, Type targetType, String targetPropertyName,
|
public Assignment getTargetAssignment(Method mappingMethod, Type targetType,
|
||||||
FormattingParameters formattingParameters, SelectionParameters selectionParameters, SourceRHS sourceRHS,
|
FormattingParameters formattingParameters,
|
||||||
boolean preferUpdateMapping, AnnotationMirror positionHint) {
|
SelectionCriteria criteria, SourceRHS sourceRHS,
|
||||||
|
AnnotationMirror positionHint) {
|
||||||
SelectionCriteria criteria =
|
|
||||||
SelectionCriteria.forMappingMethods( selectionParameters, targetPropertyName, preferUpdateMapping );
|
|
||||||
|
|
||||||
ResolvingAttempt attempt = new ResolvingAttempt(
|
ResolvingAttempt attempt = new ResolvingAttempt(
|
||||||
sourceModel,
|
sourceModel,
|
||||||
@ -182,12 +179,13 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// then direct assignable
|
// then direct assignable
|
||||||
if ( sourceType.isAssignableTo( targetType ) ||
|
if ( !hasQualfiers() ) {
|
||||||
|
if ( sourceType.isAssignableTo( targetType ) ||
|
||||||
isAssignableThroughCollectionCopyConstructor( sourceType, targetType ) ) {
|
isAssignableThroughCollectionCopyConstructor( sourceType, targetType ) ) {
|
||||||
Assignment simpleAssignment = sourceRHS;
|
Assignment simpleAssignment = sourceRHS;
|
||||||
return simpleAssignment;
|
return simpleAssignment;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point the SourceType will either
|
// At this point the SourceType will either
|
||||||
// 1. be a String
|
// 1. be a String
|
||||||
// 2. or when its a primitive / wrapped type and analysis successful equal to its TargetType. But in that
|
// 2. or when its a primitive / wrapped type and analysis successful equal to its TargetType. But in that
|
||||||
@ -196,25 +194,29 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
// in NativeType is not successful. We don't want to go through type conversion, double mappings etc.
|
// in NativeType is not successful. We don't want to go through type conversion, double mappings etc.
|
||||||
// with something that we already know to be wrong.
|
// with something that we already know to be wrong.
|
||||||
if ( sourceType.isLiteral()
|
if ( sourceType.isLiteral()
|
||||||
&& "java.lang.String".equals( sourceType.getFullyQualifiedName( ) )
|
&& "java.lang.String".equals( sourceType.getFullyQualifiedName() )
|
||||||
&& targetType.isNative() ) {
|
&& targetType.isNative() ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// then type conversion
|
// then type conversion
|
||||||
ConversionAssignment conversion = resolveViaConversion( sourceType, targetType );
|
if ( !hasQualfiers() ) {
|
||||||
if ( conversion != null ) {
|
ConversionAssignment conversion = resolveViaConversion( sourceType, targetType );
|
||||||
conversion.reportMessageWhenNarrowing( messager, this );
|
if ( conversion != null ) {
|
||||||
conversion.getAssignment().setAssignment( sourceRHS );
|
conversion.reportMessageWhenNarrowing( messager, this );
|
||||||
return conversion.getAssignment();
|
conversion.getAssignment().setAssignment( sourceRHS );
|
||||||
|
return conversion.getAssignment();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for a built-in method
|
// check for a built-in method
|
||||||
Assignment builtInMethod = resolveViaBuiltInMethod( sourceType, targetType );
|
if (!hasQualfiers() ) {
|
||||||
if ( builtInMethod != null ) {
|
Assignment builtInMethod = resolveViaBuiltInMethod( sourceType, targetType );
|
||||||
builtInMethod.setAssignment( sourceRHS );
|
if ( builtInMethod != null ) {
|
||||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
builtInMethod.setAssignment( sourceRHS );
|
||||||
return builtInMethod;
|
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||||
|
return builtInMethod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 step method, first: method(method(source))
|
// 2 step method, first: method(method(source))
|
||||||
@ -235,7 +237,7 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
selectionCriteria.setPreferUpdateMapping( false );
|
selectionCriteria.setPreferUpdateMapping( false );
|
||||||
|
|
||||||
// 2 step method, finally: conversion(method(source))
|
// 2 step method, finally: conversion(method(source))
|
||||||
conversion = resolveViaMethodAndConversion( sourceType, targetType );
|
ConversionAssignment conversion = resolveViaMethodAndConversion( sourceType, targetType );
|
||||||
if ( conversion != null ) {
|
if ( conversion != null ) {
|
||||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||||
return conversion.getAssignment();
|
return conversion.getAssignment();
|
||||||
@ -245,6 +247,10 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasQualfiers() {
|
||||||
|
return selectionCriteria != null && selectionCriteria.hasQualfiers();
|
||||||
|
}
|
||||||
|
|
||||||
private ConversionAssignment resolveViaConversion(Type sourceType, Type targetType) {
|
private ConversionAssignment resolveViaConversion(Type sourceType, Type targetType) {
|
||||||
|
|
||||||
ConversionProvider conversionProvider = conversions.getConversion( sourceType, targetType );
|
ConversionProvider conversionProvider = conversions.getConversion( sourceType, targetType );
|
||||||
@ -379,9 +385,9 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* then this method tries to resolve this combination and make a mapping methodY( conversionX ( parameter ) )
|
* then this method tries to resolve this combination and make a mapping methodY( conversionX ( parameter ) )
|
||||||
*
|
*
|
||||||
* In stead of directly using a built in method candidate all the return types as 'B' of all available built-in
|
* In stead of directly using a built in method candidate all the return types as 'B' of all available built-in
|
||||||
* methods are used to resolve a mapping (assignment) from result type to 'B'. If a match is found, an attempt
|
* methods are used to resolve a mapping (assignment) from result type to 'B'. If a match is found, an attempt
|
||||||
* is done to find a matching type conversion.
|
* is done to find a matching type conversion.
|
||||||
*/
|
*/
|
||||||
private Assignment resolveViaConversionAndMethod(Type sourceType, Type targetType) {
|
private Assignment resolveViaConversionAndMethod(Type sourceType, Type targetType) {
|
||||||
|
|
||||||
@ -503,7 +509,8 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
if ( candidates.size() > 1 ) {
|
if ( candidates.size() > 1 ) {
|
||||||
|
|
||||||
if ( sourceRHS.getSourceErrorMessagePart() != null ) {
|
if ( sourceRHS.getSourceErrorMessagePart() != null ) {
|
||||||
messager.printMessage( mappingMethod.getExecutable(),
|
messager.printMessage(
|
||||||
|
mappingMethod.getExecutable(),
|
||||||
positionHint,
|
positionHint,
|
||||||
Message.GENERAL_AMBIGIOUS_MAPPING_METHOD,
|
Message.GENERAL_AMBIGIOUS_MAPPING_METHOD,
|
||||||
sourceRHS.getSourceErrorMessagePart(),
|
sourceRHS.getSourceErrorMessagePart(),
|
||||||
@ -512,7 +519,8 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
messager.printMessage( mappingMethod.getExecutable(),
|
messager.printMessage(
|
||||||
|
mappingMethod.getExecutable(),
|
||||||
positionHint,
|
positionHint,
|
||||||
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
||||||
returnType,
|
returnType,
|
||||||
@ -535,7 +543,8 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
return MethodReference.forMapperReference(
|
return MethodReference.forMapperReference(
|
||||||
method.getMethod(),
|
method.getMethod(),
|
||||||
mapperReference,
|
mapperReference,
|
||||||
method.getParameterBindings() );
|
method.getParameterBindings()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -546,14 +555,14 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
boolean bothCollectionOrMap = false;
|
boolean bothCollectionOrMap = false;
|
||||||
|
|
||||||
if ( ( sourceType.isCollectionType() && targetType.isCollectionType() ) ||
|
if ( ( sourceType.isCollectionType() && targetType.isCollectionType() ) ||
|
||||||
( sourceType.isMapType() && targetType.isMapType() ) ) {
|
( sourceType.isMapType() && targetType.isMapType() ) ) {
|
||||||
bothCollectionOrMap = true;
|
bothCollectionOrMap = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( bothCollectionOrMap ) {
|
if ( bothCollectionOrMap ) {
|
||||||
return hasCompatibleCopyConstructor(
|
return hasCompatibleCopyConstructor(
|
||||||
sourceType,
|
sourceType,
|
||||||
targetType.getImplementationType() != null ? targetType.getImplementationType() : targetType
|
targetType.getImplementationType() != null ? targetType.getImplementationType() : targetType
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,8 +574,9 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
*
|
*
|
||||||
* @param sourceType the source type
|
* @param sourceType the source type
|
||||||
* @param targetType the target type
|
* @param targetType the target type
|
||||||
|
*
|
||||||
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
|
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
private boolean hasCompatibleCopyConstructor(Type sourceType, Type targetType) {
|
private boolean hasCompatibleCopyConstructor(Type sourceType, Type targetType) {
|
||||||
if ( targetType.isPrimitive() ) {
|
if ( targetType.isPrimitive() ) {
|
||||||
@ -584,7 +594,8 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
// get the constructor resolved against the type arguments of specific target type
|
// get the constructor resolved against the type arguments of specific target type
|
||||||
ExecutableType typedConstructor = (ExecutableType) typeUtils.asMemberOf(
|
ExecutableType typedConstructor = (ExecutableType) typeUtils.asMemberOf(
|
||||||
(DeclaredType) targetType.getTypeMirror(),
|
(DeclaredType) targetType.getTypeMirror(),
|
||||||
constructor );
|
constructor
|
||||||
|
);
|
||||||
|
|
||||||
TypeMirror parameterType = Collections.first( typedConstructor.getParameterTypes() );
|
TypeMirror parameterType = Collections.first( typedConstructor.getParameterTypes() );
|
||||||
if ( parameterType.getKind() == TypeKind.DECLARED ) {
|
if ( parameterType.getKind() == TypeKind.DECLARED ) {
|
||||||
@ -603,7 +614,8 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
parameterType = typeUtils.getDeclaredType(
|
parameterType = typeUtils.getDeclaredType(
|
||||||
(TypeElement) p.asElement(),
|
(TypeElement) p.asElement(),
|
||||||
typeArguments.toArray( new TypeMirror[typeArguments.size()] ) );
|
typeArguments.toArray( new TypeMirror[typeArguments.size()] )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeUtils.isAssignable( sourceType.getTypeMirror(), parameterType ) ) {
|
if ( typeUtils.isAssignable( sourceType.getTypeMirror(), parameterType ) ) {
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._1714;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface Issue1714Mapper {
|
||||||
|
|
||||||
|
Issue1714Mapper INSTANCE = Mappers.getMapper( Issue1714Mapper.class );
|
||||||
|
|
||||||
|
@Mapping(source = "programInstance", target = "seasonNumber", qualifiedByName = "getSeasonNumber")
|
||||||
|
OfferEntity map(OnDemand offerStatusDTO);
|
||||||
|
|
||||||
|
@Named("getTitle")
|
||||||
|
default String mapTitle(Program programInstance) {
|
||||||
|
return "dont care";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("getSeasonNumber")
|
||||||
|
default Integer mapSeasonNumber(Program programInstance) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OfferEntity {
|
||||||
|
|
||||||
|
private String seasonNumber;
|
||||||
|
|
||||||
|
public String getSeasonNumber() {
|
||||||
|
return seasonNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeasonNumber(String seasonNumber) {
|
||||||
|
this.seasonNumber = seasonNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnDemand {
|
||||||
|
|
||||||
|
private Program programInstance;
|
||||||
|
|
||||||
|
public Program getProgramInstance() {
|
||||||
|
return programInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramInstance(Program programInstance) {
|
||||||
|
this.programInstance = programInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Program {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._1714;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@IssueKey("1714")
|
||||||
|
@WithClasses({
|
||||||
|
Issue1714Mapper.class
|
||||||
|
})
|
||||||
|
public class Issue1714Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void codeShouldBeGeneratedCorrectly() {
|
||||||
|
|
||||||
|
Issue1714Mapper.OnDemand source = new Issue1714Mapper.OnDemand();
|
||||||
|
source.setProgramInstance( new Issue1714Mapper.Program() );
|
||||||
|
|
||||||
|
Issue1714Mapper.OfferEntity result = Issue1714Mapper.INSTANCE.map( source );
|
||||||
|
|
||||||
|
assertThat( result.getSeasonNumber() ).isEqualTo( "1" );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,9 @@ public abstract class BaseMapper {
|
|||||||
|
|
||||||
public abstract Target sourceToTarget(Source source);
|
public abstract Target sourceToTarget(Source source);
|
||||||
|
|
||||||
|
@Qualified
|
||||||
|
public abstract Target sourceToTargetQualified(Source source);
|
||||||
|
|
||||||
private static final List<Invocation> INVOCATIONS = new ArrayList<Invocation>();
|
private static final List<Invocation> INVOCATIONS = new ArrayList<Invocation>();
|
||||||
|
|
||||||
@BeforeMapping
|
@BeforeMapping
|
||||||
|
@ -96,14 +96,15 @@ public class QualifierTest {
|
|||||||
ErroneousMapper.class
|
ErroneousMapper.class
|
||||||
} )
|
} )
|
||||||
@ExpectedCompilationOutcome(
|
@ExpectedCompilationOutcome(
|
||||||
value = CompilationResult.FAILED,
|
value = CompilationResult.FAILED,
|
||||||
diagnostics = {
|
diagnostics = {
|
||||||
@Diagnostic( type = ErroneousMapper.class,
|
@Diagnostic(type = ErroneousMapper.class,
|
||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 28,
|
line = 28,
|
||||||
messageRegExp = "Ambiguous mapping methods found for mapping property "
|
messageRegExp =
|
||||||
+ "\"java.lang.String title\" to java.lang.String.*" )
|
"Can't map property \"java.lang.String title\" to \"java.lang.String title\". "
|
||||||
}
|
+ "Consider to declare/implement a mapping method: \"java.lang.String map(java.lang.String value)*")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
public void shouldNotProduceMatchingMethod() {
|
public void shouldNotProduceMatchingMethod() {
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,9 @@ import java.lang.annotation.ElementType;
|
|||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.IterableMapping;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Qualifier;
|
import org.mapstruct.Qualifier;
|
||||||
@ -29,6 +32,14 @@ public abstract class TopologyMapper {
|
|||||||
@Mapping( target = "topologyFeatures", qualifiedBy = Cities.class )
|
@Mapping( target = "topologyFeatures", qualifiedBy = Cities.class )
|
||||||
public abstract TopologyEntity mapTopologyAsCity(TopologyDto dto);
|
public abstract TopologyEntity mapTopologyAsCity(TopologyDto dto);
|
||||||
|
|
||||||
|
@Rivers
|
||||||
|
@IterableMapping( qualifiedBy = Rivers.class )
|
||||||
|
public abstract List<TopologyFeatureEntity> mapTopologiesAsRiver(List<TopologyFeatureDto> in);
|
||||||
|
|
||||||
|
@Cities
|
||||||
|
@IterableMapping( qualifiedBy = Cities.class )
|
||||||
|
public abstract List<TopologyFeatureEntity> mapTopologiesAsCities(List<TopologyFeatureDto> in);
|
||||||
|
|
||||||
@Rivers
|
@Rivers
|
||||||
protected TopologyFeatureEntity mapRiver( TopologyFeatureDto dto ) {
|
protected TopologyFeatureEntity mapRiver( TopologyFeatureDto dto ) {
|
||||||
TopologyFeatureEntity topologyFeatureEntity = null;
|
TopologyFeatureEntity topologyFeatureEntity = null;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user