Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,10 @@ else if ( !method.isUpdateMethod() ) {

this.unprocessedTargetProperties = new LinkedHashMap<>( accessors );

boolean constructorAccessorHadError = false;
if ( !method.isUpdateMethod() && !hasFactoryMethod ) {
ConstructorAccessor constructorAccessor = getConstructorAccessor( resultTypeToMap );
if ( constructorAccessor != null ) {
if ( constructorAccessor != null && !constructorAccessor.hasError ) {

this.unprocessedConstructorProperties = constructorAccessor.constructorAccessors;

Expand All @@ -250,8 +251,10 @@ else if ( !method.isUpdateMethod() ) {
else {
this.unprocessedConstructorProperties = new LinkedHashMap<>();
}
constructorAccessorHadError = constructorAccessor != null && constructorAccessor.hasError;

this.targetProperties.addAll( this.unprocessedConstructorProperties.keySet() );

this.unprocessedTargetProperties.putAll( this.unprocessedConstructorProperties );
}
else {
Expand Down Expand Up @@ -320,7 +323,7 @@ else if ( !method.isUpdateMethod() ) {

// report errors on unmapped properties
if ( shouldHandledDefinedMappings ) {
reportErrorForUnmappedTargetPropertiesIfRequired();
reportErrorForUnmappedTargetPropertiesIfRequired( resultTypeToMap, constructorAccessorHadError );
reportErrorForUnmappedSourcePropertiesIfRequired();
}
reportErrorForMissingIgnoredSourceProperties();
Expand Down Expand Up @@ -939,7 +942,7 @@ private ConstructorAccessor getConstructorAccessor(Type type) {
)
.collect( Collectors.joining( ", " ) )
);
return null;
return new ConstructorAccessor( true, Collections.emptyList(), Collections.emptyMap() );
}
else {
return getConstructorAccessor( type, accessibleConstructors.get( 0 ) );
Expand Down Expand Up @@ -998,7 +1001,7 @@ else if ( constructorProperties.size() != constructorParameters.size() ) {
GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS,
type
);
return null;
return new ConstructorAccessor( true, Collections.emptyList(), Collections.emptyMap() );
}
else {
Map<String, Accessor> constructorAccessors = new LinkedHashMap<>();
Expand Down Expand Up @@ -1706,36 +1709,45 @@ private ReportingPolicyGem getUnmappedTargetPolicy() {
return method.getOptions().getMapper().unmappedTargetPolicy();
}

private void reportErrorForUnmappedTargetPropertiesIfRequired() {
private void reportErrorForUnmappedTargetPropertiesIfRequired(Type resultType,
boolean constructorAccessorHadError) {

// fetch settings from element to implement
ReportingPolicyGem unmappedTargetPolicy = getUnmappedTargetPolicy();

if ( method instanceof ForgedMethod && targetProperties.isEmpty() ) {
//TODO until we solve 1140 we report this error when the target properties are empty
ForgedMethod forgedMethod = (ForgedMethod) method;
if ( forgedMethod.getHistory() == null ) {
Type sourceType = this.method.getParameters().get( 0 ).getType();
Type targetType = this.method.getReturnType();
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND,
sourceType.describe(),
targetType.describe(),
targetType.describe(),
sourceType.describe()
);
if ( targetProperties.isEmpty() ) {
if ( method instanceof ForgedMethod ) {
ForgedMethod forgedMethod = (ForgedMethod) method;
if ( forgedMethod.getHistory() == null ) {
Type sourceType = this.method.getParameters().get( 0 ).getType();
Type targetType = this.method.getReturnType();
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND,
sourceType.describe(),
targetType.describe(),
targetType.describe(),
sourceType.describe()
);
}
else {
ForgedMethodHistory history = forgedMethod.getHistory();
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND,
history.createSourcePropertyErrorMessage(),
history.getTargetType().describe(),
history.createTargetPropertyName(),
history.getTargetType().describe(),
history.getSourceType().describe()
);
}
}
else {
ForgedMethodHistory history = forgedMethod.getHistory();
else if ( !constructorAccessorHadError ) {
ctx.getMessager().printMessage(
this.method.getExecutable(),
Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND,
history.createSourcePropertyErrorMessage(),
history.getTargetType().describe(),
history.createTargetPropertyName(),
history.getTargetType().describe(),
history.getSourceType().describe()
method.getExecutable(),
Message.PROPERTYMAPPING_TARGET_HAS_NO_TARGET_PROPERTIES,
resultType.describe()
);
}
}
Expand All @@ -1755,7 +1767,8 @@ else if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.require
reportErrorForUnmappedProperties(
unprocessedTargetProperties,
unmappedPropertiesMsg,
unmappedForgedPropertiesMsg );
unmappedForgedPropertiesMsg
);

}
}
Expand Down Expand Up @@ -1886,12 +1899,19 @@ private void reportErrorForUnusedSourceParameters() {
}

private static class ConstructorAccessor {
private final boolean hasError;
private final List<ParameterBinding> parameterBindings;
private final Map<String, Accessor> constructorAccessors;

private ConstructorAccessor(
List<ParameterBinding> parameterBindings,
Map<String, Accessor> constructorAccessors) {
this( false, parameterBindings, constructorAccessors );
}

private ConstructorAccessor(boolean hasError, List<ParameterBinding> parameterBindings,
Map<String, Accessor> constructorAccessors) {
this.hasError = hasError;
this.parameterBindings = parameterBindings;
this.constructorAccessors = constructorAccessors;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public enum Message {
PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET("No property named \"%s\" exists in source parameter(s). Please define the source explicitly."),
PROPERTYMAPPING_NO_SUITABLE_COLLECTION_OR_MAP_CONSTRUCTOR( "%s does not have an accessible copy or no-args constructor." ),
PROPERTYMAPPING_EXPRESSION_AND_CONDITION_QUALIFIED_BY_NAME_BOTH_DEFINED( "Expression and condition qualified by name are both defined in @Mapping, either define an expression or a condition qualified by name." ),
PROPERTYMAPPING_TARGET_HAS_NO_TARGET_PROPERTIES( "No target property found for target \"%s\".", Diagnostic.Kind.WARNING ),

CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ),
CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.junit.jupiter.api.extension.RegisterExtension;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.WithProperties;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
Expand Down Expand Up @@ -176,12 +177,12 @@ public void erroneousMapperWithAnnotationOnlyOnClass() {
@Diagnostic(
kind = javax.tools.Diagnostic.Kind.ERROR,
type = ErroneousMapperWithClassOnMethod.class,
line = 17,
line = 18,
message = "Annotation \"CustomClassOnlyAnnotation\" is not allowed on methods."
)
}
)
@WithClasses({ ErroneousMapperWithClassOnMethod.class, CustomClassOnlyAnnotation.class })
@WithClasses({ ErroneousMapperWithClassOnMethod.class, CustomClassOnlyAnnotation.class, WithProperties.class })
public void erroneousMapperWithClassOnMethod() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.mapstruct.AnnotateWith;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.WithProperties;

/**
* @author Ben Zegveld
Expand All @@ -15,13 +16,6 @@
public interface ErroneousMapperWithClassOnMethod {

@AnnotateWith( value = CustomClassOnlyAnnotation.class )
Target toString(Source value);
WithProperties toString(String string);

class Source {

}

class Target {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,29 @@ public interface Issue1111Mapper {

List<List<Target>> listList(List<List<Source>> in);

class Source { }
class Source {
private final String value;

class Target { }
public Source(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

class Target {

private final String value;

public Target(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class Issue1111Test {
@ProcessorTest
public void shouldCompile() {

List<List<Source>> source = Arrays.asList( Arrays.asList( new Source() ) );
List<List<Source>> source = Arrays.asList( Arrays.asList( new Source( "test" ) ) );

List<List<Issue1111Mapper.Target>> target = Issue1111Mapper.INSTANCE.listList( source );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ public void setB(BEntity b) {
}

static class BEntity {

private String id;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}

static class ADto {
Expand All @@ -46,6 +56,7 @@ public void setB(BDto b) {

class BDto {
private final String passedViaConstructor;
private String id;

BDto(String passedViaConstructor) {
this.passedViaConstructor = passedViaConstructor;
Expand All @@ -54,6 +65,14 @@ class BDto {
String getPassedViaConstructor() {
return passedViaConstructor;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}

abstract void mergeA(AEntity source, @MappingTarget ADto target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@

import org.mapstruct.Mapper;
import org.mapstruct.ObjectFactory;
import org.mapstruct.ReportingPolicy;

/**
* Results in an ambiguous factory method error, as there are two methods with matching source types available.
*
* @author Andreas Gudian
*/
@Mapper(uses = TargetFactories.class)
@Mapper(uses = TargetFactories.class, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ErroneousIssue1242MapperMultipleSources {
abstract TargetA toTargetA(SourceA source);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@

import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.ReportingPolicy;

/**
* Test mapper for properly resolving the best fitting factory method
*
* @author Andreas Gudian
*/
@Mapper(uses = TargetFactories.class)
@Mapper(uses = TargetFactories.class, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class Issue1242Mapper {
abstract TargetA toTargetA(SourceA source);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void factoryMethodWithSourceParamIsChosen() {
diagnostics = {
@Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 20,
line = 21,
message = "Ambiguous factory methods found for creating TargetB: " +
"TargetB anotherTargetBCreator(SourceB source), " +
"TargetB TargetFactories.createTargetB(SourceB source, @TargetType Class<TargetB> clazz), " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* @author Andreas Gudian
*/
class TargetB {
protected String value;
private final String passedViaConstructor;

TargetB(String passedViaConstructor) {
Expand All @@ -18,4 +19,12 @@ class TargetB {
String getPassedViaConstructor() {
return passedViaConstructor;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,35 @@ public interface Issue1345Mapper {

class A {

private String value;
private String readOnlyProperty;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getReadOnlyProperty() {
return readOnlyProperty;
}
}

class B {

private String value;
private String property;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getProperty() {
return property;
}
Expand Down
Loading