Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ private void handleUnprocessedDefinedTargets() {
.build();

Accessor targetPropertyReadAccessor =
method.getResultType().getPropertyReadAccessors().get( propertyName );
method.getResultType().getReadAccessor( propertyName );
MappingReferences mappingRefs = extractMappingReferences( propertyName, true );
PropertyMapping propertyMapping = new PropertyMappingBuilder()
.mappingContext( ctx )
Expand Down Expand Up @@ -1047,7 +1047,7 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp
}

Accessor targetWriteAccessor = unprocessedTargetProperties.get( targetPropertyName );
Accessor targetReadAccessor = resultTypeToMap.getPropertyReadAccessors().get( targetPropertyName );
Accessor targetReadAccessor = resultTypeToMap.getReadAccessor( targetPropertyName );

if ( targetWriteAccessor == null ) {
if ( targetReadAccessor == null ) {
Expand Down Expand Up @@ -1389,7 +1389,7 @@ private void applyPropertyNameBasedMapping(List<SourceReference> sourceReference
}

Accessor targetPropertyReadAccessor =
method.getResultType().getPropertyReadAccessors().get( targetPropertyName );
method.getResultType().getReadAccessor( targetPropertyName );
MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false );
PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx )
.sourceMethod( method )
Expand Down Expand Up @@ -1432,7 +1432,7 @@ private void applyParameterNameBasedMapping() {
.build();

Accessor targetPropertyReadAccessor =
method.getResultType().getPropertyReadAccessors().get( targetProperty.getKey() );
method.getResultType().getReadAccessor( targetProperty.getKey() );
MappingReferences mappingRefs = extractMappingReferences( targetProperty.getKey(), false );
PropertyMapping propertyMapping = new PropertyMappingBuilder()
.mappingContext( ctx )
Expand Down Expand Up @@ -1473,22 +1473,11 @@ private SourceReference getSourceRefByTargetName(Parameter sourceParameter, Stri
return sourceRef;
}

if ( sourceParameter.getType().isMapType() ) {
List<Type> typeParameters = sourceParameter.getType().getTypeParameters();
if ( typeParameters.size() == 2 && typeParameters.get( 0 ).isString() ) {
return SourceReference.fromMapSource(
new String[] { targetPropertyName },
sourceParameter
);
}
}

Accessor sourceReadAccessor =
sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName );
Accessor sourceReadAccessor = sourceParameter.getType().getReadAccessor( targetPropertyName );
if ( sourceReadAccessor != null ) {
// property mapping
Accessor sourcePresenceChecker =
sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName );
sourceParameter.getType().getPresenceChecker( targetPropertyName );

DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror();
Type returnType = ctx.getTypeFactory().getReturnType( declaredSourceType, sourceReadAccessor );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
import org.mapstruct.ap.internal.model.common.PresenceCheck;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry;
import org.mapstruct.ap.internal.model.presence.SourceReferenceContainsKeyPresenceCheck;
import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.util.ValueProvider;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.AccessorType;

