mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
Refactoring of BeanMapping and Source/TargetReferences (common base class) (#1903)
This commit is contained in:
parent
6d9a50601e
commit
e12f9ffd7b
@ -19,7 +19,6 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.tools.Diagnostic;
|
||||
@ -52,7 +51,6 @@ import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.source.Mapping.getMappingByTargetName;
|
||||
import static org.mapstruct.ap.internal.model.beanmapping.MappingReferences.forSourceMethod;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_ABSTRACT;
|
||||
@ -88,7 +86,6 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
private final Set<Parameter> unprocessedSourceParameters = new HashSet<>();
|
||||
private final Set<String> existingVariableNames = new HashSet<>();
|
||||
private final Map<String, Set<MappingReference>> unprocessedDefinedTargets = new LinkedHashMap<>();
|
||||
private Function<String, Mapping> singleMapping;
|
||||
|
||||
private MappingReferences mappingReferences;
|
||||
|
||||
@ -103,15 +100,12 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
|
||||
public Builder sourceMethod(SourceMethod sourceMethod) {
|
||||
singleMapping =
|
||||
targetName -> getMappingByTargetName( targetName, sourceMethod.getMappingOptions().getMappings() );
|
||||
this.method = sourceMethod;
|
||||
this.mappingReferences = forSourceMethod( sourceMethod, ctx.getMessager(), ctx.getTypeFactory() );
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder forgedMethod(ForgedMethod forgedMethod) {
|
||||
singleMapping = targetPropertyName -> null;
|
||||
this.method = forgedMethod;
|
||||
mappingReferences = forgedMethod.getMappingReferences();
|
||||
Parameter sourceParameter = first( Parameter.getSourceParameters( forgedMethod.getParameters() ) );
|
||||
@ -320,12 +314,14 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
.name( propertyName )
|
||||
.build();
|
||||
|
||||
Accessor targetPropertyReadAccessor =
|
||||
method.getResultType().getPropertyReadAccessors().get( propertyName );
|
||||
MappingReferences mappingRefs = extractMappingReferences( propertyName, true );
|
||||
PropertyMapping propertyMapping = new PropertyMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.targetWriteAccessor( unprocessedTargetProperties.get( propertyName ) )
|
||||
.targetReadAccessor( getTargetPropertyReadAccessor( propertyName ) )
|
||||
.targetReadAccessor( targetPropertyReadAccessor )
|
||||
.targetPropertyName( propertyName )
|
||||
.sourceReference( reference )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
@ -480,18 +476,16 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
|
||||
for ( MappingReference mapping : mappingReferences.getMappingReferences() ) {
|
||||
TargetReference targetReference = mapping.getTargetReference();
|
||||
if ( targetReference.isValid() ) {
|
||||
String target = first( targetReference.getPropertyEntries() ).getFullName();
|
||||
if ( mapping.isValid() ) {
|
||||
String target = mapping.getTargetReference().getShallowestPropertyName();
|
||||
if ( !handledTargets.contains( target ) ) {
|
||||
if ( handleDefinedMapping( mapping, handledTargets ) ) {
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) {
|
||||
List<PropertyEntry> sourceEntries = mapping.getSourceReference().getPropertyEntries();
|
||||
if ( !sourceEntries.isEmpty() ) {
|
||||
String source = first( sourceEntries ).getFullName();
|
||||
if ( mapping.getSourceReference() != null ) {
|
||||
String source = mapping.getSourceReference().getShallowestPropertyName();
|
||||
if ( source != null ) {
|
||||
unprocessedSourceProperties.remove( source );
|
||||
}
|
||||
}
|
||||
@ -558,6 +552,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
}
|
||||
|
||||
// check if source / expression / constant are not somehow handled already
|
||||
if ( unprocessedDefinedTargets.containsKey( targetPropertyName ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check the mapping options
|
||||
// its an ignored property mapping
|
||||
if ( mapping.isIgnored() ) {
|
||||
@ -565,11 +564,52 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
handledTargets.add( targetProperty.getName() );
|
||||
}
|
||||
|
||||
// its a constant
|
||||
// if we have an unprocessed target that means that it most probably is nested and we should
|
||||
// not generated any mapping for it now. Eventually it will be done though
|
||||
else if ( mapping.getConstant() != null ) {
|
||||
|
||||
propertyMapping = new ConstantMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.constantExpression( mapping.getConstant() )
|
||||
.targetProperty( targetProperty )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.formattingParameters( mapping.getFormattingParameters() )
|
||||
.selectionParameters( mapping.getSelectionParameters() )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.mirror( mapping.getMirror() )
|
||||
.build();
|
||||
handledTargets.add( targetPropertyName );
|
||||
}
|
||||
|
||||
// its an expression
|
||||
// if we have an unprocessed target that means that it most probably is nested and we should
|
||||
// not generated any mapping for it now. Eventually it will be done though
|
||||
else if ( mapping.getJavaExpression() != null ) {
|
||||
|
||||
propertyMapping = new JavaExpressionMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.javaExpression( mapping.getJavaExpression() )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.targetProperty( targetProperty )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.mirror( mapping.getMirror() )
|
||||
.build();
|
||||
handledTargets.add( targetPropertyName );
|
||||
}
|
||||
// its a plain-old property mapping
|
||||
else if ( mapping.getSourceName() != null ) {
|
||||
else {
|
||||
|
||||
// determine source parameter
|
||||
SourceReference sourceRef = mappingRef.getSourceReference();
|
||||
if ( sourceRef == null && method.getSourceParameters().size() == 1 ) {
|
||||
sourceRef = getSourceRef( method.getSourceParameters().get( 0 ), targetPropertyName );
|
||||
}
|
||||
|
||||
if ( sourceRef.isValid() ) {
|
||||
|
||||
// targetProperty == null can occur: we arrived here because we want as many errors
|
||||
@ -598,46 +638,6 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
errorOccured = true;
|
||||
}
|
||||
}
|
||||
|
||||
// its a constant
|
||||
// if we have an unprocessed target that means that it most probably is nested and we should
|
||||
// not generated any mapping for it now. Eventually it will be done though
|
||||
else if ( mapping.getConstant() != null && !unprocessedDefinedTargets.containsKey( targetPropertyName ) ) {
|
||||
|
||||
propertyMapping = new ConstantMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.constantExpression( mapping.getConstant() )
|
||||
.targetProperty( targetProperty )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.formattingParameters( mapping.getFormattingParameters() )
|
||||
.selectionParameters( mapping.getSelectionParameters() )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.mirror( mapping.getMirror() )
|
||||
.build();
|
||||
handledTargets.add( targetPropertyName );
|
||||
}
|
||||
|
||||
// its an expression
|
||||
// if we have an unprocessed target that means that it most probably is nested and we should
|
||||
// not generated any mapping for it now. Eventually it will be done though
|
||||
else if ( mapping.getJavaExpression() != null
|
||||
&& !unprocessedDefinedTargets.containsKey( targetPropertyName ) ) {
|
||||
|
||||
propertyMapping = new JavaExpressionMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.javaExpression( mapping.getJavaExpression() )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.targetProperty( targetProperty )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.mirror( mapping.getMirror() )
|
||||
.build();
|
||||
handledTargets.add( targetPropertyName );
|
||||
}
|
||||
|
||||
// remaining are the mappings without a 'source' so, 'only' a date format or qualifiers
|
||||
if ( propertyMapping != null ) {
|
||||
propertyMappings.add( propertyMapping );
|
||||
@ -653,91 +653,60 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
* the set of remaining target properties.
|
||||
*/
|
||||
private void applyPropertyNameBasedMapping() {
|
||||
|
||||
Iterator<Entry<String, Accessor>> targetPropertyEntriesIterator =
|
||||
unprocessedTargetProperties.entrySet().iterator();
|
||||
|
||||
while ( targetPropertyEntriesIterator.hasNext() ) {
|
||||
|
||||
Entry<String, Accessor> targetProperty = targetPropertyEntriesIterator.next();
|
||||
String targetPropertyName = targetProperty.getKey();
|
||||
|
||||
PropertyMapping propertyMapping = null;
|
||||
|
||||
if ( propertyMapping == null ) {
|
||||
|
||||
for ( Parameter sourceParameter : method.getSourceParameters() ) {
|
||||
|
||||
Type sourceType = sourceParameter.getType();
|
||||
|
||||
if ( sourceType.isPrimitive() || sourceType.isArrayType() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PropertyMapping newPropertyMapping = null;
|
||||
|
||||
Accessor sourceReadAccessor =
|
||||
sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName );
|
||||
|
||||
Accessor sourcePresenceChecker =
|
||||
sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName );
|
||||
|
||||
if ( sourceReadAccessor != null ) {
|
||||
Mapping mapping = singleMapping.apply( targetProperty.getKey() );
|
||||
DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror();
|
||||
|
||||
SourceReference sourceRef = new SourceReference.BuilderFromProperty()
|
||||
.sourceParameter( sourceParameter )
|
||||
.type( ctx.getTypeFactory().getReturnType( declaredSourceType, sourceReadAccessor ) )
|
||||
.readAccessor( sourceReadAccessor )
|
||||
.presenceChecker( sourcePresenceChecker )
|
||||
.name( targetProperty.getKey() )
|
||||
.build();
|
||||
|
||||
MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false );
|
||||
newPropertyMapping = new PropertyMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.targetWriteAccessor( targetProperty.getValue() )
|
||||
.targetReadAccessor( getTargetPropertyReadAccessor( targetPropertyName ) )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.sourceReference( sourceRef )
|
||||
.formattingParameters( mapping != null ? mapping.getFormattingParameters() : null )
|
||||
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
|
||||
.defaultValue( mapping != null ? mapping.getDefaultValue() : null )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptySet() )
|
||||
.forgeMethodWithMappingReferences( mappingRefs )
|
||||
.nullValueCheckStrategy( mapping != null ? mapping.getNullValueCheckStrategy() : null )
|
||||
.nullValuePropertyMappingStrategy( mapping != null ?
|
||||
mapping.getNullValuePropertyMappingStrategy() : null )
|
||||
.mirror( mapping != null ? mapping.getMirror() : null )
|
||||
.build();
|
||||
|
||||
unprocessedSourceParameters.remove( sourceParameter );
|
||||
}
|
||||
|
||||
if ( propertyMapping != null && newPropertyMapping != null ) {
|
||||
// TODO improve error message
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
Message.BEANMAPPING_SEVERAL_POSSIBLE_SOURCES,
|
||||
targetPropertyName
|
||||
);
|
||||
break;
|
||||
}
|
||||
else if ( newPropertyMapping != null ) {
|
||||
propertyMapping = newPropertyMapping;
|
||||
}
|
||||
List<SourceReference> sourceReferences = new ArrayList<>();
|
||||
for ( String targetPropertyName : unprocessedTargetProperties.keySet() ) {
|
||||
for ( Parameter sourceParameter : method.getSourceParameters() ) {
|
||||
SourceReference sourceRef = getSourceRef( sourceParameter, targetPropertyName );
|
||||
if ( sourceRef != null ) {
|
||||
sourceReferences.add( sourceRef );
|
||||
}
|
||||
}
|
||||
}
|
||||
applyPropertyNameBasedMapping( sourceReferences );
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all target properties and all source parameters.
|
||||
* <p>
|
||||
* When a property name match occurs, the remainder will be checked for duplicates. Matches will be removed from
|
||||
* the set of remaining target properties.
|
||||
*/
|
||||
private void applyPropertyNameBasedMapping(List<SourceReference> sourceReferences) {
|
||||
|
||||
for ( SourceReference sourceRef : sourceReferences ) {
|
||||
|
||||
String targetPropertyName = sourceRef.getDeepestPropertyName();
|
||||
Accessor targetPropertyWriteAccessor = unprocessedTargetProperties.remove( targetPropertyName );
|
||||
if ( targetPropertyWriteAccessor == null ) {
|
||||
// TODO improve error message
|
||||
ctx.getMessager()
|
||||
.printMessage( method.getExecutable(),
|
||||
Message.BEANMAPPING_SEVERAL_POSSIBLE_SOURCES,
|
||||
targetPropertyName
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessor targetPropertyReadAccessor =
|
||||
method.getResultType().getPropertyReadAccessors().get( targetPropertyName );
|
||||
MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false );
|
||||
PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.targetWriteAccessor( targetPropertyWriteAccessor )
|
||||
.targetReadAccessor( targetPropertyReadAccessor )
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.sourceReference( sourceRef )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.forgeMethodWithMappingReferences( mappingRefs )
|
||||
.build();
|
||||
|
||||
unprocessedSourceParameters.remove( sourceRef.getParameter() );
|
||||
|
||||
if ( propertyMapping != null ) {
|
||||
propertyMappings.add( propertyMapping );
|
||||
targetPropertyEntriesIterator.remove();
|
||||
unprocessedDefinedTargets.remove( targetPropertyName );
|
||||
unprocessedSourceProperties.remove( targetPropertyName );
|
||||
}
|
||||
unprocessedDefinedTargets.remove( targetPropertyName );
|
||||
unprocessedSourceProperties.remove( targetPropertyName );
|
||||
}
|
||||
}
|
||||
|
||||
@ -756,30 +725,24 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
Parameter sourceParameter = sourceParameters.next();
|
||||
if ( sourceParameter.getName().equals( targetProperty.getKey() ) ) {
|
||||
Mapping mapping = singleMapping.apply( targetProperty.getKey() );
|
||||
|
||||
SourceReference sourceRef = new SourceReference.BuilderFromProperty()
|
||||
.sourceParameter( sourceParameter )
|
||||
.name( targetProperty.getKey() )
|
||||
.build();
|
||||
|
||||
Accessor targetPropertyReadAccessor =
|
||||
method.getResultType().getPropertyReadAccessors().get( targetProperty.getKey() );
|
||||
MappingReferences mappingRefs = extractMappingReferences( targetProperty.getKey(), false );
|
||||
PropertyMapping propertyMapping = new PropertyMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.targetWriteAccessor( targetProperty.getValue() )
|
||||
.targetReadAccessor( getTargetPropertyReadAccessor( targetProperty.getKey() ) )
|
||||
.targetReadAccessor( targetPropertyReadAccessor )
|
||||
.targetPropertyName( targetProperty.getKey() )
|
||||
.sourceReference( sourceRef )
|
||||
.formattingParameters( mapping != null ? mapping.getFormattingParameters() : null )
|
||||
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptySet() )
|
||||
.forgeMethodWithMappingReferences( mappingRefs )
|
||||
.nullValueCheckStrategy( mapping != null ? mapping.getNullValueCheckStrategy() : null )
|
||||
.nullValuePropertyMappingStrategy( mapping != null ?
|
||||
mapping.getNullValuePropertyMappingStrategy() : null )
|
||||
.mirror( mapping != null ? mapping.getMirror() : null )
|
||||
.build();
|
||||
|
||||
propertyMappings.add( propertyMapping );
|
||||
@ -792,6 +755,33 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private SourceReference getSourceRef(Parameter sourceParameter, String targetPropertyName) {
|
||||
|
||||
SourceReference sourceRef = null;
|
||||
|
||||
if ( sourceParameter.getType().isPrimitive() || sourceParameter.getType().isArrayType() ) {
|
||||
return sourceRef;
|
||||
}
|
||||
|
||||
Accessor sourceReadAccessor =
|
||||
sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName );
|
||||
|
||||
Accessor sourcePresenceChecker =
|
||||
sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName );
|
||||
|
||||
if ( sourceReadAccessor != null ) {
|
||||
DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror();
|
||||
Type returnType = ctx.getTypeFactory().getReturnType( declaredSourceType, sourceReadAccessor );
|
||||
sourceRef = new SourceReference.BuilderFromProperty().sourceParameter( sourceParameter )
|
||||
.type( returnType )
|
||||
.readAccessor( sourceReadAccessor )
|
||||
.presenceChecker( sourcePresenceChecker )
|
||||
.name( targetPropertyName )
|
||||
.build();
|
||||
}
|
||||
return sourceRef;
|
||||
}
|
||||
|
||||
private MappingReferences extractMappingReferences(String targetProperty, boolean restrictToDefinedMappings) {
|
||||
if ( unprocessedDefinedTargets.containsKey( targetProperty ) ) {
|
||||
Set<MappingReference> mappings = unprocessedDefinedTargets.get( targetProperty );
|
||||
@ -800,10 +790,6 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Accessor getTargetPropertyReadAccessor(String propertyName) {
|
||||
return method.getResultType().getPropertyReadAccessors().get( propertyName );
|
||||
}
|
||||
|
||||
private ReportingPolicyPrism getUnmappedTargetPolicy() {
|
||||
if ( mappingReferences.isForForgedMethods() ) {
|
||||
return ReportingPolicyPrism.IGNORE;
|
||||
@ -1034,10 +1020,5 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
return Objects.equals( propertyMappings, that.propertyMappings );
|
||||
}
|
||||
|
||||
private interface SingleMappingByTargetPropertyNameFunction {
|
||||
|
||||
Mapping getSingleMappingByTargetPropertyName(String targetPropertyName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -51,8 +51,6 @@ import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping;
|
||||
import static org.mapstruct.ap.internal.model.ForgedMethod.forPropertyMapping;
|
||||
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
|
||||
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Collections.last;
|
||||
|
||||
/**
|
||||
* Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to
|
||||
@ -519,10 +517,10 @@ public class PropertyMapping extends ModelElement {
|
||||
|
||||
private SourceRHS getSourceRHS( SourceReference sourceReference ) {
|
||||
Parameter sourceParam = sourceReference.getParameter();
|
||||
List<PropertyEntry> propertyEntries = sourceReference.getPropertyEntries();
|
||||
PropertyEntry propertyEntry = sourceReference.getDeepestProperty();
|
||||
|
||||
// parameter reference
|
||||
if ( propertyEntries.isEmpty() ) {
|
||||
if ( propertyEntry == null ) {
|
||||
return new SourceRHS( sourceParam.getName(),
|
||||
sourceParam.getType(),
|
||||
existingVariableNames,
|
||||
@ -530,8 +528,7 @@ public class PropertyMapping extends ModelElement {
|
||||
);
|
||||
}
|
||||
// simple property
|
||||
else if ( propertyEntries.size() == 1 ) {
|
||||
PropertyEntry propertyEntry = propertyEntries.get( 0 );
|
||||
else if ( !sourceReference.isNested() ) {
|
||||
String sourceRef = sourceParam.getName() + "." + ValueProvider.of( propertyEntry.getReadAccessor() );
|
||||
return new SourceRHS( sourceParam.getName(),
|
||||
sourceRef,
|
||||
@ -543,7 +540,7 @@ public class PropertyMapping extends ModelElement {
|
||||
}
|
||||
// nested property given as dot path
|
||||
else {
|
||||
Type sourceType = last( propertyEntries ).getType();
|
||||
Type sourceType = propertyEntry.getType();
|
||||
if ( sourceType.isPrimitive() && !targetType.isPrimitive() ) {
|
||||
// Handle null's. If the forged method needs to be mapped to an object, the forged method must be
|
||||
// able to return null. So in that case primitive types are mapped to their corresponding wrapped
|
||||
@ -581,7 +578,7 @@ public class PropertyMapping extends ModelElement {
|
||||
);
|
||||
|
||||
// create a local variable to which forged method can be assigned.
|
||||
String desiredName = last( sourceReference.getPropertyEntries() ).getName();
|
||||
String desiredName = propertyEntry.getName();
|
||||
sourceRhs.setSourceLocalVarName( sourceRhs.createUniqueVarName( desiredName ) );
|
||||
|
||||
return sourceRhs;
|
||||
@ -595,7 +592,7 @@ public class PropertyMapping extends ModelElement {
|
||||
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 = sourceReference.getShallowestProperty();
|
||||
if ( propertyEntry.getPresenceChecker() != null ) {
|
||||
sourcePresenceChecker = sourceParam.getName()
|
||||
+ "." + propertyEntry.getPresenceChecker().getSimpleName() + "()";
|
||||
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.internal.model.beanmapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Collections.last;
|
||||
|
||||
/**
|
||||
* Class acts as a common base class for {@link TargetReference} and {@link SourceReference}.
|
||||
*
|
||||
* @author sjaak
|
||||
*/
|
||||
public abstract class AbstractReference {
|
||||
|
||||
private final Parameter parameter;
|
||||
private final List<PropertyEntry> propertyEntries;
|
||||
private final boolean isValid;
|
||||
|
||||
protected AbstractReference(Parameter sourceParameter, List<PropertyEntry> sourcePropertyEntries, boolean isValid) {
|
||||
this.parameter = sourceParameter;
|
||||
this.propertyEntries = sourcePropertyEntries;
|
||||
this.isValid = isValid;
|
||||
}
|
||||
|
||||
public Parameter getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public List<PropertyEntry> getPropertyEntries() {
|
||||
return propertyEntries;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public List<String> getElementNames() {
|
||||
List<String> elementNames = new ArrayList<>();
|
||||
if ( parameter != null ) {
|
||||
// only relevant for source properties
|
||||
elementNames.add( parameter.getName() );
|
||||
}
|
||||
for ( PropertyEntry propertyEntry : propertyEntries ) {
|
||||
elementNames.add( propertyEntry.getName() );
|
||||
}
|
||||
return elementNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the property name on the shallowest nesting level
|
||||
* @return
|
||||
*/
|
||||
public PropertyEntry getShallowestProperty() {
|
||||
if ( propertyEntries.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
return first( propertyEntries );
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the property name on the shallowest nesting level
|
||||
* @return
|
||||
*/
|
||||
public String getShallowestPropertyName() {
|
||||
if ( propertyEntries.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
return first( propertyEntries ).getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the property name on the deepest nesting level
|
||||
* @return
|
||||
*/
|
||||
public PropertyEntry getDeepestProperty() {
|
||||
if ( propertyEntries.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
return last( propertyEntries );
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the property name on the deepest nesting level
|
||||
* @return
|
||||
*/
|
||||
public String getDeepestPropertyName() {
|
||||
if ( propertyEntries.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
return last( propertyEntries ).getName();
|
||||
}
|
||||
|
||||
public boolean isNested() {
|
||||
return propertyEntries.size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String result = "";
|
||||
if ( !isValid ) {
|
||||
result = "invalid";
|
||||
}
|
||||
else if ( propertyEntries.isEmpty() ) {
|
||||
if ( parameter != null ) {
|
||||
result = String.format( "parameter \"%s %s\"", parameter.getType(), parameter.getName() );
|
||||
}
|
||||
}
|
||||
else if ( propertyEntries.size() == 1 ) {
|
||||
PropertyEntry propertyEntry = propertyEntries.get( 0 );
|
||||
result = String.format( "property \"%s %s\"", propertyEntry.getType(), propertyEntry.getName() );
|
||||
}
|
||||
else {
|
||||
PropertyEntry lastPropertyEntry = propertyEntries.get( propertyEntries.size() - 1 );
|
||||
result = String.format(
|
||||
"property \"%s %s\"",
|
||||
lastPropertyEntry.getType(),
|
||||
Strings.join( getElementNames(), "." )
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ package org.mapstruct.ap.internal.model.beanmapping;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.mapstruct.ap.internal.model.source.Mapping;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
|
||||
/**
|
||||
* Represents the intermediate (nesting) state of the {@link Mapping} in this class.
|
||||
@ -80,16 +79,24 @@ public class MappingReference {
|
||||
return Objects.hash( mapping );
|
||||
}
|
||||
|
||||
public boolean isValid( ) {
|
||||
boolean result = false;
|
||||
if ( targetReference.isValid() ) {
|
||||
result = sourceReference != null ? sourceReference.isValid() : true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String targetRefName = Strings.join( targetReference.getElementNames(), "." );
|
||||
String sourceRefName = "null";
|
||||
String targetRefStr = targetReference.toString();
|
||||
String sourceRefStr = "null";
|
||||
if ( sourceReference != null ) {
|
||||
sourceRefName = Strings.join( sourceReference.getElementNames(), "." );
|
||||
sourceRefStr = sourceReference.toString();
|
||||
}
|
||||
return "MappingReference {"
|
||||
+ "\n sourceRefName='" + sourceRefName + "\',"
|
||||
+ "\n targetRefName='" + targetRefName + "\',"
|
||||
+ "\n sourceReference='" + sourceRefStr + "\',"
|
||||
+ "\n targetReference='" + targetRefStr + "\',"
|
||||
+ "\n}";
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,9 @@ public class MappingReferences {
|
||||
public static MappingReferences forSourceMethod(SourceMethod sourceMethod, FormattingMessager messager,
|
||||
TypeFactory typeFactory) {
|
||||
|
||||
Set<Mapping> mappings = sourceMethod.getMappingOptions().getMappings();
|
||||
|
||||
|
||||
Set<MappingReference> references = new LinkedHashSet<>();
|
||||
Set<MappingReference> targetThisReferences = new LinkedHashSet<>();
|
||||
|
||||
for ( Mapping mapping : sourceMethod.getMappingOptions().getMappings() ) {
|
||||
|
||||
// handle source reference
|
||||
@ -52,9 +51,15 @@ public class MappingReferences {
|
||||
// add when inverse is also valid
|
||||
MappingReference mappingReference = new MappingReference( mapping, targetReference, sourceReference );
|
||||
if ( isValidWhenInversed( mappingReference ) ) {
|
||||
references.add( mappingReference );
|
||||
if ( targetReference.isTargetThis() ) {
|
||||
targetThisReferences.add( mappingReference );
|
||||
}
|
||||
else {
|
||||
references.add( mappingReference );
|
||||
}
|
||||
}
|
||||
}
|
||||
references.addAll( targetThisReferences );
|
||||
return new MappingReferences( references, false );
|
||||
}
|
||||
|
||||
@ -104,7 +109,7 @@ public class MappingReferences {
|
||||
|
||||
for ( MappingReference mappingRef : mappingReferences ) {
|
||||
TargetReference targetReference = mappingRef.getTargetReference();
|
||||
if ( targetReference.isValid() && targetReference.getPropertyEntries().size() > 1 ) {
|
||||
if ( targetReference.isValid() && targetReference.isNested()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
package org.mapstruct.ap.internal.model.beanmapping;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.beanmapping.PropertyEntry.forSourceReference;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Collections.last;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -22,7 +21,6 @@ import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.internal.model.source.Mapping;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
@ -50,11 +48,7 @@ import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class SourceReference {
|
||||
|
||||
private final Parameter parameter;
|
||||
private final List<PropertyEntry> propertyEntries;
|
||||
private final boolean isValid;
|
||||
public class SourceReference extends AbstractReference {
|
||||
|
||||
/**
|
||||
* Builds a {@link SourceReference} from an {@code @Mappping}.
|
||||
@ -371,88 +365,23 @@ public class SourceReference {
|
||||
}
|
||||
|
||||
public SourceReference build() {
|
||||
return new SourceReference( sourceParameter, sourceReference.propertyEntries, true );
|
||||
return new SourceReference( sourceParameter, sourceReference.getPropertyEntries(), true );
|
||||
}
|
||||
}
|
||||
|
||||
private SourceReference(Parameter sourceParameter, List<PropertyEntry> sourcePropertyEntries, boolean isValid) {
|
||||
this.parameter = sourceParameter;
|
||||
this.propertyEntries = sourcePropertyEntries;
|
||||
this.isValid = isValid;
|
||||
}
|
||||
|
||||
public Parameter getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public List<PropertyEntry> getPropertyEntries() {
|
||||
return propertyEntries;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public List<String> getElementNames() {
|
||||
List<String> sourceName = new ArrayList<>();
|
||||
sourceName.add( parameter.getName() );
|
||||
for ( PropertyEntry propertyEntry : propertyEntries ) {
|
||||
sourceName.add( propertyEntry.getName() );
|
||||
}
|
||||
return sourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this reference, which is adapted to the given method
|
||||
*
|
||||
* @param method the method to create the copy for
|
||||
* @return the copy
|
||||
*/
|
||||
public SourceReference copyForInheritanceTo(SourceMethod method) {
|
||||
List<Parameter> replacementParamCandidates = new ArrayList<>();
|
||||
for ( Parameter sourceParam : method.getSourceParameters() ) {
|
||||
if ( parameter != null && sourceParam.getType().isAssignableTo( parameter.getType() ) ) {
|
||||
replacementParamCandidates.add( sourceParam );
|
||||
}
|
||||
}
|
||||
|
||||
Parameter replacement = parameter;
|
||||
if ( replacementParamCandidates.size() == 1 ) {
|
||||
replacement = first( replacementParamCandidates );
|
||||
}
|
||||
|
||||
return new SourceReference( replacement, propertyEntries, isValid );
|
||||
super( sourceParameter, sourcePropertyEntries, isValid );
|
||||
}
|
||||
|
||||
public SourceReference pop() {
|
||||
if ( propertyEntries.size() > 1 ) {
|
||||
if ( getPropertyEntries().size() > 1 ) {
|
||||
List<PropertyEntry> newPropertyEntries =
|
||||
new ArrayList<>( propertyEntries.subList( 1, propertyEntries.size() ) );
|
||||
return new SourceReference( parameter, newPropertyEntries, isValid );
|
||||
new ArrayList<>( getPropertyEntries().subList( 1, getPropertyEntries().size() ) );
|
||||
return new SourceReference( getParameter(), newPropertyEntries, isValid() );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@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(), "." )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,12 +51,7 @@ import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class TargetReference {
|
||||
|
||||
private final Parameter parameter;
|
||||
private final List<PropertyEntry> propertyEntries;
|
||||
private final boolean isValid;
|
||||
|
||||
public class TargetReference extends AbstractReference {
|
||||
|
||||
/**
|
||||
* Builds a {@link TargetReference} from an {@code @Mappping}.
|
||||
@ -348,46 +343,24 @@ public class TargetReference {
|
||||
}
|
||||
}
|
||||
|
||||
private TargetReference(Parameter sourceParameter, List<PropertyEntry> sourcePropertyEntries, boolean isValid) {
|
||||
this.parameter = sourceParameter;
|
||||
this.propertyEntries = sourcePropertyEntries;
|
||||
this.isValid = isValid;
|
||||
private TargetReference(Parameter sourceParameter, List<PropertyEntry> targetPropertyEntries, boolean isValid) {
|
||||
super( sourceParameter, targetPropertyEntries, isValid );
|
||||
}
|
||||
|
||||
public Parameter getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public List<PropertyEntry> getPropertyEntries() {
|
||||
return propertyEntries;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public List<String> getElementNames() {
|
||||
List<String> elementNames = new ArrayList<>();
|
||||
if ( parameter != null ) {
|
||||
// only relevant for source properties
|
||||
elementNames.add( parameter.getName() );
|
||||
}
|
||||
for ( PropertyEntry propertyEntry : propertyEntries ) {
|
||||
elementNames.add( propertyEntry.getName() );
|
||||
}
|
||||
return elementNames;
|
||||
public boolean isTargetThis() {
|
||||
return getPropertyEntries().isEmpty();
|
||||
}
|
||||
|
||||
public TargetReference pop() {
|
||||
if ( propertyEntries.size() > 1 ) {
|
||||
List<PropertyEntry> newPropertyEntries = new ArrayList<>( propertyEntries.size() - 1 );
|
||||
for ( PropertyEntry propertyEntry : propertyEntries ) {
|
||||
if ( getPropertyEntries().size() > 1 ) {
|
||||
List<PropertyEntry> newPropertyEntries = new ArrayList<>( getPropertyEntries().size() - 1 );
|
||||
for ( PropertyEntry propertyEntry : getPropertyEntries() ) {
|
||||
PropertyEntry newPropertyEntry = propertyEntry.pop();
|
||||
if ( newPropertyEntry != null ) {
|
||||
newPropertyEntries.add( newPropertyEntry );
|
||||
}
|
||||
}
|
||||
return new TargetReference( null, newPropertyEntries, isValid );
|
||||
return new TargetReference( null, newPropertyEntries, isValid() );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
|
@ -63,8 +63,8 @@ public class OrderingTest {
|
||||
@Diagnostic(type = ErroneousAddressMapperWithCyclicDependency.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 24,
|
||||
messageRegExp = "Cycle\\(s\\) between properties given via dependsOn\\(\\): firstName -> lastName -> "
|
||||
+ "middleName -> firstName"
|
||||
messageRegExp = "Cycle\\(s\\) between properties given via dependsOn\\(\\): lastName -> middleName -> "
|
||||
+ "firstName -> lastName"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -84,11 +84,11 @@ public class SuggestMostSimilarNameTest {
|
||||
diagnostics = {
|
||||
@Diagnostic(type = PersonGarageWrongSourceMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 19,
|
||||
line = 21,
|
||||
messageRegExp = "No property named \"garage\\.colour\\.rgb\".*Did you mean \"garage\\.color\"\\?"),
|
||||
@Diagnostic(type = PersonGarageWrongSourceMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 22,
|
||||
line = 28,
|
||||
messageRegExp = "No property named \"garage\\.colour\".*Did you mean \"garage\\.color\"\\?")
|
||||
}
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ package org.mapstruct.ap.test.namesuggestion.erroneous;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.namesuggestion.Person;
|
||||
import org.mapstruct.ap.test.namesuggestion.PersonDto;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
@ -16,10 +17,18 @@ public interface PersonGarageWrongSourceMapper {
|
||||
|
||||
PersonGarageWrongSourceMapper MAPPER = Mappers.getMapper( PersonGarageWrongSourceMapper.class );
|
||||
|
||||
@Mapping(source = "garage.colour.rgb", target = "garage.color.rgb")
|
||||
@Mappings( {
|
||||
@Mapping( target = "garage.color.rgb", source = "garage.colour.rgb" ),
|
||||
@Mapping( target = "fullAge", source = "age" ),
|
||||
@Mapping( target = "fullName", source = "name" )
|
||||
} )
|
||||
Person mapPerson(PersonDto dto);
|
||||
|
||||
@Mapping(source = "garage.colour", target = "garage.color")
|
||||
@Mappings( {
|
||||
@Mapping( target = "garage.color", source = "garage.colour" ),
|
||||
@Mapping( target = "fullAge", source = "age" ),
|
||||
@Mapping( target = "fullName", source = "name" )
|
||||
} )
|
||||
Person mapPersonGarage(PersonDto dto);
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user