diff --git a/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java b/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java
index afaba9737..0ea3ee7df 100644
--- a/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java
+++ b/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java
@@ -7,6 +7,54 @@ package org.mapstruct;
/**
* Strategy for propagating the value of collection-typed properties from source to target.
+ *
+ * In the table below, the dash {@code -} indicates a property name.
+ * Next, the trailing {@code s} indicates the plural form.
+ * The table explains the options and how they are applied to the presence / absence of a
+ * {@code set-s}, {@code add-} and / or {@code get-s} method on the target object.
+ *
+ * Collection mapping strategy options
+ *
+ * Option |
+ * Only target set-s Available |
+ * Only target add- Available |
+ * Both set-s/add- Available |
+ * No set-s/add- Available |
+ * Existing Target ({@code @TargetType}) |
+ *
+ *
+ * {@link #ACCESSOR_ONLY} |
+ * set-s |
+ * get-s |
+ * set-s |
+ * get-s |
+ * get-s |
+ *
+ *
+ * {@link #SETTER_PREFERRED} |
+ * set-s |
+ * add- |
+ * set-s |
+ * get-s |
+ * get-s |
+ *
+ *
+ * {@link #ADDER_PREFERRED} |
+ * set-s |
+ * add- |
+ * add- |
+ * get-s |
+ * get-s |
+ *
+ *
+ * {@link #TARGET_IMMUTABLE} |
+ * set-s |
+ * exception |
+ * set-s |
+ * exception |
+ * set-s |
+ *
+ *
*
* @author Sjaak Derksen
*/
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
index 6d86aad45..689490626 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
@@ -788,6 +788,13 @@ public class Type extends ModelElement implements Comparable {
// an adder has been found (according strategy) so overrule current choice.
candidate = adderMethod;
}
+
+ if ( cmStrategy == CollectionMappingStrategyGem.TARGET_IMMUTABLE
+ && candidate.getAccessorType() == AccessorType.GETTER ) {
+ // If the collection mapping strategy is target immutable
+ // then the getter method cannot be used as a setter
+ continue;
+ }
}
else if ( candidate.getAccessorType() == AccessorType.FIELD && ( Executables.isFinal( candidate ) ||
result.containsKey( targetPropertyName ) ) ) {
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Mapper.java
new file mode 100644
index 000000000..885d74448
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Mapper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.bugs._2952;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.mapstruct.CollectionMappingStrategy;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Filip Hrisafov
+ */
+@Mapper(collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE)
+public interface Issue2952Mapper {
+
+ Issue2952Mapper INSTANCE = Mappers.getMapper( Issue2952Mapper.class );
+
+ Target map(Source source);
+
+ class Source {
+ private final String value;
+
+ public Source(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ class Target {
+ private final Map attributes = new HashMap<>();
+ private final List values = new ArrayList<>();
+ private String value;
+
+ public Map getAttributes() {
+ return Collections.unmodifiableMap( attributes );
+ }
+
+ public List getValues() {
+ return Collections.unmodifiableList( values );
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Test.java
new file mode 100644
index 000000000..7a517ec97
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Test.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.bugs._2952;
+
+import org.mapstruct.ap.testutil.IssueKey;
+import org.mapstruct.ap.testutil.ProcessorTest;
+import org.mapstruct.ap.testutil.WithClasses;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Filip Hrisafov
+ */
+@IssueKey("2952")
+@WithClasses({
+ Issue2952Mapper.class
+})
+class Issue2952Test {
+
+ @ProcessorTest
+ void shouldCorrectIgnoreImmutableIterable() {
+ Issue2952Mapper.Target target = Issue2952Mapper.INSTANCE.map( new Issue2952Mapper.Source( "test" ) );
+
+ assertThat( target.getValue() ).isEqualTo( "test" );
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java
index bbf3a6d24..1f2f2f00b 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java
@@ -5,6 +5,7 @@
*/
package org.mapstruct.ap.test.collection.immutabletarget;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -13,7 +14,7 @@ import java.util.List;
*/
public class CupboardEntityOnlyGetter {
- private List content;
+ private List content = new ArrayList<>();
public List getContent() {
return content;
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ErroneousCupboardMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardNoSetterMapper.java
similarity index 80%
rename from processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ErroneousCupboardMapper.java
rename to processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardNoSetterMapper.java
index 028ac4d76..a6674e629 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ErroneousCupboardMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardNoSetterMapper.java
@@ -15,9 +15,9 @@ import org.mapstruct.factory.Mappers;
* @author Sjaak Derksen
*/
@Mapper( collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE )
-public interface ErroneousCupboardMapper {
+public interface CupboardNoSetterMapper {
- ErroneousCupboardMapper INSTANCE = Mappers.getMapper( ErroneousCupboardMapper.class );
+ CupboardNoSetterMapper INSTANCE = Mappers.getMapper( CupboardNoSetterMapper.class );
void map( CupboardDto in, @MappingTarget CupboardEntityOnlyGetter out );
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java
index b3d2096bc..de2243629 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java
@@ -11,9 +11,6 @@ import java.util.Collections;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
-import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
-import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
-import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,19 +38,17 @@ public class ImmutableProductTest {
@ProcessorTest
@WithClasses({
- ErroneousCupboardMapper.class,
+ CupboardNoSetterMapper.class,
CupboardEntityOnlyGetter.class
})
- @ExpectedCompilationOutcome(
- value = CompilationResult.FAILED,
- diagnostics = {
- @Diagnostic(type = ErroneousCupboardMapper.class,
- kind = javax.tools.Diagnostic.Kind.ERROR,
- line = 22,
- message = "No write accessor found for property \"content\" in target type.")
- }
- )
- public void testShouldFailOnPropertyMappingNoPropertySetterOnlyGetter() {
+ public void shouldIgnoreImmutableTarget() {
+ CupboardDto in = new CupboardDto();
+ in.setContent( Arrays.asList( "flour", "peas" ) );
+ CupboardEntityOnlyGetter out = new CupboardEntityOnlyGetter();
+ out.getContent().add( "bread" );
+ CupboardNoSetterMapper.INSTANCE.map( in, out );
+
+ assertThat( out.getContent() ).containsExactly( "bread" );
}
}