/**
* This method is used to convert the nested properties as listed in propertyEntries into a method
Expand Down Expand Up @@ -165,11 +168,20 @@ public static class SafePropertyEntry {
public SafePropertyEntry(PropertyEntry entry, String safeName, String previousPropertyName) {
this.safeName = safeName;
this.readAccessorName = ValueProvider.of( entry.getReadAccessor() ).getValue();
if ( entry.getPresenceChecker() != null ) {
this.presenceChecker = new SourceReferenceMethodPresenceCheck(
previousPropertyName,
entry.getPresenceChecker().getSimpleName()
);
Accessor presenceChecker = entry.getPresenceChecker();
if ( presenceChecker != null ) {
if ( presenceChecker.getAccessorType() == AccessorType.MAP_CONTAINS ) {
this.presenceChecker = new SourceReferenceContainsKeyPresenceCheck(
previousPropertyName,
presenceChecker.getSimpleName()
);
}
else {
this.presenceChecker = new SourceReferenceMethodPresenceCheck(
previousPropertyName,
presenceChecker.getSimpleName()
);
}
}
else {
this.presenceChecker = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences m
boolean forceUpdateMethod) {

Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName );
Accessor targetReadAccessor = targetType.getPropertyReadAccessors().get( targetPropertyName );
Accessor targetReadAccessor = targetType.getReadAccessor( targetPropertyName );
if ( targetWriteAccessor == null ) {
Set<String> readAccessors = targetType.getPropertyReadAccessors().keySet();
String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;

import org.mapstruct.ap.internal.model.common.Parameter;
import org.mapstruct.ap.internal.model.common.Type;
Expand All @@ -27,8 +24,6 @@
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.MapValueAccessor;
import org.mapstruct.ap.internal.util.accessor.MapValuePresenceChecker;

import static org.mapstruct.ap.internal.model.beanmapping.PropertyEntry.forSourceReference;
import static org.mapstruct.ap.internal.util.Collections.last;
Expand All @@ -51,32 +46,13 @@
* <li>{@code propertyEntries[1]} will describe {@code propB}</li>
* </ul>
*
* After building, {@link #isValid()} will return true when when no problems are detected during building.
* After building, {@link #isValid()} will return true when no problems are detected during building.
*
* @author Sjaak Derksen
* @author Filip Hrisafov
*/
public class SourceReference extends AbstractReference {

public static SourceReference fromMapSource(String[] segments, Parameter parameter) {
Type parameterType = parameter.getType();
Type valueType = parameterType.getTypeParameters().get( 1 );

TypeElement typeElement = parameterType.getTypeElement();
TypeMirror typeMirror = valueType.getTypeMirror();
String simpleName = String.join( ".", segments );

MapValueAccessor mapValueAccessor = new MapValueAccessor( typeElement, typeMirror, simpleName );
MapValuePresenceChecker mapValuePresenceChecker = new MapValuePresenceChecker(
typeElement,
typeMirror,
simpleName
);
List<PropertyEntry> entries = Collections.singletonList(
PropertyEntry.forSourceReference( segments, mapValueAccessor, mapValuePresenceChecker, valueType )
);
return new SourceReference( parameter, entries, true );
}

/**
* Builds a {@link SourceReference} from an {@code @Mappping}.
*/
Expand Down Expand Up @@ -173,11 +149,6 @@ public SourceReference build() {
* @return the source reference
*/
private SourceReference buildFromSingleSourceParameters(String[] segments, Parameter parameter) {

if ( canBeTreatedAsMapSourceType( parameter.getType() ) ) {
return fromMapSource( segments, parameter );
}

boolean foundEntryMatch;

String[] propertyNames = segments;
Expand Down Expand Up @@ -214,14 +185,6 @@ private SourceReference buildFromSingleSourceParameters(String[] segments, Param
*/
private SourceReference buildFromMultipleSourceParameters(String[] segments, Parameter parameter) {

if (parameter != null && canBeTreatedAsMapSourceType( parameter.getType() )) {
String[] propertyNames = new String[0];
if ( segments.length > 1 ) {
propertyNames = Arrays.copyOfRange( segments, 1, segments.length );
}
return fromMapSource( propertyNames, parameter );
}

boolean foundEntryMatch;

String[] propertyNames = new String[0];
Expand All @@ -244,17 +207,8 @@ private SourceReference buildFromMultipleSourceParameters(String[] segments, Par
return new SourceReference( parameter, entries, foundEntryMatch );
}

private boolean canBeTreatedAsMapSourceType(Type type) {
if ( !type.isMapType() ) {
return false;
}

List<Type> typeParameters = type.getTypeParameters();
return typeParameters.size() == 2 && typeParameters.get( 0 ).isString();
}

/**
* When there are more than one source parameters, the first segment name of the propery
* When there are more than one source parameters, the first segment name of the property
* needs to match the parameter name to avoid ambiguity
*
* consider: {@code Target map( Source1 source1, Source2 source2 )}
Expand Down Expand Up @@ -356,24 +310,20 @@ private List<PropertyEntry> matchWithSourceAccessorTypes(Type type, String[] ent
Type newType = type;
for ( int i = 0; i < entryNames.length; i++ ) {
boolean matchFound = false;
Map<String, Accessor> sourceReadAccessors = newType.getPropertyReadAccessors();
Map<String, Accessor> sourcePresenceCheckers = newType.getPropertyPresenceCheckers();

for ( Map.Entry<String, Accessor> getter : sourceReadAccessors.entrySet() ) {
if ( getter.getKey().equals( entryNames[i] ) ) {
newType = typeFactory.getReturnType(
(DeclaredType) newType.getTypeMirror(),
getter.getValue()
);
sourceEntries.add( forSourceReference(
Arrays.copyOf( entryNames, i + 1 ),
getter.getValue(),
sourcePresenceCheckers.get( entryNames[i] ),
newType
) );
matchFound = true;
break;
}
Accessor readAccessor = newType.getReadAccessor( entryNames[i] );
if ( readAccessor != null ) {
Accessor presenceChecker = newType.getPresenceChecker( entryNames[i] );
newType = typeFactory.getReturnType(
(DeclaredType) newType.getTypeMirror(),
readAccessor
);
sourceEntries.add( forSourceReference(
Arrays.copyOf( entryNames, i + 1 ),
readAccessor,
presenceChecker,
newType
) );
matchFound = true;
}
if ( !matchFound ) {
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.AccessorType;
import org.mapstruct.ap.internal.util.accessor.MapValueAccessor;
import org.mapstruct.ap.internal.util.accessor.MapValuePresenceChecker;

import static org.mapstruct.ap.internal.util.Collections.first;

Expand All @@ -60,6 +62,7 @@
* through {@link TypeFactory}.
*
* @author Gunnar Morling
* @author Filip Hrisafov
*/
public class Type extends ModelElement implements Comparable<Type> {

Expand Down Expand Up @@ -308,6 +311,17 @@ public boolean isMapType() {
return isMapType;
}

private boolean hasStringMapSignature() {
if ( isMapType() ) {
List<Type> typeParameters = getTypeParameters();
if ( typeParameters.size() == 2 && typeParameters.get( 0 ).isString() ) {
return true;
}
}

return false;
}

public boolean isCollectionOrMapType() {
return isCollectionType || isMapType;
}
Expand Down Expand Up @@ -597,6 +611,42 @@ public Type asRawType() {
}
}

public Accessor getReadAccessor(String propertyName) {
if ( hasStringMapSignature() ) {
ExecutableElement getMethod = getAllMethods()
.stream()
.filter( m -> m.getSimpleName().contentEquals( "get" ) )
.filter( m -> m.getParameters().size() == 1 )
.findAny()
.orElse( null );
return new MapValueAccessor( getMethod, typeParameters.get( 1 ).getTypeMirror(), propertyName );
}

Map<String, Accessor> readAccessors = getPropertyReadAccessors();

return readAccessors.get( propertyName );
}

public Accessor getPresenceChecker(String propertyName) {
if ( hasStringMapSignature() ) {
ExecutableElement containsKeyMethod = getAllMethods()
.stream()
.filter( m -> m.getSimpleName().contentEquals( "containsKey" ) )
.filter( m -> m.getParameters().size() == 1 )
.findAny()
.orElse( null );

return new MapValuePresenceChecker(
containsKeyMethod,
typeParameters.get( 1 ).getTypeMirror(),
propertyName
);
}

Map<String, Accessor> presenceCheckers = getPropertyPresenceCheckers();
return presenceCheckers.get( propertyName );
}

/**
* getPropertyReadAccessors
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ void shouldMapFromNestedMap() {
assertThat( target.getNestedTarget().getStringFromNestedMap() ).isEqualTo( "valueFromNestedMap" );
}

@IssueKey("2553")
@ProcessorTest
@WithClasses(MapToBeanFromMapAndNestedMapWithDefinedMapping.class)
void shouldMapFromNestedMapWithDefinedMapping() {

MapToBeanFromMapAndNestedMapWithDefinedMapping.Source source =
new MapToBeanFromMapAndNestedMapWithDefinedMapping.Source();
MapToBeanFromMapAndNestedMapWithDefinedMapping.Target target =
MapToBeanFromMapAndNestedMapWithDefinedMapping.INSTANCE.toTarget( source );

assertThat( target ).isNotNull();
assertThat( target.getNested() ).isEqualTo( "valueFromNestedMap" );
}

@ProcessorTest
@WithClasses(ObjectMapToBeanWithQualifierMapper.class)
void shouldUseObjectQualifiedMethod() {
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.