mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#207 JavaDoc improvements; Joining singularization methods
This commit is contained in:
parent
acfca6235d
commit
160bdb2e86
@ -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 )}.
|
||||
* <p>
|
||||
* 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 )}.
|
||||
* <p>
|
||||
* 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() )}.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
@ -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()}).
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Type> {
|
||||
* Tries to find an addMethod in this type for given collection property in this type.
|
||||
*
|
||||
* Matching occurs on:
|
||||
* <ul>
|
||||
* <li>1. The generic type parameter type of the collection should match the adder method argument</li>
|
||||
* <li>2. When there are more candidates, property name is made singular (as good as is possible). This routine
|
||||
* <ol>
|
||||
* <li>The generic type parameter type of the collection should match the adder method argument</li>
|
||||
* <li>When there are more candidates, property name is made singular (as good as is possible). This routine
|
||||
* looks for a matching add method name.</li>
|
||||
* <li>3. The singularization rules of Dali are used to make a property name singular. This routine
|
||||
* <li>The singularization rules of Dali are used to make a property name singular. This routine
|
||||
* looks for a matching add method name.</li>
|
||||
* </ul>
|
||||
* </ol>
|
||||
*
|
||||
* @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<Type> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -498,23 +498,23 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
|
||||
|
||||
// first check if there's a setter method.
|
||||
ExecutableElement adderAccessor = null;
|
||||
ExecutableElement adderMethod = null;
|
||||
if ( Executables.isSetterMethod( targetAccessor ) ) {
|
||||
Type targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
|
||||
// ok, the current accessor is a setter. So now the strategy determines what to use
|
||||
if ( cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
|
||||
adderAccessor = method.getResultType().getAdderForType( targetType, targetPropertyName );
|
||||
adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
|
||||
}
|
||||
}
|
||||
else if ( Executables.isGetterMethod( targetAccessor ) ) {
|
||||
// the current accessor is a getter (no setter available). But still, an add method is according
|
||||
// to the above strategy (SETTER_PREFERRED || ADDER_PREFERRED) preferred over the getter.
|
||||
Type targetType = typeFactory.getReturnType( targetAccessor );
|
||||
adderAccessor = method.getResultType().getAdderForType( targetType, targetPropertyName );
|
||||
adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
|
||||
}
|
||||
if ( adderAccessor != null ) {
|
||||
if ( adderMethod != null ) {
|
||||
// an adder has been found (according strategy) so overrule current choice.
|
||||
targetAccessor = adderAccessor;
|
||||
targetAccessor = adderMethod;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,23 +106,8 @@ public class Executables {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'element name' to which an adder method applies.
|
||||
*
|
||||
* If an collection getter / setter are defined by a plural name of the element they apply to, then this
|
||||
* method gives the supposedly (singular) element name of the collection.
|
||||
* for example:
|
||||
* getter = {@code List<Child> getChildren()} , then the adder name is supposedly named: {@code addChild(Child v)},
|
||||
* element name = 'Child'
|
||||
*
|
||||
* getter = {@code List<Bike> getBikes()} , then the adder name is supposedly named: {@code addBike(Bike v)},
|
||||
* element name = 'Bike'
|
||||
*
|
||||
* getter = {@code List<Goose> 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 ) ) {
|
||||
|
@ -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() {
|
||||
|
@ -32,7 +32,7 @@ public class Nouns {
|
||||
|
||||
private Nouns() { }
|
||||
|
||||
private static final List<ReplaceRule> SINGULAR_HUMAN_RULES = Arrays.asList(
|
||||
private static final List<ReplaceRule> 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 <a href="http://www.eclipse.org/webtools/dali/">Dali</a>
|
||||
* project. Applied as a fallback if the other rules didn't yield a match.
|
||||
*/
|
||||
private static final List<ReplaceRule> 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 <a href="http://www.eclipse.org/webtools/dali/"></a> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,4 @@ public class IndoorPet extends Pet {
|
||||
public void setValue( Long value ) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user