#207 adder method support for target collections and unit test

This commit is contained in:
sjaakd 2014-06-19 21:42:02 +02:00
parent 6d682c5118
commit acfca6235d
35 changed files with 1751 additions and 104 deletions

View File

@ -0,0 +1,52 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct;
/**
* Strategy for mapping of collections.
* @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.
*/
SETTER_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.
*/
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.
*/
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() }
*/
DEFAULT;
}

View File

@ -83,4 +83,12 @@ 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()
*
* @return true if the adder should be used.
*/
CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.DEFAULT;
}

View File

@ -72,4 +72,12 @@ public @interface MapperConfig {
* @return The component model for the generated mapper.
*/
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()
*
* @return true if the adder should be used.
*/
CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.DEFAULT;
}

View File

@ -76,4 +76,5 @@ public @interface Mapping {
* @return A constant {@code String} expression specifying the value for the designated target prperty
*/
String expression() default "";
}

View File

@ -74,4 +74,5 @@ public @interface Mapping {
* @return A constant {@code String} expression specifying the value for the designated target prperty
*/
String expression() default "";
}

View File

@ -0,0 +1,75 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.model.assignment;
import java.util.ArrayList;
import java.util.List;
import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.Type;
/**
* Wraps the assignment in a target setter.
*
* @author Sjaak Derksen
*/
public class AdderWrapper extends AssignmentWrapper {
private final List<Type> exceptionTypesToExclude;
private final String sourceReference;
private final Type sourceType;
public AdderWrapper(
Assignment decoratedAssignment,
List<Type> exceptionTypesToExclude,
String sourceReference,
Type sourceType) {
super( decoratedAssignment );
this.exceptionTypesToExclude = exceptionTypesToExclude;
this.sourceReference = sourceReference;
this.sourceType = sourceType;
}
@Override
public List<Type> getExceptionTypes() {
List<Type> parentExceptionTypes = super.getExceptionTypes();
List<Type> result = new ArrayList<Type>( parentExceptionTypes );
for ( Type exceptionTypeToExclude : exceptionTypesToExclude ) {
for ( Type parentExceptionType : parentExceptionTypes ) {
if ( parentExceptionType.isAssignableTo( exceptionTypeToExclude ) ) {
result.remove( parentExceptionType );
}
}
}
return result;
}
public Type getSourceType() {
return sourceType;
}
@Override
public String getSourceReference() {
return sourceReference;
}
public String getIteratorReference() {
return getAssignment().getSourceReference();
}
}

View File

@ -24,7 +24,6 @@ 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;
@ -32,12 +31,13 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
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;
import org.mapstruct.ap.util.TypeUtilsJDK6Fix;
/**
@ -75,6 +75,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private List<ExecutableElement> getters = null;
private List<ExecutableElement> setters = null;
private List<ExecutableElement> adders = null;
private List<ExecutableElement> alternativeTargetAccessors = null;
//CHECKSTYLE:OFF
@ -275,6 +276,70 @@ public class Type extends ModelElement implements Comparable<Type> {
return getters;
}
/**
* 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
* 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
* looks for a matching add method name.</li>
* </ul>
*
* @param collectionProperty property type (assumed collection) to find the adder method for
* @param pluralPropertyName the property name (assumed plural)
*
* @return corresponding adder method for getter when present
*/
public ExecutableElement getAdderForType( Type collectionProperty, String pluralPropertyName ) {
List<ExecutableElement> candidates = new ArrayList<ExecutableElement>();
if ( collectionProperty.isCollectionType ) {
// this is a collection, so this can be done always
if ( !collectionProperty.getTypeParameters().isEmpty() ) {
// there's only one type arg to a collection
TypeMirror typeArg = collectionProperty.getTypeParameters().get( 0 ).getTypeMirror();
// now, look for a method that
// 1) starts with add,
// 2) and has typeArg as one and only arg
List<ExecutableElement> adderList = getAdders();
for ( ExecutableElement adder : adderList ) {
VariableElement arg = adder.getParameters().get( 0 );
if ( arg.asType().equals( typeArg ) ) {
candidates.add( adder );
}
}
}
}
if (candidates.isEmpty()) {
return null;
}
else if (candidates.size() == 1) {
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 ) ) ) {
return candidate;
}
}
}
return null;
}
/**
* getSetters
*
@ -292,6 +357,24 @@ public class Type extends ModelElement implements Comparable<Type> {
* Alternative accessors could be a getter for a collection / map. By means of the
* {@link Collection#addAll(Collection) } or {@link Map#putAll(Map)} this getter can still be used as
* targetAccessor. JAXB XJC tool generates such constructs. This method can be extended when new cases come along.
* getAdders
*
* @return an unmodifiable list of all adders
*/
private List<ExecutableElement> getAdders() {
if ( adders == null ) {
List<? extends Element> members = elementUtils.getAllMembers( typeElement );
adders = Collections.unmodifiableList( Filters.adderMethodsIn( members ) );
}
return adders;
}
/**
* Alternative accessors could be a getter for a collection. By means of the
* {@link java.util.Collection#addAll(java.util.Collection) } this getter can still
* be used as targetAccessor. JAXB XJC tool generates such constructs.
*
* This method can be extended when new cases come along.
*
* @return an unmodifiable list of alternative target accessors.
*/

