#207 JavaDoc improvements; Joining singularization methods

This commit is contained in:
Gunnar Morling 2014-06-22 12:22:18 +02:00
parent acfca6235d
commit 160bdb2e86
11 changed files with 86 additions and 100 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 ) ) {

View File

@ -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() {

View File

@ -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;
}
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -37,7 +37,4 @@ public class IndoorPet extends Pet {
public void setValue( Long value ) {
this.value = value;
}
}