mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#3144 Map to Bean should only be possible for single source mappings and if explicitly used in multi source mappings
This commit is contained in:
parent
d075d9a5b6
commit
86919c637f
@ -586,7 +586,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
.build();
|
||||
|
||||
ReadAccessor targetPropertyReadAccessor =
|
||||
method.getResultType().getReadAccessor( propertyName );
|
||||
method.getResultType().getReadAccessor( propertyName, forceUpdateMethod );
|
||||
MappingReferences mappingRefs = extractMappingReferences( propertyName, true );
|
||||
PropertyMapping propertyMapping = new PropertyMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
@ -1160,7 +1160,10 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
|
||||
Accessor targetWriteAccessor = unprocessedTargetProperties.get( targetPropertyName );
|
||||
ReadAccessor targetReadAccessor = resultTypeToMap.getReadAccessor( targetPropertyName );
|
||||
ReadAccessor targetReadAccessor = resultTypeToMap.getReadAccessor(
|
||||
targetPropertyName,
|
||||
method.getSourceParameters().size() == 1
|
||||
);
|
||||
|
||||
if ( targetWriteAccessor == null ) {
|
||||
if ( targetReadAccessor == null ) {
|
||||
@ -1513,7 +1516,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
|
||||
ReadAccessor targetPropertyReadAccessor =
|
||||
method.getResultType().getReadAccessor( targetPropertyName );
|
||||
method.getResultType()
|
||||
.getReadAccessor( targetPropertyName, method.getSourceParameters().size() == 1 );
|
||||
MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false );
|
||||
PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
@ -1556,7 +1560,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
.build();
|
||||
|
||||
ReadAccessor targetPropertyReadAccessor =
|
||||
method.getResultType().getReadAccessor( targetProperty.getKey() );
|
||||
method.getResultType()
|
||||
.getReadAccessor( targetProperty.getKey(), method.getSourceParameters().size() == 1 );
|
||||
MappingReferences mappingRefs = extractMappingReferences( targetProperty.getKey(), false );
|
||||
PropertyMapping propertyMapping = new PropertyMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
@ -1600,7 +1605,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
return sourceRef;
|
||||
}
|
||||
|
||||
ReadAccessor sourceReadAccessor = sourceParameter.getType().getReadAccessor( targetPropertyName );
|
||||
ReadAccessor sourceReadAccessor = sourceParameter.getType()
|
||||
.getReadAccessor( targetPropertyName, method.getSourceParameters().size() == 1 );
|
||||
if ( sourceReadAccessor != null ) {
|
||||
// property mapping
|
||||
PresenceCheckAccessor sourcePresenceChecker =
|
||||
|
@ -643,7 +643,10 @@ public class NestedTargetPropertyMappingHolder {
|
||||
boolean forceUpdateMethod) {
|
||||
|
||||
Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName );
|
||||
ReadAccessor targetReadAccessor = targetType.getReadAccessor( targetPropertyName );
|
||||
ReadAccessor targetReadAccessor = targetType.getReadAccessor(
|
||||
targetPropertyName,
|
||||
method.getSourceParameters().size() == 1
|
||||
);
|
||||
if ( targetWriteAccessor == null ) {
|
||||
Set<String> readAccessors = targetType.getPropertyReadAccessors().keySet();
|
||||
String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors );
|
||||
|
@ -152,15 +152,27 @@ public class SourceReference extends AbstractReference {
|
||||
private SourceReference buildFromSingleSourceParameters(String[] segments, Parameter parameter) {
|
||||
boolean foundEntryMatch;
|
||||
|
||||
boolean allowedMapToBean = false;
|
||||
if ( segments.length > 0 ) {
|
||||
if ( parameter.getType().isMapType() ) {
|
||||
// When the parameter type is a map and the parameter name matches the first segment
|
||||
// then the first segment should not be treated as a property of the map
|
||||
allowedMapToBean = !segments[0].equals( parameter.getName() );
|
||||
}
|
||||
}
|
||||
String[] propertyNames = segments;
|
||||
List<PropertyEntry> entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames );
|
||||
List<PropertyEntry> entries = matchWithSourceAccessorTypes(
|
||||
parameter.getType(),
|
||||
propertyNames,
|
||||
allowedMapToBean
|
||||
);
|
||||
foundEntryMatch = ( entries.size() == propertyNames.length );
|
||||
|
||||
if ( !foundEntryMatch ) {
|
||||
//Lets see if the expression contains the parameterName, so parameterName.propName1.propName2
|
||||
if ( getSourceParameterFromMethodOrTemplate( segments[0] ) != null ) {
|
||||
propertyNames = Arrays.copyOfRange( segments, 1, segments.length );
|
||||
entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames );
|
||||
entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames, true );
|
||||
foundEntryMatch = ( entries.size() == propertyNames.length );
|
||||
}
|
||||
else {
|
||||
@ -193,7 +205,7 @@ public class SourceReference extends AbstractReference {
|
||||
|
||||
if ( segments.length > 1 && parameter != null ) {
|
||||
propertyNames = Arrays.copyOfRange( segments, 1, segments.length );
|
||||
entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames );
|
||||
entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames, true );
|
||||
foundEntryMatch = ( entries.size() == propertyNames.length );
|
||||
}
|
||||
else {
|
||||
@ -306,13 +318,14 @@ public class SourceReference extends AbstractReference {
|
||||
}
|
||||
}
|
||||
|
||||
private List<PropertyEntry> matchWithSourceAccessorTypes(Type type, String[] entryNames) {
|
||||
private List<PropertyEntry> matchWithSourceAccessorTypes(Type type, String[] entryNames,
|
||||
boolean allowedMapToBean) {
|
||||
List<PropertyEntry> sourceEntries = new ArrayList<>();
|
||||
Type newType = type;
|
||||
for ( int i = 0; i < entryNames.length; i++ ) {
|
||||
boolean matchFound = false;
|
||||
Type noBoundsType = newType.withoutBounds();
|
||||
ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryNames[i] );
|
||||
ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryNames[i], i > 0 || allowedMapToBean );
|
||||
if ( readAccessor != null ) {
|
||||
PresenceCheckAccessor presenceChecker = noBoundsType.getPresenceChecker( entryNames[i] );
|
||||
newType = typeFactory.getReturnType(
|
||||
|
@ -652,8 +652,8 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
}
|
||||
}
|
||||
|
||||
public ReadAccessor getReadAccessor(String propertyName) {
|
||||
if ( hasStringMapSignature() ) {
|
||||
public ReadAccessor getReadAccessor(String propertyName, boolean allowedMapToBean) {
|
||||
if ( allowedMapToBean && hasStringMapSignature() ) {
|
||||
ExecutableElement getMethod = getAllMethods()
|
||||
.stream()
|
||||
.filter( m -> m.getSimpleName().contentEquals( "get" ) )
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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._3144;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
public interface Issue3144Mapper {
|
||||
|
||||
Issue3144Mapper INSTANCE = Mappers.getMapper( Issue3144Mapper.class );
|
||||
|
||||
@Mapping(target = "map", source = "sourceMap")
|
||||
Target mapExplicitDefined(Map<String, String> sourceMap);
|
||||
|
||||
@Mapping(target = "map", ignore = true)
|
||||
Target map(Map<String, String> sourceMap);
|
||||
|
||||
Target mapMultiParameters(Source source, Map<String, String> map);
|
||||
|
||||
@Mapping(target = "value", source = "map.testValue")
|
||||
Target mapMultiParametersDefinedMapping(Source source, Map<String, String> map);
|
||||
|
||||
class Source {
|
||||
private final String sourceValue;
|
||||
|
||||
public Source(String sourceValue) {
|
||||
this.sourceValue = sourceValue;
|
||||
}
|
||||
|
||||
public String getSourceValue() {
|
||||
return sourceValue;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
private String value;
|
||||
private Map<String, String> map;
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Map<String, String> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public void setMap(Map<String, String> map) {
|
||||
this.map = map;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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._3144;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@WithClasses(Issue3144Mapper.class)
|
||||
@IssueKey("3144")
|
||||
class Issue3144Test {
|
||||
|
||||
@ProcessorTest
|
||||
void shouldCorrectlyHandleMapBeanMapping() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put( "value", "Map Value" );
|
||||
map.put( "testValue", "Map Test Value" );
|
||||
|
||||
Issue3144Mapper.Target target = Issue3144Mapper.INSTANCE.mapExplicitDefined( map );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getValue() ).isEqualTo( "Map Value" );
|
||||
assertThat( target.getMap() )
|
||||
.containsOnly(
|
||||
entry( "value", "Map Value" ),
|
||||
entry( "testValue", "Map Test Value" )
|
||||
);
|
||||
|
||||
target = Issue3144Mapper.INSTANCE.map( map );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getValue() ).isEqualTo( "Map Value" );
|
||||
assertThat( target.getMap() ).isNull();
|
||||
|
||||
target = Issue3144Mapper.INSTANCE.mapMultiParameters( null, map );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getValue() ).isNull();
|
||||
assertThat( target.getMap() )
|
||||
.containsOnly(
|
||||
entry( "value", "Map Value" ),
|
||||
entry( "testValue", "Map Test Value" )
|
||||
);
|
||||
|
||||
target = Issue3144Mapper.INSTANCE.mapMultiParametersDefinedMapping( null, map );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getValue() ).isEqualTo( "Map Test Value" );
|
||||
assertThat( target.getMap() ).isNull();
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ public interface MapToBeanUsingMappingMethodMapper {
|
||||
|
||||
MapToBeanUsingMappingMethodMapper INSTANCE = Mappers.getMapper( MapToBeanUsingMappingMethodMapper.class );
|
||||
|
||||
@Mapping(target = "normalInt", source = "number")
|
||||
@Mapping(target = "normalInt", source = "source.number")
|
||||
Target toTarget(Map<String, Integer> source);
|
||||
|
||||
default String mapIntegerToString( Integer input ) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user