View File

@ -190,6 +190,7 @@ public class Mapping {
return targetAnnotationValue;
}
public Mapping reverse() {
Mapping reverse = null;
if ( expression != null ) {

View File

@ -35,6 +35,8 @@ import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.ap.model.Assignment;
import static org.mapstruct.ap.model.Assignment.AssignmentType.DIRECT;
import static org.mapstruct.ap.model.Assignment.AssignmentType.TYPE_CONVERTED;
@ -50,6 +52,7 @@ import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.model.MappingMethod;
import org.mapstruct.ap.model.PropertyMapping;
import org.mapstruct.ap.model.assignment.AdderWrapper;
import org.mapstruct.ap.model.assignment.AssignmentFactory;
import org.mapstruct.ap.model.assignment.GetterCollectionOrMapWrapper;
import org.mapstruct.ap.model.assignment.LocalVarWrapper;
@ -81,7 +84,7 @@ import org.mapstruct.ap.util.Strings;
*/
public class MapperCreationProcessor implements ModelElementProcessor<List<SourceMethod>, Mapper> {
private enum TargetAccessorType { GETTER, SETTER };
private enum TargetAccessorType { GETTER, SETTER, ADDER };
private Elements elementUtils;
private Types typeUtils;
@ -108,9 +111,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
private Mapper getMapper(TypeElement element, List<SourceMethod> methods) {
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
List<MapperReference> mapperReferences = getReferencedMappers( element );
List<MappingMethod> mappingMethods = getMappingMethods( mapperReferences, methods, unmappedTargetPolicy );
List<MappingMethod> mappingMethods = getMappingMethods( mapperReferences, methods, element );
mappingMethods.addAll( mappingResolver.getVirtualMethodsToGenerate() );
Mapper mapper = new Mapper.Builder()
@ -151,6 +153,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
}
private CollectionMappingStrategy getEffectiveCollectionMappingStrategy(TypeElement element) {
MapperConfig mapperSettings = MapperConfig.getInstanceOn( element );
return mapperSettings.getCollectionMappingStrategy();
}
private Decorator getDecorator(TypeElement element, List<SourceMethod> methods) {
DecoratedWithPrism decoratorPrism = DecoratedWithPrism.getInstanceOn( element );
@ -248,7 +255,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
private List<MappingMethod> getMappingMethods(List<MapperReference> mapperReferences, List<SourceMethod> methods,
ReportingPolicy unmappedTargetPolicy) {
TypeElement element ) {
List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>();
for ( SourceMethod method : methods ) {
@ -301,7 +308,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
mapperReferences,
methods,
method,
unmappedTargetPolicy
element
);
if ( beanMappingMethod != null ) {
hasFactoryMethod = beanMappingMethod.getFactoryMethod() != null;
@ -386,9 +393,9 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
private PropertyMapping getPropertyMapping(List<MapperReference> mapperReferences,
List<SourceMethod> methods,
SourceMethod method,
ExecutableElement targetAcessor,
ExecutableElement targetAccessor,
String targetPropertyName,
Parameter parameter) {
String targetPropertyName = Executables.getPropertyName( targetAcessor );
// check if there's a mapping defined
Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
@ -415,7 +422,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
methods,
method,
sourceConstant,
targetAcessor,
targetAccessor,
dateFormat
);
}
@ -435,7 +442,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
method,
parameter,
sourceAccessor,
targetAcessor,
targetAccessor,
targetPropertyName,
dateFormat
);
}
@ -448,7 +456,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
method,
parameter,
sourceAccessor,
targetAcessor,
targetAccessor,
targetPropertyName,
dateFormat
);
}
@ -457,7 +466,12 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
private BeanMappingMethod getBeanMappingMethod(List<MapperReference> mapperReferences, List<SourceMethod> methods,
SourceMethod method, ReportingPolicy unmappedTargetPolicy) {
SourceMethod method, TypeElement element) {
// fetch settings from element to implement
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy( element );
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
Set<String> mappedTargetProperties = new HashSet<String>();
@ -476,10 +490,44 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
// A target access is in general a setter method on the target object. However, in case of collections,
// the current target accessor can also be a getter method.
//
// The following if block, checks if the target accessor should be overruled by an add method.
if ( cmStrategy.equals( CollectionMappingStrategy.SETTER_PREFERRED ) ||
cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
// first check if there's a setter method.
ExecutableElement adderAccessor = 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 );
}
}
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 );
}
if ( adderAccessor != null ) {
// an adder has been found (according strategy) so overrule current choice.
targetAccessor = adderAccessor;
}
}
PropertyMapping propertyMapping = null;
if ( mapping != null && mapping.getSourceParameterName() != null ) {
Parameter parameter = method.getSourceParameter( mapping.getSourceParameterName() );
propertyMapping = getPropertyMapping( mapperReferences, methods, method, targetAccessor, parameter );
propertyMapping = getPropertyMapping(
mapperReferences,
methods,
method,
targetAccessor,
targetPropertyName,
parameter );
}
if ( propertyMapping == null ) {
@ -489,6 +537,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
methods,
method,
targetAccessor,
targetPropertyName,
sourceParameter
);
if ( propertyMapping != null && newPropertyMapping != null ) {
@ -657,24 +706,36 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
SourceMethod method,
Parameter parameter,
ExecutableElement sourceAccessor,
ExecutableElement targetAcessor,
ExecutableElement targetAccessor,
String targetPropertyName,
String dateFormat) {
TargetAccessorType targetAccessorType = TargetAccessorType.SETTER;
Type sourceType = typeFactory.getReturnType( sourceAccessor );
Type sourceType;
Type targetType;
TargetAccessorType targetAccessorType;
String sourceReference = parameter.getName() + "." + sourceAccessor.getSimpleName().toString() + "()";
if ( Executables.isSetterMethod( targetAcessor ) ) {
targetType = typeFactory.getSingleParameter( targetAcessor ).getType();
String iteratorReference = null;
if ( Executables.isSetterMethod( targetAccessor ) ) {
sourceType = typeFactory.getReturnType( sourceAccessor );
targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
targetAccessorType = TargetAccessorType.SETTER;
}
else { // must be getter
targetType = typeFactory.getReturnType( targetAcessor );
else if ( Executables.isAdderMethod( targetAccessor ) ) {
sourceType = typeFactory.getReturnType( sourceAccessor );
if ( sourceType.isCollectionType() && !sourceType.getTypeParameters().isEmpty() ) {
sourceType = sourceType.getTypeParameters().get( 0 );
iteratorReference = Executables.getElementNameForAdder( targetAccessor );
}
targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
targetAccessorType = TargetAccessorType.ADDER;
}
else {
sourceType = typeFactory.getReturnType( sourceAccessor );
targetType = typeFactory.getReturnType( targetAccessor );
targetAccessorType = TargetAccessorType.GETTER;
}
String targetPropertyName = Executables.getPropertyName( targetAcessor );
String mappedElement = "property '" + Executables.getPropertyName( sourceAccessor ) + "'";
String sourcePropertyName = Executables.getPropertyName( sourceAccessor );
String mappedElement = "property '" + sourcePropertyName + "'";
Assignment assignment = mappingResolver.getTargetAssignment(
method,
@ -685,7 +746,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
targetType,
targetPropertyName,
dateFormat,
sourceReference
iteratorReference != null ? iteratorReference : sourceReference
);
if ( assignment != null ) {
@ -703,19 +764,15 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
// wrap the setter in the collection / map initializers
switch ( targetAccessorType ) {
case GETTER:
// target accessor is getter, so decorate assignment as getter
assignment = new GetterCollectionOrMapWrapper( assignment,
targetAcessor.getSimpleName().toString() );
break;
default: // setter
assignment = new SetterCollectionOrMapWrapper( assignment,
targetAcessor.getSimpleName().toString() );
break;
if ( targetAccessorType == TargetAccessorType.SETTER ) {
// target accessor is setter, so decorate assignment as setter
assignment = new SetterCollectionOrMapWrapper( assignment,
targetAccessor.getSimpleName().toString() );
}
else {
// target accessor is getter, so decorate assignment as getter
assignment = new GetterCollectionOrMapWrapper( assignment,
targetAccessor.getSimpleName().toString() );
}
// For collections and maps include a null check, when the assignment type is DIRECT.
@ -728,11 +785,17 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
else {
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
if ( !sourceType.isPrimitive() && ( assignment.getType() == TYPE_CONVERTED ) ) {
if ( targetAccessorType == TargetAccessorType.SETTER ) {
assignment = new SetterWrapper( assignment, method.getThrownTypes() );
if ( !sourceType.isPrimitive() && ( assignment.getType() == TYPE_CONVERTED ) ) {
// for primitive types null check is not possible at all, but a conversion needs
// a null check.
assignment = new NullCheckWrapper( assignment );
// a null check.
assignment = new NullCheckWrapper( assignment );
}
}
else {
// adder, so wrap as adder
assignment = new AdderWrapper( assignment, method.getThrownTypes(), sourceReference, sourceType );
}
}
}
@ -742,16 +805,16 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
String.format(
"Can't map property \"%s %s\" to \"%s %s\".",
sourceType,
Executables.getPropertyName( sourceAccessor ),
sourcePropertyName,
targetType,
Executables.getPropertyName( targetAcessor )
targetPropertyName
),
method.getExecutable()
);
}
return new PropertyMapping(
parameter.getName(),
targetAcessor.getSimpleName().toString(),
targetAccessor.getSimpleName().toString(),
targetType,
assignment
);

View File

@ -70,6 +70,17 @@ public class Executables {
return false;
}
public static boolean isAdderMethod(ExecutableElement method) {
String name = method.getSimpleName().toString();
if ( isPublic( method ) && name.startsWith( "add" ) && name.length() > 3 && method.getParameters()
.size() == 1 && method.getReturnType().getKind() == TypeKind.VOID ) {
return true;
}
return false;
}
private static boolean isPublic(ExecutableElement method) {
return method.getModifiers().contains( Modifier.PUBLIC );
}
@ -94,6 +105,35 @@ public class Executables {
throw new IllegalArgumentException( "Executable " + getterOrSetterMethod + " is not getter or setter method." );
}
/**
* 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
*/
public static String getElementNameForAdder(ExecutableElement adderMethod) {
if ( isAdderMethod( adderMethod ) ) {
return Introspector.decapitalize(
adderMethod.getSimpleName().toString().substring( 3 )
);
}
throw new IllegalArgumentException( "Executable " + adderMethod + " is not an adder method." );
}
public static Set<String> getPropertyNames(List<ExecutableElement> propertyAccessors) {
Set<String> propertyNames = new HashSet<String>();

View File

@ -58,4 +58,16 @@ public class Filters {
}
return setterMethods;
}
public static List<ExecutableElement> adderMethodsIn(Iterable<? extends Element> elements) {
List<ExecutableElement> adderMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) {
if ( Executables.isAdderMethod( method ) ) {
adderMethods.add( method );
}
}
return adderMethods;
}
}

View File

@ -27,10 +27,13 @@ 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.
*
@ -89,6 +92,25 @@ public class MapperConfig {
}
}
public CollectionMappingStrategy getCollectionMappingStrategy() {
CollectionMappingStrategy mapperPolicy = valueOf( mapperPrism.collectionMappingStrategy() );
if ( !mapperPolicy.equals( 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 ) ) {
// 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;
}
public String componentModel() {
if ( !mapperPrism.componentModel().equals( "default" ) ) {
return mapperPrism.componentModel();

View File

@ -0,0 +1,143 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.util;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Class taking care of Noun manipulation
*
* @author Sjaak Derksen
*/
public class Nouns {
private Nouns() { }
private static final List<ReplaceRule> SINGULAR_HUMAN_RULES = Arrays.asList(
new ReplaceRule( "(equipment|information|rice|money|species|series|fish|sheep)$", "$1" ),
new ReplaceRule( "(f)eet$", "$1oot" ),
new ReplaceRule( "(t)eeth$", "$1ooth" ),
new ReplaceRule( "(g)eese$", "$1oose" ),
new ReplaceRule( "(s)tadiums$", "$1tadium" ),
new ReplaceRule( "(m)oves$", "$1ove" ),
new ReplaceRule( "(s)exes$", "$1ex" ),
new ReplaceRule( "(c)hildren$", "$1hild" ),
new ReplaceRule( "(m)en$", "$1an" ),
new ReplaceRule( "(p)eople$", "$1erson" ),
new ReplaceRule( "(quiz)zes$", "$1" ),
new ReplaceRule( "(matr)ices$", "$1ix" ),
new ReplaceRule( "(vert|ind)ices$", "$1ex" ),
new ReplaceRule( "^(ox)en", "$1" ),
new ReplaceRule( "(alias|status)$", "$1" ), // already singular, but ends in 's'
new ReplaceRule( "(alias|status)es$", "$1" ),
new ReplaceRule( "(octop|vir)us$", "$1us" ), // already singular, but ends in 's'
new ReplaceRule( "(octop|vir)i$", "$1us" ),
new ReplaceRule( "(cris|ax|test)es$", "$1is" ),
new ReplaceRule( "(cris|ax|test)is$", "$1is" ), // already singular, but ends in 's'
new ReplaceRule( "(shoe)s$", "$1" ),
new ReplaceRule( "(o)es$", "$1" ),
new ReplaceRule( "(bus)es$", "$1" ),
new ReplaceRule( "([m|l])ice$", "$1ouse" ),
new ReplaceRule( "(x|ch|ss|sh)es$", "$1" ),
new ReplaceRule( "(m)ovies$", "$1ovie" ),
new ReplaceRule( "(s)eries$", "$1eries" ),
new ReplaceRule( "([^aeiouy]|qu)ies$", "$1y" ),
new ReplaceRule( "([lr])ves$", "$1f" ),
new ReplaceRule( "(tive)s$", "$1" ),
new ReplaceRule( "(hive)s$", "$1" ),
new ReplaceRule( "([^f])ves$", "$1fe" ),
new ReplaceRule( "(^analy)sis$", "$1sis" ), // already singular, but ends in 's'
new ReplaceRule( "(^analy)ses$", "$1sis" ),
new ReplaceRule( "((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis" ),
new ReplaceRule( "([ti])a$", "$1um" ),
new ReplaceRule( "(n)ews$", "$1ews" ),
new ReplaceRule( "(s|si|u)s$", "$1s" ), // '-us' and '-ss' are already singular
new ReplaceRule( "s$", "" )
);
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$", "" )
);
/**
* 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
*/
public static String singularizeHuman( String in ) {
for ( ReplaceRule replaceRule : SINGULAR_HUMAN_RULES ) {
String match = replaceRule.apply( in );
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 );
if ( match != null ) {
return match;
}
}
return in;
}
private static final class ReplaceRule {
private final String regexp;
private final String replacement;
private final Pattern pattern;
private ReplaceRule( String regexp, String replacement ) {
this.regexp = regexp;
this.replacement = replacement;
this.pattern = Pattern.compile( this.regexp, Pattern.CASE_INSENSITIVE );
}
private String apply( String input ) {
String result = null;
Matcher matcher = this.pattern.matcher( input );
if ( matcher.find() ) {
result = matcher.replaceAll( this.replacement );
}
return result;
}
}
}

View File

@ -33,70 +33,70 @@ import static org.mapstruct.ap.util.Collections.asSet;
public class Strings {
private static final Set<String> KEYWORDS = asSet(
"abstract",
"continue",
"for",
"new",
"switch",
"assert",
"default",
"goto",
"package",
"synchronized",
"boolean",
"do",
"if",
"private",
"this",
"break",
"double",
"implements",
"protected",
"throw",
"byte",
"else",
"import",
"public",
"throws",
"case",
"enum",
"instanceof",
"return",
"transient",
"catch",
"extends",
"int",
"short",
"try",
"char",
"final",
"interface",
"static",
"void",
"class",
"finally",
"long",
"strictfp",
"volatile",
"const",
"float",
"native",
"super",
"while"
"abstract",
"continue",
"for",
"new",
"switch",
"assert",
"default",
"goto",
"package",
"synchronized",
"boolean",
"do",
"if",
"private",
"this",
"break",
"double",
"implements",
"protected",
"throw",
"byte",
"else",
"import",
"public",
"throws",
"case",
"enum",
"instanceof",
"return",
"transient",
"catch",
"extends",
"int",
"short",
"try",
"char",
"final",
"interface",
"static",
"void",
"class",
"finally",
"long",
"strictfp",
"volatile",
"const",
"float",
"native",
"super",
"while"
);
private Strings() {
}
public static String capitalize(String string) {
public static String capitalize( String string ) {
return string == null ? null : string.substring( 0, 1 ).toUpperCase() + string.substring( 1 );
}
public static String decapitalize(String string) {
public static String decapitalize( String string ) {
return string == null ? null : string.substring( 0, 1 ).toLowerCase() + string.substring( 1 );
}
public static String join(Iterable<?> iterable, String separator) {
public static String join( Iterable<?> iterable, String separator ) {
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
@ -114,11 +114,11 @@ public class Strings {
return sb.toString();
}
public static boolean isEmpty(String string) {
public static boolean isEmpty( String string ) {
return string == null || string.isEmpty();
}
public static String getSaveVariableName(String name, String... existingVariableNames) {
public static String getSaveVariableName( String name, String... existingVariableNames ) {
return getSaveVariableName( name, Arrays.asList( existingVariableNames ) );
}
@ -130,9 +130,9 @@ public class Strings {
* @param existingVariableNames the names of other variables existing in the same scope
*
* @return a variable name based on the given original name, not conflicting with any of the given other names or
* any Java keyword; starting with a lower-case letter
* any Java keyword; starting with a lower-case letter
*/
public static String getSaveVariableName(String name, Collection<String> existingVariableNames) {
public static String getSaveVariableName( String name, Collection<String> existingVariableNames ) {
name = decapitalize( name );
Set<String> conflictingNames = new HashSet<String>( KEYWORDS );
@ -144,4 +144,5 @@ public class Strings {
return name;
}
}

View File

@ -0,0 +1,46 @@
<#--
Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
and/or other contributors as indicated by the @authors tag. See the
copyright.txt file in the distribution for a full listing of all
contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<#if (exceptionTypes?size == 0) >
for ( <@includeModel object=sourceType/> ${iteratorReference} : ${sourceReference} ) {
${ext.targetBeanName}.${ext.targetAccessorName}( <@includeModel object=assignment
targetBeanName=ext.targetBeanName
raw=ext.raw
existingInstanceMapping=ext.existingInstanceMapping
targetAccessorName=ext.targetAccessorName
targetType=ext.targetType/> );
}
<#else>
try {
for ( <@includeModel object=sourceType/> ${iteratorReference} : ${sourceReference} ) {
${ext.targetBeanName}.${ext.targetAccessorName}( <@includeModel object=assignment
targetBeanName=ext.targetBeanName
raw=ext.raw
existingInstanceMapping=ext.existingInstanceMapping
targetAccessorName=ext.targetAccessorName
targetType=ext.targetType/> );
}
}
<#list exceptionTypes as exceptionType>
catch ( <@includeModel object=exceptionType/> e ) {
throw new RuntimeException( e );
}
</#list>
</#if>

View File

@ -0,0 +1,221 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
import org.mapstruct.ap.test.collection.adder.target.AdderUsageObserver;
import org.mapstruct.ap.test.collection.adder.source.Source;
import org.mapstruct.ap.test.collection.adder.source.SourceTeeth;
import org.mapstruct.ap.test.collection.adder.target.TargetOnlyGetter;
import org.mapstruct.ap.test.collection.adder.target.TargetHuman;
import org.mapstruct.ap.test.collection.adder.target.TargetDali;
import org.mapstruct.ap.test.collection.adder.target.Target;
import org.mapstruct.ap.test.collection.adder.target.TargetViaTargetType;
import org.mapstruct.ap.test.collection.adder.target.TargetWithoutSetter;
import java.util.ArrayList;
import java.util.Arrays;
import static org.fest.assertions.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.collection.adder.target.IndoorPet;
import org.mapstruct.ap.test.collection.adder.target.OutdoorPet;
import org.mapstruct.ap.test.collection.adder.target.Pet;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
/**
*
* @author Sjaak Derksen
*/
@WithClasses( {
Source.class,
SourceTeeth.class,
Target.class,
TargetDali.class,
TargetHuman.class,
TargetOnlyGetter.class,
TargetViaTargetType.class,
TargetWithoutSetter.class,
SourceTargetMapper.class,
SourceTargetMapperStrategyDefault.class,
SourceTargetMapperStrategySetterPreferred.class,
PetMapper.class,
TeethMapper.class,
AdderUsageObserver.class,
Pet.class,
IndoorPet.class,
OutdoorPet.class,
DogException.class,
CatException.class } )
@RunWith( AnnotationProcessorTestRunner.class )
public class AdderTest {
@Test
public void testAdd() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "mouse" ) );
Target target = SourceTargetMapper.INSTANCE.toTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getPets().size() ).isEqualTo( 1 );
assertThat( target.getPets().get( 0 ) ).isEqualTo( 2L );
assertTrue( AdderUsageObserver.isUsed() );
}
@Test( expected = DogException.class )
public void testAddWithExceptionInThrowsClause() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "dog" ) );
SourceTargetMapper.INSTANCE.toTarget( source );
}
@Test( expected = RuntimeException.class )
public void testAddWithExceptionNotInThrowsClause() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "cat" ) );
SourceTargetMapper.INSTANCE.toTarget( source );
}
@Test
public void testAddwithExistingTarget() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "mouse" ) );
Target target = new Target();
target.setPets( new ArrayList<Long>( Arrays.asList( 1L ) ) );
SourceTargetMapper.INSTANCE.toExistingTarget( source, target );
assertThat( target ).isNotNull();
assertThat( target.getPets().size() ).isEqualTo( 2 );
assertThat( target.getPets().get( 0 ) ).isEqualTo( 1L );
assertThat( target.getPets().get( 1 ) ).isEqualTo( 2L );
assertTrue( AdderUsageObserver.isUsed() );
}
@Test
public void testShouldUseDefaultStrategy() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "mouse" ) );
Target target = SourceTargetMapperStrategyDefault.INSTANCE.shouldFallBackToAdder( source );
assertThat( target ).isNotNull();
assertThat( target.getPets().size() ).isEqualTo( 1 );
assertThat( target.getPets().get( 0 ) ).isEqualTo( 2L );
assertFalse( AdderUsageObserver.isUsed() );
}
@Test
public void testShouldPreferSetterStrategyButThereIsNone() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "mouse" ) );
TargetWithoutSetter target = SourceTargetMapperStrategySetterPreferred.INSTANCE.toTargetDontUseAdder( source );
assertThat( target ).isNotNull();
assertThat( target.getPets().size() ).isEqualTo( 1 );
assertThat( target.getPets().get( 0 ) ).isEqualTo( 2L );
assertTrue( AdderUsageObserver.isUsed() );
}
@Test
public void testShouldPreferHumanSingular() {
AdderUsageObserver.setUsed( false );
SourceTeeth source = new SourceTeeth();
source.setTeeth( Arrays.asList( "moler" ) );
TargetHuman target = SourceTargetMapper.INSTANCE.toTargetHuman( source );
assertThat( target ).isNotNull();
assertThat( target.getTeeth().size() ).isEqualTo( 1 );
assertThat( target.getTeeth().get( 0 ) ).isEqualTo( 3 );
assertTrue( AdderUsageObserver.isUsed() );
}
@Test
public void testshouldFallBackToDaliSingularInAbsenseOfHumanSingular() {
AdderUsageObserver.setUsed( false );
SourceTeeth source = new SourceTeeth();
source.setTeeth( Arrays.asList( "moler" ) );
TargetDali target = SourceTargetMapper.INSTANCE.toTargetDali( source );
assertThat( target ).isNotNull();
assertThat( target.getTeeth().size() ).isEqualTo( 1 );
assertThat( target.getTeeth().get( 0 ) ).isEqualTo( 3 );
assertTrue( AdderUsageObserver.isUsed() );
}
@Test
public void testAddReverse() throws DogException {
AdderUsageObserver.setUsed( false );
Target source = new Target();
source.setPets( Arrays.asList( 3L ) );
Source target = SourceTargetMapper.INSTANCE.toSource( source );
assertThat( target ).isNotNull();
assertThat( target.getPets().size() ).isEqualTo( 1 );
assertThat( target.getPets().get( 0 ) ).isEqualTo( "cat" );
}
@Test
public void testAddOnlyGetter() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "mouse" ) );
TargetOnlyGetter target = SourceTargetMapper.INSTANCE.toTargetOnlyGetter( source );
assertThat( target ).isNotNull();
assertThat( target.getPets().size() ).isEqualTo( 1 );
assertThat( target.getPets().get( 0 ) ).isEqualTo( 2L );
assertTrue( AdderUsageObserver.isUsed() );
}
@Test
public void testAddViaTargetType() throws DogException {
AdderUsageObserver.setUsed( false );
Source source = new Source();
source.setPets( Arrays.asList( "mouse" ) );
TargetViaTargetType target = SourceTargetMapper.INSTANCE.toTargetViaTargetType( source );
assertThat( target ).isNotNull();
assertThat( target.getPets().size() ).isEqualTo( 1 );
assertThat( target.getPets().get( 0 ) ).isNotNull();
assertThat( target.getPets().get( 0 ).getValue() ).isEqualTo( 2L );
assertTrue( AdderUsageObserver.isUsed() );
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
/**
*
* @author Sjaak Derksen
*/
public class CatException extends Exception {
public CatException() {
}
public CatException( String msg ) {
super( msg );
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
/**
*
* @author Sjaak Derksen
*/
public class DogException extends Exception {
public DogException() {
}
public DogException( String msg ) {
super( msg );
}
}

View File

@ -0,0 +1,99 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.mapstruct.TargetType;
import org.mapstruct.ap.test.collection.adder.target.IndoorPet;
import org.mapstruct.ap.test.collection.adder.target.OutdoorPet;
import org.mapstruct.ap.test.collection.adder.target.Pet;
/**
*
* @author Sjaak Derksen
*/
public class PetMapper {
private static final Map<String, Long> PETS_TO_TARGET = ImmutableMap.<String, Long>builder()
.put( "rabbit", 1L )
.put( "mouse", 2L ).build();
private static final Map<Long, String> PETS_TO_SOURCE = ImmutableMap.<Long, String>builder()
.put( 1L, "rabbit" )
.put( 2L, "mouse" )
.put( 3L, "cat" )
.put( 4L, "dog" ).build();
/**
* method to be used when using an adder
*
* @param pet
* @return
* @throws CatException
* @throws DogException
*/
public Long toPet( String pet ) throws CatException, DogException {
if ( "cat".equals( pet ) ) {
throw new CatException();
}
else if ( "dog".equals( pet ) ) {
throw new DogException();
}
return PETS_TO_TARGET.get( pet );
}
/**
* Method to be used when not using an adder
*
* @param pets
* @return
* @throws CatException
* @throws DogException
*/
public List<Long> toPets( List<String> pets ) throws CatException, DogException {
List<Long> result = new ArrayList<Long>();
for (String pet : pets) {
result.add( toPet( pet) );
}
return result;
}
@SuppressWarnings( "unchecked" )
public <T extends Pet> T toPet(String pet, @TargetType Class<T> clazz) throws CatException, DogException {
if ( clazz == IndoorPet.class ) {
return (T) new IndoorPet( toPet( pet ) );
}
if ( clazz == OutdoorPet.class ) {
return (T) new OutdoorPet( toPet( pet ) );
}
return null;
}
public List<String> toSourcePets( List<Long> pets ) throws CatException, DogException {
List<String> result = new ArrayList<String>();
for (Long pet : pets) {
result.add( PETS_TO_SOURCE.get( pet ) );
}
return result;
}
}

View File

@ -0,0 +1,55 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
import org.mapstruct.ap.test.collection.adder.source.Source;
import org.mapstruct.ap.test.collection.adder.source.SourceTeeth;
import org.mapstruct.ap.test.collection.adder.target.TargetOnlyGetter;
import org.mapstruct.ap.test.collection.adder.target.TargetHuman;
import org.mapstruct.ap.test.collection.adder.target.TargetDali;
import org.mapstruct.ap.test.collection.adder.target.Target;
import org.mapstruct.ap.test.collection.adder.target.TargetViaTargetType;
import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper( collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
uses = { PetMapper.class, TeethMapper.class } )
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
Target toTarget( Source source ) throws DogException;
Source toSource( Target source );
void toExistingTarget( Source source, @MappingTarget Target target );
TargetDali toTargetDali( SourceTeeth source );
TargetHuman toTargetHuman( SourceTeeth source );
TargetOnlyGetter toTargetOnlyGetter( Source source ) throws DogException;
TargetViaTargetType toTargetViaTargetType( Source source );
}

View File

@ -0,0 +1,37 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
import org.mapstruct.ap.test.collection.adder.source.Source;
import org.mapstruct.ap.test.collection.adder.target.Target;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper( uses = { PetMapper.class } )
public interface SourceTargetMapperStrategyDefault {
SourceTargetMapperStrategyDefault INSTANCE = Mappers.getMapper( SourceTargetMapperStrategyDefault.class );
Target shouldFallBackToAdder( Source source ) throws DogException;
}

View File

@ -0,0 +1,41 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
import org.mapstruct.ap.test.collection.adder.source.Source;
import org.mapstruct.ap.test.collection.adder.target.TargetWithoutSetter;
import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper( collectionMappingStrategy = CollectionMappingStrategy.SETTER_PREFERRED,
uses = { PetMapper.class }
)
public interface SourceTargetMapperStrategySetterPreferred {
SourceTargetMapperStrategySetterPreferred INSTANCE =
Mappers.getMapper( SourceTargetMapperStrategySetterPreferred.class );
TargetWithoutSetter toTargetDontUseAdder( Source source ) throws DogException;
}

View File

@ -0,0 +1,38 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
/**
*
* @author Sjaak Derksen
*/
public class TeethMapper {
private static final Map<String, Integer> TEETH = ImmutableMap.<String, Integer>builder()
.put( "incisor", 1 )
.put( "canine", 2 )
.put( "moler", 3 ).build();
public Integer toTooth( String tooth ) {
return TEETH.get( tooth );
}
}

View File

@ -0,0 +1,38 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.source;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class Source {
private List<String> pets;
public List<String> getPets() {
return pets;
}
public void setPets( List<String> pets ) {
this.pets = pets;
}
}

View File

@ -0,0 +1,38 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.source;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class SourceTeeth {
private List<String> teeth;
public List<String> getTeeth() {
return teeth;
}
public void setTeeth( List<String> teeth ) {
this.teeth = teeth;
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
/**
*
* @author Sjaak Derksen
*/
public class AdderUsageObserver {
private AdderUsageObserver() { }
private static boolean used = false;
public static boolean isUsed() {
return used;
}
public static void setUsed( boolean used ) {
AdderUsageObserver.used = used;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
/**
*
* @author Sjaak Derksen
*/
public class IndoorPet extends Pet {
private Long value;
public IndoorPet( Long value ) {
this.value = value;
}
public Long getValue() {
return value;
}
public void setValue( Long value ) {
this.value = value;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
/**
*
* @author Sjaak Derksen
*/
public class OutdoorPet extends Pet {
private Long value;
public OutdoorPet( Long value ) {
this.value = value;
}
public Long getValue() {
return value;
}
public void setValue( Long value ) {
this.value = value;
}
}

View File

@ -0,0 +1,27 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
/**
*
* @author Sjaak Derksen
*/
public class Pet {
}

View File

@ -0,0 +1,59 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class Target {
private List<Long> pets;
public List<Long> getPets() {
return pets;
}
public void setPets( List<Long> pets ) {
this.pets = pets;
}
public void addCat( Long cat ) {
// dummy method to test selection mechanims
}
public void addDog( Long cat ) {
// dummy method to test selection mechanims
}
public void addPets( Long cat ) {
// dummy method to test selection mechanims
}
public void addPet( Long pet ) {
AdderUsageObserver.setUsed( true );
if ( pets == null ) {
pets = new ArrayList<Long>();
}
pets.add( pet );
}
}

View File

@ -0,0 +1,48 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class TargetDali {
private List<Integer> teeth;
public List<Integer> getTeeth() {
return teeth;
}
public void setTeeth( List<Integer> teeth ) {
this.teeth = teeth;
}
public void addTeeth( Integer tooth ) {
AdderUsageObserver.setUsed( true );
if ( teeth == null ) {
teeth = new ArrayList<Integer>();
}
teeth.add( tooth );
}
}

View File

@ -0,0 +1,54 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class TargetHuman {
private List<Integer> teeth;
public List<Integer> getTeeth() {
return teeth;
}
public void setTeeth( List<Integer> teeth ) {
this.teeth = teeth;
}
public void addTooth( Integer pet ) {
AdderUsageObserver.setUsed( true );
if ( teeth == null ) {
teeth = new ArrayList<Integer>();
}
teeth.add( pet );
}
public void addTeeth( Integer tooth ) {
if ( teeth == null ) {
teeth = new ArrayList<Integer>();
}
teeth.add( tooth );
}
}

View File

@ -0,0 +1,55 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class TargetOnlyGetter {
private List<Long> pets;
public List<Long> getPets() {
return pets;
}
public void addCat( Long cat ) {
// dummy method to test selection mechanims
}
public void addDog( Long cat ) {
// dummy method to test selection mechanims
}
public void addPets( Long cat ) {
// dummy method to test selection mechanims
}
public void addPet( Long pet ) {
AdderUsageObserver.setUsed( true );
if ( pets == null ) {
pets = new ArrayList<Long>();
}
pets.add( pet );
}
}

View File

@ -0,0 +1,47 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class TargetViaTargetType {
private List<IndoorPet> pets;
public List<IndoorPet> getPets() {
return pets;
}
public void setPets( List<IndoorPet> pets ) {
this.pets = pets;
}
public void addPet( IndoorPet pet ) {
AdderUsageObserver.setUsed( true );
if ( pets == null ) {
pets = new ArrayList<IndoorPet>();
}
pets.add( pet );
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.collection.adder.target;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class TargetWithoutSetter {
private List<Long> pets;
public List<Long> getPets() {
return pets;
}
public void addPet( Long pet ) {
AdderUsageObserver.setUsed( true );
if ( pets == null ) {
pets = new ArrayList<Long>();
}
pets.add( pet );
}
}