readAndWriteTargetProperties = new HashSet<>( unprocessedTargetProperties.keySet() );
+ readAndWriteTargetProperties.addAll( resultTypeToMap.getPropertyReadAccessors().keySet() );
+ mappingReferences = forSourceMethod(
+ (SourceMethod) method,
+ resultTypeToMap,
+ readAndWriteTargetProperties,
+ ctx.getMessager(),
+ ctx.getTypeFactory()
+ );
+ }
+ }
+
/**
* @return builder is required when there is a returnTypeBuilder and the mapping method is not update method.
* However, builder is also required when there is a returnTypeBuilder, the mapping target is the builder and
@@ -320,9 +349,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
PropertyMapping propertyMapping = new PropertyMappingBuilder()
.mappingContext( ctx )
.sourceMethod( method )
- .targetWriteAccessor( unprocessedTargetProperties.get( propertyName ) )
- .targetReadAccessor( targetPropertyReadAccessor )
- .targetPropertyName( propertyName )
+ .target(
+ propertyName,
+ targetPropertyReadAccessor,
+ unprocessedTargetProperties.get( propertyName )
+ )
.sourceReference( reference )
.existingVariableNames( existingVariableNames )
.dependsOn( mappingRefs.collectNestedDependsOn() )
@@ -464,22 +495,24 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
*
* It is furthermore checked whether the given mappings are correct. When an error occurs, the method continues
* in search of more problems.
+ *
+ * @param resultTypeToMap the type in which the defined target properties are defined
*/
- private boolean handleDefinedMappings() {
+ private boolean handleDefinedMappings(Type resultTypeToMap) {
boolean errorOccurred = false;
Set handledTargets = new HashSet<>();
// first we have to handle nested target mappings
if ( mappingReferences.hasNestedTargetReferences() ) {
- errorOccurred = handleDefinedNestedTargetMapping( handledTargets );
+ errorOccurred = handleDefinedNestedTargetMapping( handledTargets, resultTypeToMap );
}
for ( MappingReference mapping : mappingReferences.getMappingReferences() ) {
if ( mapping.isValid() ) {
String target = mapping.getTargetReference().getShallowestPropertyName();
if ( !handledTargets.contains( target ) ) {
- if ( handleDefinedMapping( mapping, handledTargets ) ) {
+ if ( handleDefinedMapping( mapping, resultTypeToMap, handledTargets ) ) {
errorOccurred = true;
}
}
@@ -504,11 +537,13 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
return errorOccurred;
}
- private boolean handleDefinedNestedTargetMapping(Set handledTargets) {
+ private boolean handleDefinedNestedTargetMapping(Set handledTargets, Type resultTypeToMap) {
NestedTargetPropertyMappingHolder holder = new NestedTargetPropertyMappingHolder.Builder()
.mappingContext( ctx )
.method( method )
+ .targetPropertiesWriteAccessors( unprocessedTargetProperties )
+ .targetPropertyType( resultTypeToMap )
.mappingReferences( mappingReferences )
.existingVariableNames( existingVariableNames )
.build();
@@ -517,26 +552,24 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
propertyMappings.addAll( holder.getPropertyMappings() );
handledTargets.addAll( holder.getHandledTargets() );
// Store all the unprocessed defined targets.
- for ( Entry> entry : holder.getUnprocessedDefinedTarget()
+ for ( Entry> entry : holder.getUnprocessedDefinedTarget()
.entrySet() ) {
if ( entry.getValue().isEmpty() ) {
continue;
}
- unprocessedDefinedTargets.put( entry.getKey().getName(), entry.getValue() );
+ unprocessedDefinedTargets.put( entry.getKey(), entry.getValue() );
}
return holder.hasErrorOccurred();
}
- private boolean handleDefinedMapping(MappingReference mappingRef, Set handledTargets) {
-
+ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTypeToMap,
+ Set handledTargets) {
boolean errorOccured = false;
PropertyMapping propertyMapping = null;
TargetReference targetRef = mappingRef.getTargetReference();
MappingOptions mapping = mappingRef.getMapping();
- PropertyEntry targetProperty = first( targetRef.getPropertyEntries() );
- String targetPropertyName = targetProperty.getName();
// unknown properties given via dependsOn()?
for ( String dependency : mapping.getDependsOn() ) {
@@ -552,16 +585,95 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
}
}
+ String targetPropertyName = first( targetRef.getPropertyEntries() );
+
// check if source / expression / constant are not somehow handled already
if ( unprocessedDefinedTargets.containsKey( targetPropertyName ) ) {
return false;
}
+ Accessor targetWriteAccessor = unprocessedTargetProperties.get( targetPropertyName );
+ Accessor targetReadAccessor = resultTypeToMap.getPropertyReadAccessors().get( targetPropertyName );
+
+ if ( targetWriteAccessor == null ) {
+ if ( targetReadAccessor == null ) {
+ Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet();
+ String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors );
+
+ Message msg;
+ Object[] args;
+
+ if ( targetRef.getPathProperties().isEmpty() ) {
+ msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE;
+ args = new Object[] {
+ targetPropertyName,
+ resultTypeToMap,
+ mostSimilarProperty
+ };
+ }
+ else {
+ List pathProperties = new ArrayList<>( targetRef.getPathProperties() );
+ pathProperties.add( mostSimilarProperty );
+ msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE;
+ args = new Object[] {
+ targetPropertyName,
+ resultTypeToMap,
+ mapping.getTargetName(),
+ Strings.join( pathProperties, "." )
+ };
+ }
+
+ ctx.getMessager()
+ .printMessage(
+ mapping.getElement(),
+ mapping.getMirror(),
+ mapping.getTargetAnnotationValue(),
+ msg,
+ args
+ );
+ return true;
+ }
+ else if ( mapping.getInheritContext() != null && mapping.getInheritContext().isReversed() ) {
+ // read only reversed mappings are implicitly ignored
+ return false;
+ }
+ else if ( !mapping.isIgnored() ) {
+ // report an error for read only mappings
+ Message msg;
+ Object[] args;
+
+ if ( Objects.equals( targetPropertyName, mapping.getTargetName() ) ) {
+ msg = Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE;
+ args = new Object[] {
+ mapping.getTargetName(),
+ resultTypeToMap
+ };
+ }
+ else {
+ msg = Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_TYPE;
+ args = new Object[] {
+ targetPropertyName,
+ resultTypeToMap,
+ mapping.getTargetName()
+ };
+ }
+ ctx.getMessager()
+ .printMessage(
+ mapping.getElement(),
+ mapping.getMirror(),
+ mapping.getTargetAnnotationValue(),
+ msg,
+ args
+ );
+ return true;
+ }
+ }
+
// check the mapping options
// its an ignored property mapping
if ( mapping.isIgnored() ) {
propertyMapping = null;
- handledTargets.add( targetProperty.getName() );
+ handledTargets.add( targetPropertyName );
}
// its a constant
@@ -573,8 +685,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
.mappingContext( ctx )
.sourceMethod( method )
.constantExpression( mapping.getConstant() )
- .targetProperty( targetProperty )
- .targetPropertyName( targetPropertyName )
+ .target( targetPropertyName, targetReadAccessor, targetWriteAccessor )
.formattingParameters( mapping.getFormattingParameters() )
.selectionParameters( mapping.getSelectionParameters() )
.options( mapping )
@@ -595,8 +706,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
.sourceMethod( method )
.javaExpression( mapping.getJavaExpression() )
.existingVariableNames( existingVariableNames )
- .targetProperty( targetProperty )
- .targetPropertyName( targetPropertyName )
+ .target( targetPropertyName, targetReadAccessor, targetWriteAccessor )
.dependsOn( mapping.getDependsOn() )
.mirror( mapping.getMirror() )
.build();
@@ -618,8 +728,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
propertyMapping = new PropertyMappingBuilder()
.mappingContext( ctx )
.sourceMethod( method )
- .targetProperty( targetProperty )
- .targetPropertyName( targetPropertyName )
+ .target( targetPropertyName, targetReadAccessor, targetWriteAccessor )
.sourcePropertyName( mapping.getSourceName() )
.sourceReference( sourceRef )
.selectionParameters( mapping.getSelectionParameters() )
@@ -723,15 +832,13 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
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 )
- .options( method.getOptions().getBeanMapping() )
- .build();
+ .sourceMethod( method )
+ .target( targetPropertyName, targetPropertyReadAccessor, targetPropertyWriteAccessor )
+ .sourceReference( sourceRef )
+ .existingVariableNames( existingVariableNames )
+ .forgeMethodWithMappingReferences( mappingRefs )
+ .options( method.getOptions().getBeanMapping() )
+ .build();
unprocessedSourceParameters.remove( sourceRef.getParameter() );
@@ -770,9 +877,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
PropertyMapping propertyMapping = new PropertyMappingBuilder()
.mappingContext( ctx )
.sourceMethod( method )
- .targetWriteAccessor( targetProperty.getValue() )
- .targetReadAccessor( targetPropertyReadAccessor )
- .targetPropertyName( targetProperty.getKey() )
+ .target( targetProperty.getKey(), targetPropertyReadAccessor, targetProperty.getValue() )
.sourceReference( sourceRef )
.existingVariableNames( existingVariableNames )
.forgeMethodWithMappingReferences( mappingRefs )
@@ -866,17 +971,25 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
}
else if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
- Message msg = unmappedTargetPolicy.getDiagnosticKind() == Diagnostic.Kind.ERROR ?
- Message.BEANMAPPING_UNMAPPED_TARGETS_ERROR : Message.BEANMAPPING_UNMAPPED_TARGETS_WARNING;
- Object[] args = new Object[] {
- MessageFormat.format(
- "{0,choice,1#property|1 processedSourceParameters;
private final Set handledTargets;
private final List propertyMappings;
- private final Map> unprocessedDefinedTarget;
+ private final Map> unprocessedDefinedTarget;
private final boolean errorOccurred;
public NestedTargetPropertyMappingHolder(
List processedSourceParameters, Set handledTargets,
List propertyMappings,
- Map> unprocessedDefinedTarget, boolean errorOccurred) {
+ Map> unprocessedDefinedTarget, boolean errorOccurred) {
this.processedSourceParameters = processedSourceParameters;
this.handledTargets = handledTargets;
this.propertyMappings = propertyMappings;
@@ -75,7 +79,7 @@ public class NestedTargetPropertyMappingHolder {
/**
* @return a map of all the unprocessed defined targets that can be applied to name forged base methods
*/
- public Map> getUnprocessedDefinedTarget() {
+ public Map> getUnprocessedDefinedTarget() {
return unprocessedDefinedTarget;
}
@@ -94,6 +98,9 @@ public class NestedTargetPropertyMappingHolder {
private Set existingVariableNames;
private List propertyMappings;
private Set handledTargets;
+ private Map targetPropertiesWriteAccessors;
+ private Type targetType;
+ private boolean errorOccurred;
public Builder mappingReferences(MappingReferences mappingReferences) {
this.mappingReferences = mappingReferences;
@@ -115,6 +122,16 @@ public class NestedTargetPropertyMappingHolder {
return this;
}
+ public Builder targetPropertiesWriteAccessors(Map targetPropertiesWriteAccessors) {
+ this.targetPropertiesWriteAccessors = targetPropertiesWriteAccessors;
+ return this;
+ }
+
+ public Builder targetPropertyType(Type targetType) {
+ this.targetType = targetType;
+ return this;
+ }
+
public NestedTargetPropertyMappingHolder build() {
List processedSourceParameters = new ArrayList<>();
handledTargets = new HashSet<>();
@@ -123,11 +140,11 @@ public class NestedTargetPropertyMappingHolder {
// first we group by the first property in the target properties and for each of those
// properties we get the new mappings as if the first property did not exist.
GroupedTargetReferences groupedByTP = groupByTargetReferences( );
- Map> unprocessedDefinedTarget = new LinkedHashMap<>();
+ Map> unprocessedDefinedTarget = new LinkedHashMap<>();
- for ( Map.Entry> entryByTP :
+ for ( Map.Entry> entryByTP :
groupedByTP.poppedTargetReferences.entrySet() ) {
- PropertyEntry targetProperty = entryByTP.getKey();
+ String targetProperty = entryByTP.getKey();
//Now we are grouping the already popped mappings by the source parameter(s) of the method
GroupedBySourceParameters groupedBySourceParam = groupBySourceParameter(
entryByTP.getValue(),
@@ -188,7 +205,7 @@ public class NestedTargetPropertyMappingHolder {
.type( sourceEntry.getType() )
.readAccessor( sourceEntry.getReadAccessor() )
.presenceChecker( sourceEntry.getPresenceChecker() )
- .name( targetProperty.getName() )
+ .name( targetProperty )
.build();
// If we have multiple source parameters that are mapped to the target reference, or
@@ -205,7 +222,7 @@ public class NestedTargetPropertyMappingHolder {
propertyMappings.add( propertyMapping );
}
- handledTargets.add( entryByTP.getKey().getName() );
+ handledTargets.add( entryByTP.getKey() );
}
// For the nonNested mappings (assymetric) Mappings we also forge mappings
@@ -216,7 +233,7 @@ public class NestedTargetPropertyMappingHolder {
new MappingReferences( groupedSourceReferences.nonNested, true );
SourceReference reference = new SourceReference.BuilderFromProperty()
.sourceParameter( sourceParameter )
- .name( targetProperty.getName() )
+ .name( targetProperty )
.build();
boolean forceUpdateMethodForNonNested =
@@ -236,7 +253,7 @@ public class NestedTargetPropertyMappingHolder {
propertyMappings.add( propertyMapping );
}
- handledTargets.add( entryByTP.getKey().getName() );
+ handledTargets.add( entryByTP.getKey() );
}
handleSourceParameterMappings(
@@ -255,7 +272,7 @@ public class NestedTargetPropertyMappingHolder {
handledTargets,
propertyMappings,
unprocessedDefinedTarget,
- groupedByTP.errorOccurred
+ errorOccurred
);
}
@@ -268,7 +285,7 @@ public class NestedTargetPropertyMappingHolder {
* @param forceUpdateMethod whether we need to force an update method
*/
private void handleSourceParameterMappings(Set sourceParameterMappings,
- PropertyEntry targetProperty, Parameter sourceParameter,
+ String targetProperty, Parameter sourceParameter,
boolean forceUpdateMethod) {
if ( !sourceParameterMappings.isEmpty() ) {
// The source parameter mappings have no mappings, the source name is actually the parameter itself
@@ -276,7 +293,7 @@ public class NestedTargetPropertyMappingHolder {
new MappingReferences( Collections.emptySet(), false, true );
SourceReference reference = new SourceReference.BuilderFromProperty()
.sourceParameter( sourceParameter )
- .name( targetProperty.getName() )
+ .name( targetProperty )
.build();
PropertyMapping propertyMapping = createPropertyMappingForNestedTarget(
@@ -290,7 +307,7 @@ public class NestedTargetPropertyMappingHolder {
propertyMappings.add( propertyMapping );
}
- handledTargets.add( targetProperty.getName() );
+ handledTargets.add( targetProperty );
}
}
@@ -340,16 +357,11 @@ public class NestedTargetPropertyMappingHolder {
*/
private GroupedTargetReferences groupByTargetReferences( ) {
// group all mappings based on the top level name before popping
- Map> mappingsKeyedByProperty = new LinkedHashMap<>();
- Map> singleTargetReferences = new LinkedHashMap<>();
- boolean errorOccurred = false;
+ Map> mappingsKeyedByProperty = new LinkedHashMap<>();
+ Map> singleTargetReferences = new LinkedHashMap<>();
for ( MappingReference mapping : mappingReferences.getMappingReferences() ) {
TargetReference targetReference = mapping.getTargetReference();
- if ( !targetReference.isValid() ) {
- errorOccurred = true;
- continue;
- }
- PropertyEntry property = first( targetReference.getPropertyEntries() );
+ String property = first( targetReference.getPropertyEntries() );
MappingReference newMapping = mapping.popTargetReference();
if ( newMapping != null ) {
// group properties on current name.
@@ -362,7 +374,7 @@ public class NestedTargetPropertyMappingHolder {
}
}
- return new GroupedTargetReferences( mappingsKeyedByProperty, singleTargetReferences, errorOccurred );
+ return new GroupedTargetReferences( mappingsKeyedByProperty, singleTargetReferences );
}
/**
@@ -625,14 +637,45 @@ public class NestedTargetPropertyMappingHolder {
}
private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences mappingReferences,
- PropertyEntry targetProperty,
+ String targetPropertyName,
SourceReference sourceReference,
boolean forceUpdateMethod) {
+
+ Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName );
+ Accessor targetReadAccessor = targetType.getPropertyReadAccessors().get( targetPropertyName );
+ if ( targetWriteAccessor == null ) {
+ Set readAccessors = targetType.getPropertyReadAccessors().keySet();
+ String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors );
+
+ for ( MappingReference mappingReference : mappingReferences.getMappingReferences() ) {
+ MappingOptions mapping = mappingReference.getMapping();
+ List pathProperties = new ArrayList<>( mappingReference.getTargetReference()
+ .getPathProperties() );
+ if ( !pathProperties.isEmpty() ) {
+ pathProperties.set( pathProperties.size() - 1, mostSimilarProperty );
+ }
+
+ mappingContext.getMessager()
+ .printMessage(
+ mapping.getElement(),
+ mapping.getMirror(),
+ mapping.getTargetAnnotationValue(),
+ Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE,
+ targetPropertyName,
+ targetType,
+ mapping.getTargetName(),
+ Strings.join( pathProperties, "." )
+ );
+ }
+
+ errorOccurred = true;
+ return null;
+ }
+
return new PropertyMapping.PropertyMappingBuilder()
.mappingContext( mappingContext )
.sourceMethod( method )
- .targetProperty( targetProperty )
- .targetPropertyName( targetProperty.getName() )
+ .target( targetPropertyName, targetReadAccessor, targetWriteAccessor )
.sourceReference( sourceReference )
.existingVariableNames( existingVariableNames )
.dependsOn( mappingReferences.collectNestedDependsOn() )
@@ -685,21 +728,19 @@ public class NestedTargetPropertyMappingHolder {
* references (target references that were not nested).
*/
private static class GroupedTargetReferences {
- private final Map> poppedTargetReferences;
- private final Map> singleTargetReferences;
- private final boolean errorOccurred;
+ private final Map> poppedTargetReferences;
+ private final Map> singleTargetReferences;
- private GroupedTargetReferences(Map> poppedTargetReferences,
- Map> singleTargetReferences, boolean errorOccurred) {
+ private GroupedTargetReferences(Map> poppedTargetReferences,
+ Map> singleTargetReferences) {
this.poppedTargetReferences = poppedTargetReferences;
this.singleTargetReferences = singleTargetReferences;
- this.errorOccurred = errorOccurred;
}
@Override
public String toString() {
return "GroupedTargetReferences{" + "poppedTargetReferences=" + poppedTargetReferences
- + ", singleTargetReferences=" + singleTargetReferences + ", errorOccurred=" + errorOccurred + '}';
+ + ", singleTargetReferences=" + singleTargetReferences + "}";
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
index 06de2aa39..24facce72 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
@@ -94,21 +94,9 @@ public class PropertyMapping extends ModelElement {
return super.method( sourceMethod );
}
- public T targetProperty(PropertyEntry targetProp) {
- this.targetReadAccessor = targetProp.getReadAccessor();
- this.targetWriteAccessor = targetProp.getWriteAccessor();
- this.targetType = targetProp.getType();
- this.targetBuilderType = targetProp.getBuilderType();
- this.targetWriteAccessorType = targetWriteAccessor.getAccessorType();
- return (T) this;
- }
-
- public T targetReadAccessor(Accessor targetReadAccessor) {
+ public T target(String targetPropertyName, Accessor targetReadAccessor, Accessor targetWriteAccessor) {
+ this.targetPropertyName = targetPropertyName;
this.targetReadAccessor = targetReadAccessor;
- return (T) this;
- }
-
- public T targetWriteAccessor(Accessor targetWriteAccessor) {
this.targetWriteAccessor = targetWriteAccessor;
this.targetType = ctx.getTypeFactory().getType( targetWriteAccessor.getAccessedType() );
BuilderGem builder = method.getOptions().getBeanMapping().getBuilder();
@@ -122,11 +110,6 @@ public class PropertyMapping extends ModelElement {
return (T) this;
}
- public T targetPropertyName(String targetPropertyName) {
- this.targetPropertyName = targetPropertyName;
- return (T) this;
- }
-
public T sourcePropertyName(String sourcePropertyName) {
this.sourcePropertyName = sourcePropertyName;
return (T) this;
@@ -326,6 +309,11 @@ public class PropertyMapping extends ModelElement {
* Report that a mapping could not be created.
*/
private void reportCannotCreateMapping() {
+ if ( forgeMethodWithMappingReferences != null && ctx.isErroneous() ) {
+ // If we arrived here, there is an error it means that we couldn't forge a mapping method
+ // so skip the cannot create mapping
+ return;
+ }
if ( method instanceof ForgedMethod && ( (ForgedMethod) method ).getHistory() != null ) {
// The history that is part of the ForgedMethod misses the information from the current right hand
// side. Therefore we need to extract the most relevant history and use that in the error reporting.
@@ -363,9 +351,7 @@ public class PropertyMapping extends ModelElement {
.existingVariableNames( existingVariableNames )
.mappingContext( ctx )
.sourceMethod( method )
- .targetPropertyName( targetPropertyName )
- .targetReadAccessor( targetReadAccessor )
- .targetWriteAccessor( targetWriteAccessor )
+ .target( targetPropertyName, targetReadAccessor, targetWriteAccessor )
.build();
return build.getAssignment();
}
@@ -378,9 +364,7 @@ public class PropertyMapping extends ModelElement {
.existingVariableNames( existingVariableNames )
.mappingContext( ctx )
.sourceMethod( method )
- .targetPropertyName( targetPropertyName )
- .targetReadAccessor( targetReadAccessor )
- .targetWriteAccessor( targetWriteAccessor )
+ .target( targetPropertyName, targetReadAccessor, targetWriteAccessor )
.build();
return build.getAssignment();
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java
index 2675e2900..e2501ea95 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java
@@ -80,11 +80,7 @@ public class MappingReference {
}
public boolean isValid( ) {
- boolean result = false;
- if ( targetReference.isValid() ) {
- result = sourceReference != null ? sourceReference.isValid() : true;
- }
- return result;
+ return sourceReference == null || sourceReference.isValid();
}
@Override
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java
index 63af65f2e..06f0ed06c 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java
@@ -11,6 +11,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.model.source.MappingOptions;
import org.mapstruct.ap.internal.model.source.SourceMethod;
@@ -29,8 +30,11 @@ public class MappingReferences {
return EMPTY;
}
- public static MappingReferences forSourceMethod(SourceMethod sourceMethod, FormattingMessager messager,
- TypeFactory typeFactory) {
+ public static MappingReferences forSourceMethod(SourceMethod sourceMethod,
+ Type targetType,
+ Set targetProperties,
+ FormattingMessager messager,
+ TypeFactory typeFactory) {
Set references = new LinkedHashSet<>();
List targetThisReferences = new ArrayList<>( );
@@ -49,6 +53,8 @@ public class MappingReferences {
.method( sourceMethod )
.messager( messager )
.typeFactory( typeFactory )
+ .targetProperties( targetProperties )
+ .targetType( targetType )
.build();
// add when inverse is also valid
@@ -121,7 +127,7 @@ public class MappingReferences {
for ( MappingReference mappingRef : mappingReferences ) {
TargetReference targetReference = mappingRef.getTargetReference();
- if ( targetReference.isValid() && targetReference.isNested()) {
+ if ( targetReference.isNested()) {
return true;
}
@@ -141,7 +147,7 @@ public class MappingReferences {
private static boolean isValidWhenInversed(MappingReference mappingRef) {
MappingOptions mapping = mappingRef.getMapping();
if ( mapping.getInheritContext() != null && mapping.getInheritContext().isReversed() ) {
- return mappingRef.getTargetReference().isValid() && ( mappingRef.getSourceReference() == null ||
+ return ( mappingRef.getSourceReference() == null ||
mappingRef.getSourceReference().isValid() );
}
return true;
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java
index f2d087cd9..8ac5e4667 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java
@@ -7,55 +7,33 @@ package org.mapstruct.ap.internal.model.beanmapping;
import java.util.Arrays;
-import org.mapstruct.ap.internal.model.common.BuilderType;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.util.accessor.Accessor;
/**
- * A PropertyEntry contains information on the name, readAccessor (for source), readAccessor and writeAccessor
- * (for targets) and return type of a property.
+ * A PropertyEntry contains information on the name, readAccessor and presenceCheck (for source)
+ * and return type of a property.
*/
public class PropertyEntry {
private final String[] fullName;
private final Accessor readAccessor;
- private final Accessor writeAccessor;
private final Accessor presenceChecker;
private final Type type;
- private final BuilderType builderType;
/**
* Constructor used to create {@link TargetReference} property entries from a mapping
*
* @param fullName
* @param readAccessor
- * @param writeAccessor
* @param type
*/
- private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor writeAccessor,
- Accessor presenceChecker, Type type, BuilderType builderType) {
+ private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor presenceChecker, Type type) {
this.fullName = fullName;
this.readAccessor = readAccessor;
- this.writeAccessor = writeAccessor;
this.presenceChecker = presenceChecker;
this.type = type;
- this.builderType = builderType;
- }
-
- /**
- * Constructor used to create {@link TargetReference} property entries
- *
- * @param fullName name of the property (dot separated)
- * @param readAccessor its read accessor
- * @param writeAccessor its write accessor
- * @param type type of the property
- * @param builderType the builder for the type
- * @return the property entry for given parameters.
- */
- public static PropertyEntry forTargetReference(String[] fullName, Accessor readAccessor,
- Accessor writeAccessor, Type type, BuilderType builderType ) {
- return new PropertyEntry( fullName, readAccessor, writeAccessor, null, type, builderType );
}
/**
@@ -69,7 +47,7 @@ public class PropertyEntry {
*/
public static PropertyEntry forSourceReference(String[] name, Accessor readAccessor,
Accessor presenceChecker, Type type) {
- return new PropertyEntry( name, readAccessor, null, presenceChecker, type, null );
+ return new PropertyEntry( name, readAccessor, presenceChecker, type );
}
public String getName() {
@@ -80,10 +58,6 @@ public class PropertyEntry {
return readAccessor;
}
- public Accessor getWriteAccessor() {
- return writeAccessor;
- }
-
public Accessor getPresenceChecker() {
return presenceChecker;
}
@@ -92,24 +66,10 @@ public class PropertyEntry {
return type;
}
- public BuilderType getBuilderType() {
- return builderType;
- }
-
public String getFullName() {
return Strings.join( Arrays.asList( fullName ), "." );
}
- public PropertyEntry pop() {
- if ( fullName.length > 1 ) {
- String[] newFullName = Arrays.copyOfRange( fullName, 1, fullName.length );
- return new PropertyEntry(newFullName, readAccessor, writeAccessor, presenceChecker, type, builderType );
- }
- else {
- return null;
- }
- }
-
@Override
public int hashCode() {
int hash = 7;
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java
index 8a2a39823..fee97f840 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java
@@ -7,26 +7,21 @@ package org.mapstruct.ap.internal.model.beanmapping;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.type.DeclaredType;
-import org.mapstruct.ap.internal.model.common.BuilderType;
import org.mapstruct.ap.internal.model.common.Parameter;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.model.source.MappingOptions;
import org.mapstruct.ap.internal.model.source.Method;
-import org.mapstruct.ap.internal.gem.BuilderGem;
-import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem;
import org.mapstruct.ap.internal.util.FormattingMessager;
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.AccessorType;
import static org.mapstruct.ap.internal.util.Collections.first;
@@ -48,11 +43,77 @@ import static org.mapstruct.ap.internal.util.Collections.first;
* {@code propertyEntries[1]} will describe {@code propB}
*
*
- * After building, {@link #isValid()} will return true when when no problems are detected during building.
- *
* @author Sjaak Derksen
*/
-public class TargetReference extends AbstractReference {
+public class TargetReference {
+
+ private final List pathProperties;
+ private final Parameter parameter;
+ private final List propertyEntries;
+
+ public TargetReference(Parameter parameter, List propertyEntries) {
+ this( parameter, propertyEntries, Collections.emptyList() );
+ }
+
+ public TargetReference(Parameter parameter, List propertyEntries, List pathProperties) {
+ this.pathProperties = pathProperties;
+ this.parameter = parameter;
+ this.propertyEntries = propertyEntries;
+ }
+
+ public List getPathProperties() {
+ return pathProperties;
+ }
+
+ public List getPropertyEntries() {
+ return propertyEntries;
+ }
+
+ public List getElementNames() {
+ List elementNames = new ArrayList<>();
+ if ( parameter != null ) {
+ // only relevant for source properties
+ elementNames.add( parameter.getName() );
+ }
+ elementNames.addAll( propertyEntries );
+ return elementNames;
+ }
+
+ /**
+ * returns the property name on the shallowest nesting level
+ */
+ public String getShallowestPropertyName() {
+ if ( propertyEntries.isEmpty() ) {
+ return null;
+ }
+ return first( propertyEntries );
+ }
+
+ public boolean isNested() {
+ return propertyEntries.size() > 1;
+ }
+
+ @Override
+ public String toString() {
+
+ String result = "";
+ if ( propertyEntries.isEmpty() ) {
+ if ( parameter != null ) {
+ result = String.format( "parameter \"%s %s\"", parameter.getType(), parameter.getName() );
+ }
+ }
+ else if ( propertyEntries.size() == 1 ) {
+ String propertyEntry = propertyEntries.get( 0 );
+ result = String.format( "property \"%s\"", propertyEntry );
+ }
+ else {
+ result = String.format(
+ "property \"%s\"",
+ Strings.join( getElementNames(), "." )
+ );
+ }
+ return result;
+ }
/**
* Builds a {@link TargetReference} from an {@code @Mappping}.
@@ -62,21 +123,15 @@ public class TargetReference extends AbstractReference {
private Method method;
private FormattingMessager messager;
private TypeFactory typeFactory;
+ private Set targetProperties;
+ private Type targetType;
// mapping parameters
- private boolean isReversed = false;
- private boolean isIgnored = false;
private String targetName = null;
+ private MappingOptions mapping;
private AnnotationMirror annotationMirror = null;
private AnnotationValue targetAnnotationValue = null;
private AnnotationValue sourceAnnotationValue = null;
- private Method templateMethod = null;
- /**
- * During {@link #getTargetEntries(Type, String[])} an error can occur. However, we are invoking
- * that multiple times because the first entry can also be the name of the parameter. Therefore we keep
- * the error message until the end when we can report it.
- */
- private MappingErrorMessage errorMessage;
public Builder messager(FormattingMessager messager) {
this.messager = messager;
@@ -84,11 +139,7 @@ public class TargetReference extends AbstractReference {
}
public Builder mapping(MappingOptions mapping) {
- if ( mapping.getInheritContext() != null ) {
- this.isReversed = mapping.getInheritContext().isReversed();
- this.templateMethod = mapping.getInheritContext().getTemplateMethod();
- }
- this.isIgnored = mapping.isIgnored();
+ this.mapping = mapping;
this.targetName = mapping.getTargetName();
this.annotationMirror = mapping.getMirror();
this.targetAnnotationValue = mapping.getTargetAnnotationValue();
@@ -106,11 +157,22 @@ public class TargetReference extends AbstractReference {
return this;
}
+ public Builder targetProperties(Set targetProperties) {
+ this.targetProperties = targetProperties;
+ return this;
+ }
+
+ public Builder targetType(Type targetType) {
+ this.targetType = targetType;
+ return this;
+ }
+
public TargetReference build() {
Objects.requireNonNull( method );
Objects.requireNonNull( typeFactory );
Objects.requireNonNull( messager );
+ Objects.requireNonNull( targetType );
if ( targetName == null ) {
return null;
@@ -130,185 +192,27 @@ public class TargetReference extends AbstractReference {
String[] segments = targetNameTrimmed.split( "\\." );
Parameter parameter = method.getMappingTargetParameter();
- boolean foundEntryMatch;
- Type resultType = typeBasedOnMethod( method.getResultType() );
-
// there can be 4 situations
// 1. Return type
// 2. An inverse target reference where the source parameter name is used in the original mapping
// 3. @MappingTarget, with
// 4. or without parameter name.
String[] targetPropertyNames = segments;
- List entries = getTargetEntries( resultType, targetPropertyNames );
- foundEntryMatch = (entries.size() == targetPropertyNames.length);
- if ( !foundEntryMatch && segments.length > 1
- && matchesSourceOrTargetParameter( segments[0], parameter, isReversed ) ) {
- targetPropertyNames = Arrays.copyOfRange( segments, 1, segments.length );
- entries = getTargetEntries( resultType, targetPropertyNames );
- foundEntryMatch = (entries.size() == targetPropertyNames.length);
- }
-
- if ( !foundEntryMatch && errorMessage != null && !isReversed ) {
- // This is called only for reporting errors
- errorMessage.report( );
- }
-
- // foundEntryMatch = isValid, errors are handled here, and the BeanMapping uses that to ignore
- // the creation of mapping for invalid TargetReferences
- return new TargetReference( parameter, entries, foundEntryMatch );
- }
-
- private List getTargetEntries(Type type, String[] entryNames) {
-
- // initialize
- CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy();
- List targetEntries = new ArrayList<>();
- Type nextType = type;
-
- // iterate, establish for each entry the target write accessors. Other than setter is only allowed for
- // last entry
- for ( int i = 0; i < entryNames.length; i++ ) {
-
- Type mappingType = typeBasedOnMethod( nextType );
- Accessor targetReadAccessor = mappingType.getPropertyReadAccessors().get( entryNames[i] );
- Accessor targetWriteAccessor = mappingType.getPropertyWriteAccessors( cms ).get( entryNames[i] );
- boolean isLast = i == entryNames.length - 1;
- boolean isNotLast = i < entryNames.length - 1;
- if ( isWriteAccessorNotValidWhenNotLast( targetWriteAccessor, isNotLast )
- || isWriteAccessorNotValidWhenLast( targetWriteAccessor, targetReadAccessor, isIgnored, isLast ) ) {
- // there should always be a write accessor (except for the last when the mapping is ignored and
- // there is a read accessor) and there should be read accessor mandatory for all but the last
- setErrorMessage( targetWriteAccessor, targetReadAccessor, entryNames, i, nextType );
- break;
- }
-
- if ( isLast || ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER ||
- targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) ) {
- // only intermediate nested properties when they are a true setter or field accessor
- // the last may be other readAccessor (setter / getter / adder).
-
- nextType = findNextType( nextType, targetWriteAccessor, targetReadAccessor );
-
- // check if an entry alread exists, otherwise create
- String[] fullName = Arrays.copyOfRange( entryNames, 0, i + 1 );
- BuilderType builderType;
- PropertyEntry propertyEntry = null;
- if ( method.isUpdateMethod() ) {
- propertyEntry = PropertyEntry.forTargetReference( fullName,
- targetReadAccessor,
- targetWriteAccessor,
- nextType,
- null
- );
+ if ( segments.length > 1 ) {
+ String firstTargetProperty = targetPropertyNames[0];
+ // If the first target property is not within the defined target properties, then check if it matches
+ // the source or target parameter
+ if ( !targetProperties.contains( firstTargetProperty ) ) {
+ if ( matchesSourceOrTargetParameter( firstTargetProperty, parameter ) ) {
+ targetPropertyNames = Arrays.copyOfRange( segments, 1, segments.length );
}
- else {
- BuilderGem builder = method.getOptions().getBeanMapping().getBuilder();
- builderType = typeFactory.builderTypeFor( nextType, builder );
- propertyEntry = PropertyEntry.forTargetReference( fullName,
- targetReadAccessor,
- targetWriteAccessor,
- nextType,
- builderType
- );
-
- }
- targetEntries.add( propertyEntry );
}
-
}
- return targetEntries;
- }
- /**
- * Finds the next type based on the initial type.
- *
- * @param initial for which a next type should be found
- * @param targetWriteAccessor the write accessor
- * @param targetReadAccessor the read accessor
- * @return the next type that should be used for finding a property entry
- */
- private Type findNextType(Type initial, Accessor targetWriteAccessor, Accessor targetReadAccessor) {
- Type nextType;
- Accessor toUse = targetWriteAccessor != null ? targetWriteAccessor : targetReadAccessor;
- if ( toUse.getAccessorType() == AccessorType.GETTER || toUse.getAccessorType() == AccessorType.FIELD ) {
- nextType = typeFactory.getReturnType(
- (DeclaredType) typeBasedOnMethod( initial ).getTypeMirror(),
- toUse
- );
- }
- else {
- nextType = typeFactory.getSingleParameter(
- (DeclaredType) typeBasedOnMethod( initial ).getTypeMirror(),
- toUse
- ).getType();
- }
- return nextType;
- }
+ List entries = new ArrayList<>( Arrays.asList( targetPropertyNames ) );
- private void setErrorMessage(Accessor targetWriteAccessor, Accessor targetReadAccessor, String[] entryNames,
- int index, Type nextType) {
- if ( targetWriteAccessor == null && targetReadAccessor == null ) {
- errorMessage = new NoPropertyErrorMessage( this, entryNames, index, nextType );
- }
- else if ( targetWriteAccessor == null ) {
- errorMessage = new NoWriteAccessorErrorMessage(this );
- }
- else {
- //TODO there is no read accessor. What should we do here?
- errorMessage = new NoPropertyErrorMessage( this, entryNames, index, nextType );
- }
- }
-
- /**
- * When we are in an update method, i.e. source parameter with {@code @MappingTarget} then the type should
- * be itself, otherwise, we always get the effective type. The reason is that when doing updates we always
- * search for setters and getters within the updating type.
- */
- private Type typeBasedOnMethod(Type type) {
- if ( method.isUpdateMethod() ) {
- return type;
- }
- else {
- BuilderGem builder = method.getOptions().getBeanMapping().getBuilder();
- return typeFactory.effectiveResultTypeFor( type, builder );
- }
- }
-
- /**
- * A write accessor is not valid if it is {@code null} and it is not last. i.e. for nested target mappings
- * there must be a write accessor for all entries except the last one.
- *
- * @param accessor that needs to be checked
- * @param isNotLast whether or not this is the last write accessor in the entry chain
- *
- * @return {@code true} if the accessor is not valid, {@code false} otherwise
- */
- private static boolean isWriteAccessorNotValidWhenNotLast(Accessor accessor, boolean isNotLast) {
- return accessor == null && isNotLast;
- }
-
- /**
- * For a last accessor to be valid, a read accessor should exist and the mapping should be ignored. All other
- * cases represent an invalid write accessor. This method will evaluate to {@code true} if the following is
- * {@code true}:
- *
- * - {@code writeAccessor} is {@code null}
- * - It is for the last entry
- * - A read accessor does not exist, or the mapping is not ignored
- *
- *
- * @param writeAccessor that needs to be checked
- * @param readAccessor that is used
- * @param isIgnored true when ignored
- * @param isLast whether or not this is the last write accessor in the entry chain
- *
- * @return {@code true} if the write accessor is not valid, {@code false} otherwise. See description for more
- * information
- */
- private static boolean isWriteAccessorNotValidWhenLast(Accessor writeAccessor, Accessor readAccessor,
- boolean isIgnored, boolean isLast) {
- return writeAccessor == null && isLast && ( readAccessor == null || !isIgnored );
+ return new TargetReference( parameter, entries );
}
/**
@@ -317,16 +221,14 @@ public class TargetReference extends AbstractReference {
*
* @param segment that needs to be checked
* @param targetParameter the target parameter if it exists
-
- * @param isInverse whether a inverse {@link TargetReference} is being built
*
* @return {@code true} if the segment matches the name of the {@code targetParameter} or the name of the
* {@code inverseSourceParameter} when this is a inverse {@link TargetReference} is being built, {@code
* false} otherwise
*/
- private boolean matchesSourceOrTargetParameter(String segment, Parameter targetParameter, boolean isInverse) {
+ private boolean matchesSourceOrTargetParameter(String segment, Parameter targetParameter) {
boolean matchesTargetParameter = targetParameter != null && targetParameter.getName().equals( segment );
- return matchesTargetParameter || matchesSourceOnInverseSourceParameter( segment, isInverse );
+ return matchesTargetParameter || matchesSourceOnInverseSourceParameter( segment );
}
/**
@@ -344,13 +246,16 @@ public class TargetReference extends AbstractReference {
* a inverse is created.
*
* @param segment that needs to be checked*
- * @param isInverse whether a inverse {@link TargetReference} is being built
*
* @return on match when inverse and segment matches the one and only source parameter
*/
- private boolean matchesSourceOnInverseSourceParameter( String segment, boolean isInverse ) {
+ private boolean matchesSourceOnInverseSourceParameter(String segment) {
+
boolean result = false;
- if ( isInverse ) {
+ MappingOptions.InheritContext inheritContext = mapping.getInheritContext();
+ if ( inheritContext != null && inheritContext.isReversed() ) {
+
+ Method templateMethod = inheritContext.getTemplateMethod();
// there is only source parameter by definition when applying @InheritInverseConfiguration
Parameter inverseSourceParameter = first( templateMethod.getSourceParameters() );
result = inverseSourceParameter.getName().equals( segment );
@@ -359,87 +264,19 @@ public class TargetReference extends AbstractReference {
}
}
- private TargetReference(Parameter sourceParameter, List targetPropertyEntries, boolean isValid) {
- super( sourceParameter, targetPropertyEntries, isValid );
- }
-
public TargetReference pop() {
if ( getPropertyEntries().size() > 1 ) {
- List 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() );
+ List newPathProperties = new ArrayList<>( this.pathProperties );
+ newPathProperties.add( getPropertyEntries().get( 0 ) );
+ return new TargetReference(
+ null,
+ getPropertyEntries().subList( 1, getPropertyEntries().size() ),
+ newPathProperties
+ );
}
else {
return null;
}
}
- private abstract static class MappingErrorMessage {
- private final Builder builder;
-
- private MappingErrorMessage(Builder builder) {
- this.builder = builder;
- }
-
- abstract void report();
-
- protected void printErrorMessage(Message message, Object... args) {
- Object[] errorArgs = new Object[args.length + 2];
- errorArgs[0] = builder.targetName;
- errorArgs[1] = builder.method.getResultType();
- System.arraycopy( args, 0, errorArgs, 2, args.length );
- AnnotationMirror annotationMirror = builder.annotationMirror;
- builder.messager.printMessage( builder.method.getExecutable(),
- annotationMirror,
- builder.sourceAnnotationValue,
- message,
- errorArgs
- );
- }
- }
-
- private static class NoWriteAccessorErrorMessage extends MappingErrorMessage {
-
- private NoWriteAccessorErrorMessage(Builder builder) {
- super( builder );
- }
-
- @Override
- public void report() {
- printErrorMessage( Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE );
- }
- }
-
- private static class NoPropertyErrorMessage extends MappingErrorMessage {
-
- private final String[] entryNames;
- private final int index;
- private final Type nextType;
-
- private NoPropertyErrorMessage(Builder builder, String[] entryNames, int index,
- Type nextType) {
- super( builder );
- this.entryNames = entryNames;
- this.index = index;
- this.nextType = nextType;
- }
-
- @Override
- public void report() {
-
- Set readAccessors = nextType.getPropertyReadAccessors().keySet();
- String mostSimilarProperty = Strings.getMostSimilarWord( entryNames[index], readAccessors );
-
- List elements = new ArrayList<>( Arrays.asList( entryNames ).subList( 0, index ) );
- elements.add( mostSimilarProperty );
-
- printErrorMessage( Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE, Strings.join( elements, "." ) );
- }
- }
-
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java
index ed88e5107..b826295f7 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java
@@ -16,6 +16,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
@@ -50,6 +51,7 @@ public class MappingOptions extends DelegatingOptions {
private final boolean isIgnored;
private final Set dependsOn;
+ private final Element element;
private final AnnotationValue sourceAnnotationValue;
private final AnnotationValue targetAnnotationValue;
private final MappingGem mapping;
@@ -135,6 +137,7 @@ public class MappingOptions extends DelegatingOptions {
MappingOptions options = new MappingOptions(
mapping.target().getValue(),
+ method,
mapping.target().getAnnotationValue(),
source,
mapping.source().getAnnotationValue(),
@@ -169,6 +172,7 @@ public class MappingOptions extends DelegatingOptions {
null,
null,
null,
+ null,
true,
null,
null,
@@ -249,6 +253,7 @@ public class MappingOptions extends DelegatingOptions {
@SuppressWarnings("checkstyle:parameternumber")
private MappingOptions(String targetName,
+ Element element,
AnnotationValue targetAnnotationValue,
String sourceName,
AnnotationValue sourceAnnotationValue,
@@ -266,6 +271,7 @@ public class MappingOptions extends DelegatingOptions {
) {
super( next );
this.targetName = targetName;
+ this.element = element;
this.targetAnnotationValue = targetAnnotationValue;
this.sourceName = sourceName;
this.sourceAnnotationValue = sourceAnnotationValue;
@@ -377,6 +383,10 @@ public class MappingOptions extends DelegatingOptions {
return Optional.ofNullable( mapping ).map( MappingGem::mirror ).orElse( null );
}
+ public Element getElement() {
+ return element;
+ }
+
public AnnotationValue getDependsOnAnnotationValue() {
return Optional.ofNullable( mapping )
.map( MappingGem::dependsOn )
@@ -434,6 +444,7 @@ public class MappingOptions extends DelegatingOptions {
MappingOptions mappingOptions = new MappingOptions(
sourceName != null ? sourceName : targetName,
+ templateMethod.getExecutable(),
targetAnnotationValue,
sourceName != null ? targetName : null,
sourceAnnotationValue,
@@ -462,6 +473,7 @@ public class MappingOptions extends DelegatingOptions {
BeanMappingOptions beanMappingOptions ) {
MappingOptions mappingOptions = new MappingOptions(
targetName,
+ templateMethod.getExecutable(),
targetAnnotationValue,
sourceName,
sourceAnnotationValue,
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java
index bc7374d39..a9f9eff4a 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java
@@ -167,6 +167,7 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
}
}
+ @Override
public boolean isErroneous() {
return isErroneous;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java
index 197d9fa63..7746dd677 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java
@@ -368,10 +368,10 @@ public class MapperCreationProcessor implements ModelElementProcessor does not have an accessible parameterless constructor."),
- @Diagnostic(type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR,
- line = 16, message =
+ line = 17, message =
"No target bean properties found: can't map property \"org.mapstruct.ap.test.NoProperties "
+ "foo.wrapped\" to"
+ " \"org.mapstruct.ap.test.selection.generics.TypeA "
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java
index 731b743b8..210cc3692 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java
@@ -6,6 +6,7 @@
package org.mapstruct.ap.test.selection.generics;
import org.mapstruct.Mapper;
+import org.mapstruct.ObjectFactory;
import org.mapstruct.factory.Mappers;
@Mapper( uses = GenericTypeMapper.class )
@@ -14,4 +15,11 @@ public interface ErroneousSourceTargetMapper6 {
ErroneousSourceTargetMapper6 INSTANCE = Mappers.getMapper( ErroneousSourceTargetMapper6.class );
ErroneousTarget6 sourceToTarget(ErroneousSource6 source);
+
+ // We are testing that we can't create the nested
+ // not whether or not we can instantiate the WildCardSuperWrapper
+ @ObjectFactory
+ default WildCardSuperWrapper createWildCardSuperWrapper() {
+ return new WildCardSuperWrapper<>( null );
+ }
}