diff --git a/core-common/src/main/java/org/mapstruct/CollectionMappingStrategy.java b/core-common/src/main/java/org/mapstruct/CollectionMappingStrategy.java
index 8ec484cde..9903406cb 100644
--- a/core-common/src/main/java/org/mapstruct/CollectionMappingStrategy.java
+++ b/core-common/src/main/java/org/mapstruct/CollectionMappingStrategy.java
@@ -19,34 +19,44 @@
package org.mapstruct;
/**
- * Strategy for mapping of collections.
+ * Strategy for propagating the value of collection-typed properties from source to target.
+ *
* @author Sjaak Derksen
*/
public enum CollectionMappingStrategy {
/**
- * MapStruct will consider setter methods as target as way to access the target.
- *
- * Note: If no setter is available a getter will be used under the assumption it has been initialized.
+ * The setter of the target property will be used to propagate the value:
+ * {@code orderDto.setOrderLines( order.getOrderLines )}.
+ *
+ * If no setter is available but a getter method, this will be used, under the assumption it has been initialized:
+ * {@code orderDto.getOrderLines().addAll( order.getOrderLines )}.
*/
- SETTER_ONLY,
+ ACCESSOR_ONLY,
+
/**
- * MapStruct will consider setter methods as preferred way to access the target.
- *
- * If no setter is available, MapStruct will first look for an adder method before resorting to a getter.
+ * If present, the setter of the target property will be used to propagate the value:
+ * {@code orderDto.setOrderLines( order.getOrderLines )}.
+ *
+ * If no setter but and adder method is present, that adder will be invoked for each element of the source
+ * collection: {@code order.addOrderLine( orderLine() )}.
+ *
+ * If neither a setter nor an adder method but a getter for the target property is present, that getter will be
+ * used, assuming it returns an initialized collection: If no setter is available, MapStruct will first look for an
+ * adder method before resorting to a getter.
*/
SETTER_PREFERRED,
+
/**
- * MapStruct will consider adder methods as preferred way to access the target.
- *
- * If no adder is available, MapStruct will first look for a setter method before resorting to a getter.
+ * Identical to {@link #SETTER_PREFERRED}, only that adder methods will be preferred over setter methods, if both
+ * are present for a given collection-typed property.
*/
ADDER_PREFERRED,
+
/**
- * The default option is: {@link CollectionMappingStrategy#SETTER_ONLY}.
- *
- * The default options forces deliberate setting in {@link Mapper#collectionMappingStrategy() }, in order
- * to override a setting in {@link MapperConfig#collectionMappingStrategy() }
+ * If given via {@link Mapper#collectionMappingStrategy()}, causes the setting specified via
+ * {@link MapperConfig#collectionMappingStrategy()} to be applied, if present. Otherwise causes
+ * {@link #ACCESSOR_ONLY} to be applied.
*/
DEFAULT;
}
diff --git a/core-common/src/main/java/org/mapstruct/Mapper.java b/core-common/src/main/java/org/mapstruct/Mapper.java
index b1d164fd1..f6c7b503e 100644
--- a/core-common/src/main/java/org/mapstruct/Mapper.java
+++ b/core-common/src/main/java/org/mapstruct/Mapper.java
@@ -84,10 +84,14 @@ public @interface Mapper {
Class> config() default void.class;
/**
- * When a the target is a collection, look for a suitable adder. If the property is defined as plural, (so
- * getItems(), the adder will assumed to be the singular form: addItem()
+ * The strategy to be applied when propagating the value of collection-typed properties. By default, only JavaBeans
+ * accessor methods (setters or getters) will be used, but it is also possible to invoke a corresponding adder
+ * method for each element of the source collection (e.g. {@code orderDto.addOrderLine()}).
+ *
+ * Any setting given for this attribute will take precedence over {@link MapperConfig#collectionMappingStrategy()},
+ * if present.
*
- * @return true if the adder should be used.
+ * @return The strategy applied when propagating the value of collection-typed properties.
*/
CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.DEFAULT;
diff --git a/core-common/src/main/java/org/mapstruct/MapperConfig.java b/core-common/src/main/java/org/mapstruct/MapperConfig.java
index 98a413a58..a84f52cdc 100644
--- a/core-common/src/main/java/org/mapstruct/MapperConfig.java
+++ b/core-common/src/main/java/org/mapstruct/MapperConfig.java
@@ -74,10 +74,11 @@ public @interface MapperConfig {
String componentModel() default "default";
/**
- * When a the target is a collection, look for a suitable adder. If the property is defined as plural, (so
- * getItems(), the adder will assumed to be the singular form: addItem()
+ * The strategy to be applied when propagating the value of collection-typed properties. By default, only JavaBeans
+ * accessor methods (setters or getters) will be used, but it is also possible to invoke a corresponding adder
+ * method for each element of the source collection (e.g. {@code orderDto.addOrderLine()}).
*
- * @return true if the adder should be used.
+ * @return The strategy applied when propagating the value of collection-typed properties.
*/
CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.DEFAULT;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java
index 32ed1060c..9bce2eac3 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
@@ -35,6 +36,7 @@ import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
+
import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Filters;
import org.mapstruct.ap.util.Nouns;
@@ -280,13 +282,13 @@ public class Type extends ModelElement implements Comparable {
* Tries to find an addMethod in this type for given collection property in this type.
*
* Matching occurs on:
- *
- * - 1. The generic type parameter type of the collection should match the adder method argument
- * - 2. When there are more candidates, property name is made singular (as good as is possible). This routine
+ *
+ * - The generic type parameter type of the collection should match the adder method argument
+ * - When there are more candidates, property name is made singular (as good as is possible). This routine
* looks for a matching add method name.
- * - 3. The singularization rules of Dali are used to make a property name singular. This routine
+ *
- The singularization rules of Dali are used to make a property name singular. This routine
* looks for a matching add method name.
- *
+ *
*
* @param collectionProperty property type (assumed collection) to find the adder method for
* @param pluralPropertyName the property name (assumed plural)
@@ -321,17 +323,9 @@ public class Type extends ModelElement implements Comparable {
return candidates.get( 0 );
}
else {
- // try to match according human rules
for (ExecutableElement candidate : candidates) {
- String adderName = Executables.getElementNameForAdder( candidate );
- if (adderName.equals( Nouns.singularizeHuman( pluralPropertyName ) ) ) {
- return candidate;
- }
- }
- // try to match according dali rules
- for (ExecutableElement candidate : candidates) {
- String adderName = Executables.getElementNameForAdder( candidate );
- if (adderName.equals( Nouns.singularizeDali( pluralPropertyName ) ) ) {
+ String elementName = Executables.getElementNameForAdder( candidate );
+ if (elementName.equals( Nouns.singularize( pluralPropertyName ) ) ) {
return candidate;
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
index ec846c13e..4f59720e4 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
@@ -498,23 +498,23 @@ public class MapperCreationProcessor implements ModelElementProcessor getChildren()} , then the adder name is supposedly named: {@code addChild(Child v)},
- * element name = 'Child'
- *
- * getter = {@code List getBikes()} , then the adder name is supposedly named: {@code addBike(Bike v)},
- * element name = 'Bike'
- *
- * getter = {@code List getGeese()} , then the adder name is supposedly named: {@code addGoose(Goose v)},
- * element name = 'Goose'
- *
- * @param adderMethod
- *
- * @return the element name
+ * Returns the 'element name' to which an adder method applies. If. e.g. an adder method is named
+ * {@code addChild(Child v)}, the element name would be 'Child'.
*/
public static String getElementNameForAdder(ExecutableElement adderMethod) {
if ( isAdderMethod( adderMethod ) ) {
diff --git a/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java b/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java
index 0ead503c0..414bcef22 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java
@@ -18,22 +18,23 @@
*/
package org.mapstruct.ap.util;
+import static org.mapstruct.CollectionMappingStrategy.valueOf;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
+
import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.MapperConfigPrism;
import org.mapstruct.ap.prism.MapperPrism;
-import static org.mapstruct.CollectionMappingStrategy.DEFAULT;
-import static org.mapstruct.CollectionMappingStrategy.SETTER_ONLY;
-import static org.mapstruct.CollectionMappingStrategy.valueOf;
/**
* Class decorating the {@link MapperPrism} with the 'default' configuration.
*
@@ -95,20 +96,20 @@ public class MapperConfig {
public CollectionMappingStrategy getCollectionMappingStrategy() {
CollectionMappingStrategy mapperPolicy = valueOf( mapperPrism.collectionMappingStrategy() );
- if ( !mapperPolicy.equals( DEFAULT ) ) {
+ if ( !mapperPolicy.equals( CollectionMappingStrategy.DEFAULT ) ) {
// it is not the default mapper configuration, so return the mapper configured value
return mapperPolicy;
}
else if ( mapperConfigPrism != null ) {
// try the config mapper configuration
CollectionMappingStrategy configPolicy = valueOf( mapperConfigPrism.collectionMappingStrategy() );
- if ( !configPolicy.equals( DEFAULT ) ) {
+ if ( !configPolicy.equals( CollectionMappingStrategy.DEFAULT ) ) {
// its not the default configuration, so return the mapper config configured value
return configPolicy;
}
}
- // when nothing specified, return SETTER_ONLY (default option)
- return SETTER_ONLY;
+ // when nothing specified, return ACCESSOR_ONLY (default option)
+ return CollectionMappingStrategy.ACCESSOR_ONLY;
}
public String componentModel() {
diff --git a/processor/src/main/java/org/mapstruct/ap/util/Nouns.java b/processor/src/main/java/org/mapstruct/ap/util/Nouns.java
index 5fceb9fe6..e68322eab 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/Nouns.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/Nouns.java
@@ -32,7 +32,7 @@ public class Nouns {
private Nouns() { }
- private static final List SINGULAR_HUMAN_RULES = Arrays.asList(
+ private static final List SINGULAR_RULES = Arrays.asList(
new ReplaceRule( "(equipment|information|rice|money|species|series|fish|sheep)$", "$1" ),
new ReplaceRule( "(f)eet$", "$1oot" ),
new ReplaceRule( "(t)eeth$", "$1ooth" ),
@@ -74,48 +74,36 @@ public class Nouns {
new ReplaceRule( "s$", "" )
);
+ /**
+ * Replacement rules based on the routine applied by the Dali
+ * project. Applied as a fallback if the other rules didn't yield a match.
+ */
private static final List SINGULAR_DALI_RULES = Arrays.asList(
new ReplaceRule( "(us|ss)$", "$1" ),
new ReplaceRule( "(ch|s)es$", "$1" ),
- new ReplaceRule( "([^aeiouy])ies$", "$1y" ),
- new ReplaceRule( "s$", "" )
+ new ReplaceRule( "([^aeiouy])ies$", "$1y" )
);
- /**
- * Converts given in into a singular form as much as possible according human form. This will always be a best
- * attempt. The rules are language context dependent and
- *
- * @param in String to singularize
- * @return singularize form of in
+ /**
+ * Converts given pluralized noun into the singular form. If no singular form could be determined, the given word
+ * itself is returned.
*/
- public static String singularizeHuman( String in ) {
- for ( ReplaceRule replaceRule : SINGULAR_HUMAN_RULES ) {
- String match = replaceRule.apply( in );
+ public static String singularize( String plural ) {
+ for ( ReplaceRule replaceRule : SINGULAR_RULES ) {
+ String match = replaceRule.apply( plural );
if ( match != null ) {
return match;
}
}
- return in;
- }
- /**
- * Converts given in into a singular form according dali
- * @see rules
- *
- * These rules are assumed to be incomplete and give wrong conversions from plural to singular that should
- * be taken into account as well.
- *
- * @param in String to singularize
- * @return singularize form of in
- */
- public static String singularizeDali( String in ) {
for ( ReplaceRule replaceRule : SINGULAR_DALI_RULES ) {
- String match = replaceRule.apply( in );
+ String match = replaceRule.apply( plural );
if ( match != null ) {
return match;
}
}
- return in;
+
+ return plural;
}
private static final class ReplaceRule {
@@ -124,7 +112,7 @@ public class Nouns {
private final String replacement;
private final Pattern pattern;
- private ReplaceRule( String regexp, String replacement ) {
+ private ReplaceRule( String regexp, String replacement ) {
this.regexp = regexp;
this.replacement = replacement;
this.pattern = Pattern.compile( this.regexp, Pattern.CASE_INSENSITIVE );
@@ -139,5 +127,9 @@ public class Nouns {
return result;
}
+ @Override
+ public String toString() {
+ return "'" + regexp + "' -> '" + replacement;
+ }
}
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/CatException.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/CatException.java
index fb84834ba..a6174a6cf 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/CatException.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/CatException.java
@@ -19,15 +19,16 @@
package org.mapstruct.ap.test.collection.adder;
/**
- *
* @author Sjaak Derksen
*/
public class CatException extends Exception {
+ private static final long serialVersionUID = 1L;
+
public CatException() {
}
- public CatException( String msg ) {
+ public CatException(String msg) {
super( msg );
}
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/DogException.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/DogException.java
index 22c068cf6..a501c6008 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/DogException.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/DogException.java
@@ -19,15 +19,16 @@
package org.mapstruct.ap.test.collection.adder;
/**
- *
* @author Sjaak Derksen
*/
public class DogException extends Exception {
+ private static final long serialVersionUID = 1L;
+
public DogException() {
}
- public DogException( String msg ) {
+ public DogException(String msg) {
super( msg );
}
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/target/IndoorPet.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/target/IndoorPet.java
index cde9b2ad7..0cd413a33 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/target/IndoorPet.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/target/IndoorPet.java
@@ -37,7 +37,4 @@ public class IndoorPet extends Pet {
public void setValue( Long value ) {
this.value = value;
}
-
-
-
}