#168 Allow to inherit mapping method configurations from the @MapperConfig-annotated type, either automatically when all method types match, or explicitly using @InheritConfiguration.

This commit is contained in:
Andreas Gudian 2015-02-24 11:26:28 +01:00
parent 2c845480a0
commit 952ee8526d
40 changed files with 1432 additions and 585 deletions

View File

@ -112,4 +112,17 @@ public @interface Mapper {
* @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapper. * @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapper.
*/ */
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.DEFAULT; NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.DEFAULT;
/**
* The strategy to use for applying method-level configuration annotations of prototype methods in the interface
* specified with {@link #config()}. Annotations that can be inherited are for example {@link Mapping},
* {@link IterableMapping}, {@link MapMapping}, or {@link BeanMapping}.
* <p>
* If no strategy is configured, the strategy given via {@link MapperConfig#mappingInheritanceStrategy()} will be
* applied, using {@link MappingInheritanceStrategy#EXPLICIT} as default.
*
* @return The strategy to use for applying {@code @Mapping} configurations of prototype methods in the interface
* specified with {@link #config()}.
*/
MappingInheritanceStrategy mappingInheritanceStrategy() default MappingInheritanceStrategy.DEFAULT;
} }

View File

@ -26,10 +26,18 @@ import java.lang.annotation.Target;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
/** /**
* Marks a class-, interface-, enum declaration as (common) configuration. * Marks a class- or interface-declaration as (common) configuration.
* * <p>
* The {@link #unmappedTargetPolicy() } and {@link #componentModel() } can be overruled by a specific {@link Mapper} * The {@link #unmappedTargetPolicy() } and {@link #componentModel() } can be overruled by a specific {@link Mapper}
* annotation. {@link #uses() } will be used in addition to what is specified in the {@link Mapper} annotation. * annotation. {@link #uses() } will be used in addition to what is specified in the {@link Mapper} annotation.
* </p>
* <p>
* Mapping methods defined in the annotated type can be used as <em>prototypes</em> from which method-level annotations
* such as {@code @Mapping}, {@code @IterableMapping}, etc. can be inherited. Depending on the configured
* {@link #mappingInheritanceStrategy()}, the configuration can be inherited either explicitly using
* {@link InheritConfiguration} or {@link InheritInverseConfiguration}, or automatically in case all source and target
* types are assignable.
* </p>
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
@ -80,7 +88,7 @@ public @interface MapperConfig {
* *
* @return The strategy applied when propagating the value of collection-typed properties. * @return The strategy applied when propagating the value of collection-typed properties.
*/ */
CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.DEFAULT; CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.ACCESSOR_ONLY;
/** /**
* The strategy to be applied when {@code null} is passed as source value to mapping methods. If no strategy is * The strategy to be applied when {@code null} is passed as source value to mapping methods. If no strategy is
@ -88,5 +96,18 @@ public @interface MapperConfig {
* *
* @return The strategy to be applied when {@code null} is passed as source value to mapping methods. * @return The strategy to be applied when {@code null} is passed as source value to mapping methods.
*/ */
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.DEFAULT; NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
/**
* The strategy to use for applying method-level configuration annotations of prototype methods in the interface
* annotated with this annotation. Annotations that can be inherited are for example {@link Mapping},
* {@link IterableMapping}, {@link MapMapping}, or {@link BeanMapping}.
* <p>
* If no strategy is configured, {@link MappingInheritanceStrategy#EXPLICIT} will be used as default.
*
* @return The strategy to use for applying {@code @Mapping} configurations of prototype methods in the interface
* annotated with this annotation.
*/
MappingInheritanceStrategy mappingInheritanceStrategy()
default MappingInheritanceStrategy.EXPLICIT;
} }

View File

@ -0,0 +1,47 @@
/**
* Copyright 2012-2015 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;
/**
* The strategy to use for applying method-level configuration annotations of prototype methods in the interface
* specified with {@link Mapper#config()}.
*
* @author Andreas Gudian
*/
public enum MappingInheritanceStrategy {
/**
* Apply the method-level configuration annotations only if the prototype method is explicitly referenced using
* {@link InheritConfiguration}.
*/
EXPLICIT,
/**
* Apply the method-level configuration annotations if source and target types of the prototype method are
* assignable from the types of a given mapping method.
*/
AUTO_INHERIT_FROM_CONFIG,
/**
* When given via {@link Mapper#mappingInheritanceStrategy()}, the value specified via
* {@link MapperConfig#mappingInheritanceStrategy()} will be applied, if present.
* <p>
* When given via {@link MapperConfig#mappingInheritanceStrategy()}, the strategy {@link #EXPLICIT} will be applied.
*/
DEFAULT;
}

View File

@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
@ -43,9 +44,9 @@ import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.BeanMappingPrism; import org.mapstruct.ap.prism.BeanMappingPrism;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.NullValueMappingPrism; import org.mapstruct.ap.prism.NullValueMappingPrism;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Executables; import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.MapperConfig;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
/** /**
@ -145,7 +146,7 @@ public class BeanMappingMethod extends MappingMethod {
Set<String> handledTargets = new HashSet<String>(); Set<String> handledTargets = new HashSet<String>();
for ( Map.Entry<String, List<Mapping>> entry : method.getMappings().entrySet() ) { for ( Map.Entry<String, List<Mapping>> entry : method.getMappingOptions().getMappings().entrySet() ) {
for ( Mapping mapping : entry.getValue() ) { for ( Mapping mapping : entry.getValue() ) {
PropertyMapping propertyMapping = null; PropertyMapping propertyMapping = null;

View File

@ -29,6 +29,8 @@ import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.util.Message; import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
import static org.mapstruct.ap.util.Collections.first;
/** /**
* A {@link MappingMethod} which maps one enum type to another, optionally configured by one or more * A {@link MappingMethod} which maps one enum type to another, optionally configured by one or more
* {@link EnumMapping}s. * {@link EnumMapping}s.
@ -63,8 +65,7 @@ public class EnumMappingMethod extends MappingMethod {
List<EnumMapping> enumMappings = new ArrayList<EnumMapping>(); List<EnumMapping> enumMappings = new ArrayList<EnumMapping>();
List<String> sourceEnumConstants List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
= method.getSourceParameters().iterator().next().getType().getEnumConstants();
for ( String enumConstant : sourceEnumConstants ) { for ( String enumConstant : sourceEnumConstants ) {
List<Mapping> mappedConstants = method.getMappingBySourcePropertyName( enumConstant ); List<Mapping> mappedConstants = method.getMappingBySourcePropertyName( enumConstant );
@ -75,7 +76,7 @@ public class EnumMappingMethod extends MappingMethod {
else if ( mappedConstants.size() == 1 ) { else if ( mappedConstants.size() == 1 ) {
enumMappings.add( enumMappings.add(
new EnumMapping( new EnumMapping(
enumConstant, mappedConstants.iterator().next().getTargetName() enumConstant, first( mappedConstants ).getTargetName()
) )
); );
} }
@ -96,13 +97,12 @@ public class EnumMappingMethod extends MappingMethod {
} }
private boolean reportErrorIfMappedEnumConstantsDontExist(SourceMethod method) { private boolean reportErrorIfMappedEnumConstantsDontExist(SourceMethod method) {
List<String> sourceEnumConstants = List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
method.getSourceParameters().iterator().next().getType().getEnumConstants();
List<String> targetEnumConstants = method.getReturnType().getEnumConstants(); List<String> targetEnumConstants = method.getReturnType().getEnumConstants();
boolean foundIncorrectMapping = false; boolean foundIncorrectMapping = false;
for ( List<Mapping> mappedConstants : method.getMappings().values() ) { for ( List<Mapping> mappedConstants : method.getMappingOptions().getMappings().values() ) {
for ( Mapping mappedConstant : mappedConstants ) { for ( Mapping mappedConstant : mappedConstants ) {
if ( mappedConstant.getSourceName() == null ) { if ( mappedConstant.getSourceName() == null ) {
@ -118,7 +118,7 @@ public class EnumMappingMethod extends MappingMethod {
mappedConstant.getSourceAnnotationValue(), mappedConstant.getSourceAnnotationValue(),
Message.ENUMMAPPING_NON_EXISTING_CONSTANT, Message.ENUMMAPPING_NON_EXISTING_CONSTANT,
mappedConstant.getSourceName(), mappedConstant.getSourceName(),
method.getSourceParameters().iterator().next().getType() first( method.getSourceParameters() ).getType()
); );
foundIncorrectMapping = true; foundIncorrectMapping = true;
} }
@ -148,8 +148,7 @@ public class EnumMappingMethod extends MappingMethod {
private boolean reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped( private boolean reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped(
SourceMethod method) { SourceMethod method) {
List<String> sourceEnumConstants = List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
method.getSourceParameters().iterator().next().getType().getEnumConstants();
List<String> targetEnumConstants = method.getReturnType().getEnumConstants(); List<String> targetEnumConstants = method.getReturnType().getEnumConstants();
List<String> unmappedSourceEnumConstants = new ArrayList<String>(); List<String> unmappedSourceEnumConstants = new ArrayList<String>();
@ -182,6 +181,6 @@ public class EnumMappingMethod extends MappingMethod {
} }
public Parameter getSourceParameter() { public Parameter getSourceParameter() {
return getParameters().iterator().next(); return first( getParameters() );
} }
} }

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.model;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
@ -30,10 +31,12 @@ import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.prism.NullValueMappingPrism; import org.mapstruct.ap.prism.NullValueMappingPrism;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.MapperConfig;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
import static org.mapstruct.ap.util.Collections.first;
/** /**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one iterable type to another. The collection * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one iterable type to another. The collection
* elements are mapped either by a {@link TypeConversion} or another mapping method. * elements are mapped either by a {@link TypeConversion} or another mapping method.
@ -82,7 +85,7 @@ public class IterableMappingMethod extends MappingMethod {
} }
public IterableMappingMethod build() { public IterableMappingMethod build() {
Type sourceParameterType = method.getSourceParameters().iterator().next().getType(); Type sourceParameterType = first( method.getSourceParameters() ).getType();
Type resultType = method.getResultType(); Type resultType = method.getResultType();
Type sourceElementType = sourceParameterType.isArrayType() ? sourceParameterType.getComponentType() : Type sourceElementType = sourceParameterType.isArrayType() ? sourceParameterType.getComponentType() :

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.model;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.assignment.Assignment; import org.mapstruct.ap.model.assignment.Assignment;
@ -28,10 +29,12 @@ import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.prism.NullValueMappingPrism; import org.mapstruct.ap.prism.NullValueMappingPrism;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.MapperConfig;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
import static org.mapstruct.ap.util.Collections.first;
/** /**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
* values are mapped either by a {@link TypeConversion} or another mapping method if required. * values are mapped either by a {@link TypeConversion} or another mapping method if required.
@ -99,7 +102,7 @@ public class MapMappingMethod extends MappingMethod {
public MapMappingMethod build() { public MapMappingMethod build() {
List<Type> sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters(); List<Type> sourceTypeParams = first( method.getSourceParameters() ).getType().getTypeParameters();
List<Type> resultTypeParams = method.getResultType().getTypeParameters(); List<Type> resultTypeParams = method.getResultType().getTypeParameters();
// find mapping method or conversion for key // find mapping method or conversion for key

View File

@ -24,7 +24,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
@ -37,6 +37,7 @@ import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.MappingPrism; import org.mapstruct.ap.prism.MappingPrism;
import org.mapstruct.ap.prism.MappingsPrism; import org.mapstruct.ap.prism.MappingsPrism;
import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.Message; import org.mapstruct.ap.util.Message;
/** /**
@ -61,7 +62,6 @@ public class Mapping {
private final AnnotationValue sourceAnnotationValue; private final AnnotationValue sourceAnnotationValue;
private final AnnotationValue targetAnnotationValue; private final AnnotationValue targetAnnotationValue;
private SourceReference sourceReference; private SourceReference sourceReference;
private SourceReference targetReference;
public static Map<String, List<Mapping>> fromMappingsPrism(MappingsPrism mappingsAnnotation, public static Map<String, List<Mapping>> fromMappingsPrism(MappingsPrism mappingsAnnotation,
ExecutableElement method, ExecutableElement method,
@ -239,10 +239,6 @@ public class Mapping {
return sourceReference; return sourceReference;
} }
public SourceReference getTargetReference() {
return targetReference;
}
public TypeMirror getResultType() { public TypeMirror getResultType() {
return resultType; return resultType;
} }
@ -298,6 +294,33 @@ public class Mapping {
return reverse; return reverse;
} }
/**
* Creates a copy of this mapping, which is adapted to the given method
*
* @param the method to adapt the copy to
*/
public Mapping copyForInheritanceTo(SourceMethod method) {
Mapping mapping = new Mapping(
sourceName,
constant,
javaExpression,
targetName,
dateFormat,
qualifiers,
isIgnored,
mirror,
sourceAnnotationValue,
targetAnnotationValue,
resultType
);
if ( sourceReference != null ) {
mapping.sourceReference = sourceReference.copyForInheritanceTo( method );
}
return mapping;
}
@Override @Override
public String toString() { public String toString() {
return "Mapping {" + return "Mapping {" +

View File

@ -0,0 +1,137 @@
/**
* Copyright 2012-2015 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.source;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.util.FormattingMessager;
/**
* Encapsulates all options specifiable on a mapping method
*
* @author Andreas Gudian
*/
public class MappingOptions {
private Map<String, List<Mapping>> mappings;
private IterableMapping iterableMapping;
private MapMapping mapMapping;
private boolean fullyInitialized;
public MappingOptions(Map<String, List<Mapping>> mappings, IterableMapping iterableMapping, MapMapping mapMapping) {
this.mappings = mappings;
this.iterableMapping = iterableMapping;
this.mapMapping = mapMapping;
}
/**
* @return the {@link Mapping}s configured for this method, keyed by target property name. Only for enum mapping
* methods a target will be mapped by several sources.
*/
public Map<String, List<Mapping>> getMappings() {
return mappings;
}
public IterableMapping getIterableMapping() {
return iterableMapping;
}
public MapMapping getMapMapping() {
return mapMapping;
}
public void setMappings(Map<String, List<Mapping>> mappings) {
this.mappings = mappings;
}
public void setIterableMapping(IterableMapping iterableMapping) {
this.iterableMapping = iterableMapping;
}
public void setMapMapping(MapMapping mapMapping) {
this.mapMapping = mapMapping;
}
/**
* @return the {@code true}, iff the options have been fully initialized by applying all available inheritance
* options
*/
public boolean isFullyInitialized() {
return fullyInitialized;
}
public void markAsFullyInitialized() {
this.fullyInitialized = true;
}
/**
* Merges in all the mapping options configured, giving the already defined options precedence.
*
* @param inherited the options to inherit, may be {@code null}
* @param isInverse if {@code true}, the specified options are from an inverse method
* @param method the source method
* @param messager the messager
* @param typeFactory the type factory
*/
public void applyInheritedOptions(MappingOptions inherited, boolean isInverse, SourceMethod method,
FormattingMessager messager, TypeFactory typeFactory) {
if ( null != inherited ) {
if ( getIterableMapping() == null ) {
if ( inherited.getIterableMapping() != null ) {
setIterableMapping( inherited.getIterableMapping() );
}
}
if ( getMapMapping() == null ) {
if ( inherited.getMapMapping() != null ) {
setMapMapping( inherited.getMapMapping() );
}
}
Map<String, List<Mapping>> newMappings = new HashMap<String, List<Mapping>>();
for ( List<Mapping> lmappings : inherited.getMappings().values() ) {
for ( Mapping mapping : lmappings ) {
if ( isInverse ) {
mapping = mapping.reverse( method, messager, typeFactory );
}
if ( mapping != null ) {
List<Mapping> mappingsOfProperty = newMappings.get( mapping.getTargetName() );
if ( mappingsOfProperty == null ) {
mappingsOfProperty = new ArrayList<Mapping>();
newMappings.put( mapping.getTargetName(), mappingsOfProperty );
}
mappingsOfProperty.add( mapping.copyForInheritanceTo( method ) );
}
}
}
// now add all of its own mappings
newMappings.putAll( getMappings() );
setMappings( newMappings );
}
markAsFullyInitialized();
}
}

View File

@ -19,11 +19,12 @@
package org.mapstruct.ap.model.source; package org.mapstruct.ap.model.source;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
@ -33,9 +34,12 @@ import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.model.source.SourceReference.PropertyEntry; import org.mapstruct.ap.model.source.SourceReference.PropertyEntry;
import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.MapperConfig;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
import static org.mapstruct.ap.util.Collections.first;
/** /**
* Represents a mapping method with source and target type and the mappings between the properties of source and target * Represents a mapping method with source and target type and the mappings between the properties of source and target
* type. * type.
@ -50,7 +54,6 @@ public class SourceMethod implements Method {
private final Types typeUtils; private final Types typeUtils;
private final TypeFactory typeFactory; private final TypeFactory typeFactory;
private final FormattingMessager messager;
private final Type declaringMapper; private final Type declaringMapper;
private final ExecutableElement executable; private final ExecutableElement executable;
@ -61,10 +64,14 @@ public class SourceMethod implements Method {
private final Accessibility accessibility; private final Accessibility accessibility;
private final List<Type> exceptionTypes; private final List<Type> exceptionTypes;
private final MapperConfig config; private final MapperConfig config;
private final MappingOptions mappingOptions;
private final List<SourceMethod> prototypeMethods;
private Map<String, List<Mapping>> mappings; private List<Parameter> sourceParameters;
private IterableMapping iterableMapping;
private MapMapping mapMapping; private List<String> parameterNames;
private List<SourceMethod> applicablePrototypeMethods;
public static class Builder { public static class Builder {
@ -80,6 +87,7 @@ public class SourceMethod implements Method {
private TypeFactory typeFactory = null; private TypeFactory typeFactory = null;
private FormattingMessager messager = null; private FormattingMessager messager = null;
private MapperConfig mapperConfig = null; private MapperConfig mapperConfig = null;
private List<SourceMethod> prototypeMethods = Collections.emptyList();
public Builder() { public Builder() {
} }
@ -144,24 +152,27 @@ public class SourceMethod implements Method {
return this; return this;
} }
public SourceMethod createSourceMethod() { public Builder setPrototypeMethods(List<SourceMethod> prototypeMethods) {
this.prototypeMethods = prototypeMethods;
return this;
}
public SourceMethod buildSourceMethod() {
SourceMethod sourceMethod = new SourceMethod( SourceMethod sourceMethod = new SourceMethod(
declaringMapper, declaringMapper,
executable, executable,
parameters, parameters,
returnType, returnType,
exceptionTypes, exceptionTypes,
mappings, new MappingOptions( mappings, iterableMapping, mapMapping ),
iterableMapping,
mapMapping,
typeUtils, typeUtils,
typeFactory, typeFactory,
messager, mapperConfig,
mapperConfig prototypeMethods
); );
if ( mappings != null ) { if ( mappings != null ) {
for ( Map.Entry<String, List<Mapping>> entry : sourceMethod.getMappings().entrySet() ) { for ( Map.Entry<String, List<Mapping>> entry : mappings.entrySet() ) {
for ( Mapping mapping : entry.getValue() ) { for ( Mapping mapping : entry.getValue() ) {
mapping.init( sourceMethod, messager, typeFactory ); mapping.init( sourceMethod, messager, typeFactory );
} }
@ -173,26 +184,24 @@ public class SourceMethod implements Method {
@SuppressWarnings( "checkstyle:parameternumber" ) @SuppressWarnings( "checkstyle:parameternumber" )
private SourceMethod( Type declaringMapper, ExecutableElement executable, List<Parameter> parameters, private SourceMethod( Type declaringMapper, ExecutableElement executable, List<Parameter> parameters,
Type returnType, List<Type> exceptionTypes, Map<String, List<Mapping>> mappings, Type returnType, List<Type> exceptionTypes, MappingOptions mappingOptions, Types typeUtils,
IterableMapping iterableMapping, MapMapping mapMapping, Types typeUtils, TypeFactory typeFactory, MapperConfig config, List<SourceMethod> prototypeMethods) {
TypeFactory typeFactory, FormattingMessager messager, MapperConfig config) {
this.declaringMapper = declaringMapper; this.declaringMapper = declaringMapper;
this.executable = executable; this.executable = executable;
this.parameters = parameters; this.parameters = parameters;
this.returnType = returnType; this.returnType = returnType;
this.exceptionTypes = exceptionTypes; this.exceptionTypes = exceptionTypes;
this.mappings = mappings;
this.iterableMapping = iterableMapping;
this.mapMapping = mapMapping;
this.accessibility = Accessibility.fromModifiers( executable.getModifiers() ); this.accessibility = Accessibility.fromModifiers( executable.getModifiers() );
this.mappingOptions = mappingOptions;
this.mappingTargetParameter = determineMappingTargetParameter( parameters ); this.mappingTargetParameter = determineMappingTargetParameter( parameters );
this.targetTypeParameter = determineTargetTypeParameter( parameters ); this.targetTypeParameter = determineTargetTypeParameter( parameters );
this.typeUtils = typeUtils; this.typeUtils = typeUtils;
this.typeFactory = typeFactory; this.typeFactory = typeFactory;
this.messager = messager;
this.config = config; this.config = config;
this.prototypeMethods = prototypeMethods;
} }
private Parameter determineMappingTargetParameter(Iterable<Parameter> parameters) { private Parameter determineMappingTargetParameter(Iterable<Parameter> parameters) {
@ -249,11 +258,13 @@ public class SourceMethod implements Method {
*/ */
@Override @Override
public List<Parameter> getSourceParameters() { public List<Parameter> getSourceParameters() {
List<Parameter> sourceParameters = new ArrayList<Parameter>(); if ( sourceParameters == null ) {
sourceParameters = new ArrayList<Parameter>();
for ( Parameter parameter : parameters ) { for ( Parameter parameter : parameters ) {
if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) { if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) {
sourceParameters.add( parameter ); sourceParameters.add( parameter );
}
} }
} }
@ -262,10 +273,12 @@ public class SourceMethod implements Method {
@Override @Override
public List<String> getParameterNames() { public List<String> getParameterNames() {
List<String> parameterNames = new ArrayList<String>( parameters.size() ); if ( parameterNames == null ) {
parameterNames = new ArrayList<String>( parameters.size() );
for ( Parameter parameter : parameters ) { for ( Parameter parameter : parameters ) {
parameterNames.add( parameter.getName() ); parameterNames.add( parameter.getName() );
}
} }
return parameterNames; return parameterNames;
@ -289,84 +302,31 @@ public class SourceMethod implements Method {
return accessibility; return accessibility;
} }
/**
* @return the {@link Mapping}s configured for this method, keyed by target property name. Only for enum mapping
* methods a target will be mapped by several sources.
*/
public Map<String, List<Mapping>> getMappings() {
return mappings;
}
public Mapping getSingleMappingByTargetPropertyName(String targetPropertyName) { public Mapping getSingleMappingByTargetPropertyName(String targetPropertyName) {
List<Mapping> all = mappings.get( targetPropertyName ); List<Mapping> all = mappingOptions.getMappings().get( targetPropertyName );
return all != null ? all.iterator().next() : null; return all != null ? first( all ) : null;
}
public void setMappings(Map<String, List<Mapping>> mappings) {
this.mappings = mappings;
}
public IterableMapping getIterableMapping() {
return iterableMapping;
}
public void setIterableMapping(IterableMapping iterableMapping) {
this.iterableMapping = iterableMapping;
}
public MapMapping getMapMapping() {
return mapMapping;
}
public void setMapMapping(MapMapping mapMapping) {
this.mapMapping = mapMapping;
} }
public boolean reverses(SourceMethod method) { public boolean reverses(SourceMethod method) {
return getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1 return getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1
&& equals( getSourceParameters().iterator().next().getType(), method.getResultType() ) && equals( first( getSourceParameters() ).getType(), method.getResultType() )
&& equals( getResultType(), method.getSourceParameters().iterator().next().getType() ); && equals( getResultType(), first( method.getSourceParameters() ).getType() );
} }
public boolean isSame(SourceMethod method) { public boolean isSame(SourceMethod method) {
return getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1 return getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1
&& equals( getSourceParameters().iterator().next().getType(), && equals( first( getSourceParameters() ).getType(),
method.getSourceParameters().iterator().next().getType()) first( method.getSourceParameters() ).getType() )
&& equals( getResultType(), method.getResultType() ); && equals( getResultType(), method.getResultType() );
} }
public boolean isSimilar(SourceMethod method) { public boolean canInheritFrom(SourceMethod method) {
Map<Type, Integer> test = new HashMap<Type, Integer>(); return isMapMapping() == method.isMapMapping()
&& isIterableMapping() == method.isIterableMapping()
// check how many times a type occurs && isEnumMapping() == method.isEnumMapping()
for (Parameter sourceParam : method.getSourceParameters() ) { && getResultType().isAssignableTo( method.getResultType() )
Type sourceType = sourceParam.getType(); && allParametersAreAssignable( getSourceParameters(), method.getSourceParameters() );
if ( !test.containsKey( sourceType ) ) {
test.put( sourceType, 0 );
}
increase( sourceType, test );
}
// check if this method also contains the same time each parameter type.
for (Parameter sourceParam : getSourceParameters() ) {
Type sourceType = sourceParam.getType();
if ( !test.containsKey( sourceType ) ) {
// method contains a different parameter type than this
return false;
}
decrease( sourceType, test );
}
// now, if they match they should have the same source parameter types each
for ( Integer count : test.values() ) {
if ( count != 0 ) {
return false;
}
}
// finally check the return type.
return equals( getResultType(), method.getResultType() );
} }
@Override @Override
@ -380,17 +340,17 @@ public class SourceMethod implements Method {
} }
public boolean isIterableMapping() { public boolean isIterableMapping() {
return getSourceParameters().size() == 1 && getSourceParameters().iterator().next().getType().isIterableType() return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isIterableType()
&& getResultType().isIterableType(); && getResultType().isIterableType();
} }
public boolean isMapMapping() { public boolean isMapMapping() {
return getSourceParameters().size() == 1 && getSourceParameters().iterator().next().getType().isMapType() return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isMapType()
&& getResultType().isMapType(); && getResultType().isMapType();
} }
public boolean isEnumMapping() { public boolean isEnumMapping() {
return getSourceParameters().size() == 1 && getSourceParameters().iterator().next().getType().isEnumType() return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isEnumType()
&& getResultType().isEnumType(); && getResultType().isEnumType();
} }
@ -422,7 +382,7 @@ public class SourceMethod implements Method {
public List<Mapping> getMappingBySourcePropertyName(String sourcePropertyName) { public List<Mapping> getMappingBySourcePropertyName(String sourcePropertyName) {
List<Mapping> mappingsOfSourceProperty = new ArrayList<Mapping>(); List<Mapping> mappingsOfSourceProperty = new ArrayList<Mapping>();
for ( List<Mapping> mappingOfProperty : mappings.values() ) { for ( List<Mapping> mappingOfProperty : mappingOptions.getMappings().values() ) {
for ( Mapping mapping : mappingOfProperty ) { for ( Mapping mapping : mappingOfProperty ) {
if ( isEnumMapping() ) { if ( isEnumMapping() ) {
@ -435,7 +395,7 @@ public class SourceMethod implements Method {
// there can only be a mapping if there's only one entry for a source property, so: param.property. // there can only be a mapping if there's only one entry for a source property, so: param.property.
// There can be no mapping if there are more entries. So: param.property.property2 // There can be no mapping if there are more entries. So: param.property.property2
if ( sourceEntries.size() == 1 && sourcePropertyName.equals( sourceEntries.get( 0 ).getName() ) ) { if ( sourceEntries.size() == 1 && sourcePropertyName.equals( first( sourceEntries ).getName() ) ) {
mappingsOfSourceProperty.add( mapping ); mappingsOfSourceProperty.add( mapping );
} }
} }
@ -455,6 +415,45 @@ public class SourceMethod implements Method {
return null; return null;
} }
public List<SourceMethod> getApplicablePrototypeMethods() {
if ( applicablePrototypeMethods == null ) {
applicablePrototypeMethods = new ArrayList<SourceMethod>();
for ( SourceMethod prototype : prototypeMethods ) {
if ( canInheritFrom( prototype ) ) {
applicablePrototypeMethods.add( prototype );
}
}
}
return applicablePrototypeMethods;
}
private static boolean allParametersAreAssignable(List<Parameter> fromParams, List<Parameter> toParams) {
if ( fromParams.size() == toParams.size() ) {
Set<Parameter> unaccountedToParams = new HashSet<Parameter>( toParams );
for ( Parameter fromParam : fromParams ) {
// each fromParam needs at least one match, and all toParam need to be accounted for at the end
boolean hasMatch = false;
for ( Parameter toParam : toParams ) {
if ( fromParam.getType().isAssignableTo( toParam.getType() ) ) {
unaccountedToParams.remove( toParam );
hasMatch = true;
}
}
if ( !hasMatch ) {
return false;
}
}
return unaccountedToParams.isEmpty();
}
return false;
}
/** /**
* Whether an implementation of this method must be generated or not. * Whether an implementation of this method must be generated or not.
* *
@ -494,57 +493,8 @@ public class SourceMethod implements Method {
return exceptionTypes; return exceptionTypes;
} }
/** public MappingOptions getMappingOptions() {
* Merges in all the mappings configured via the given inverse mapping method, giving the locally defined mappings return mappingOptions;
* precedence.
* @param inverseMethod
* @param templateMethod
*/
public void mergeWithInverseMappings(SourceMethod inverseMethod, SourceMethod templateMethod) {
Map<String, List<Mapping>> newMappings = new HashMap<String, List<Mapping>>();
if ( inverseMethod != null && !inverseMethod.getMappings().isEmpty() ) {
for ( List<Mapping> lmappings : inverseMethod.getMappings().values() ) {
for ( Mapping inverseMapping : lmappings ) {
Mapping reversed = inverseMapping.reverse( this, messager, typeFactory );
if ( reversed != null ) {
List<Mapping> mappingsOfProperty = newMappings.get( reversed.getTargetName() );
if ( mappingsOfProperty == null ) {
mappingsOfProperty = new ArrayList<Mapping>();
newMappings.put( reversed.getTargetName(), mappingsOfProperty );
}
mappingsOfProperty.add( reversed );
}
}
}
}
if ( templateMethod != null && !templateMethod.getMappings().isEmpty() ) {
for ( List<Mapping> lmappings : templateMethod.getMappings().values() ) {
for ( Mapping templateMapping : lmappings ) {
if ( templateMapping != null ) {
List<Mapping> mappingsOfProperty = newMappings.get( templateMapping.getTargetName() );
if ( mappingsOfProperty == null ) {
mappingsOfProperty = new ArrayList<Mapping>();
newMappings.put( templateMapping.getTargetName(), mappingsOfProperty );
}
mappingsOfProperty.add( templateMapping );
}
}
}
}
if ( getMappings().isEmpty() ) {
// the mapping method is configuredByReverseMappingMethod, see SourceMethod#setMappings()
setMappings( newMappings );
}
else {
// now add all of its own mappings
newMappings.putAll( getMappings() );
getMappings().clear();
// the mapping method is NOT configuredByReverseMappingMethod,
getMappings().putAll( newMappings );
}
} }
@Override @Override
@ -552,17 +502,6 @@ public class SourceMethod implements Method {
return executable.getModifiers().contains( Modifier.STATIC ); return executable.getModifiers().contains( Modifier.STATIC );
} }
private void increase(Type key, Map<Type, Integer> test) {
Integer count = test.get( key );
count++;
test.put( key, count );
}
private void decrease(Type key, Map<Type, Integer> test) {
Integer count = test.get( key );
count--;
test.put( key, count );
}
/** /**
* *
* @return the mapper config when this method needs to be implemented * @return the mapper config when this method needs to be implemented

View File

@ -22,16 +22,18 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.model.common.Parameter; import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Executables; import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
import static org.mapstruct.ap.util.Collections.first;
/** /**
* This class describes the source side of a property mapping. * This class describes the source side of a property mapping.
* <p> * <p>
@ -287,4 +289,25 @@ public class SourceReference {
} }
} }
/**
* Creates a copy of this reference, which is adapted to the given method
*
* @param the method to adapt the copy to
*/
public SourceReference copyForInheritanceTo(SourceMethod method) {
List<Parameter> replacementParamCandidates = new ArrayList<Parameter>();
for ( Parameter sourceParam : method.getSourceParameters() ) {
if ( sourceParam.getType().isAssignableTo( parameter.getType() ) ) {
replacementParamCandidates.add( sourceParam );
}
}
Parameter replacement = parameter;
if ( replacementParamCandidates.size() == 1 ) {
replacement = first( replacementParamCandidates );
}
return new SourceReference( replacement, propertyEntries, isValid );
}
} }

View File

@ -25,6 +25,8 @@ import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
import static org.mapstruct.ap.util.Collections.first;
/** /**
* Selects on inheritance distance, e.g. the amount of inheritance steps from the parameter type. * Selects on inheritance distance, e.g. the amount of inheritance steps from the parameter type.
* *
@ -50,7 +52,7 @@ public class InheritanceSelector implements MethodSelector {
// find the methods with the minimum distance regarding getParameter getParameter type // find the methods with the minimum distance regarding getParameter getParameter type
for ( T method : methods ) { for ( T method : methods ) {
Parameter singleSourceParam = method.getSourceParameters().iterator().next(); Parameter singleSourceParam = first( method.getSourceParameters() );
int sourceTypeDistance = sourceType.distanceTo( singleSourceParam.getType() ); int sourceTypeDistance = sourceType.distanceTo( singleSourceParam.getType() );
bestMatchingSourceTypeDistance = bestMatchingSourceTypeDistance =

View File

@ -0,0 +1,31 @@
/**
* Copyright 2012-2015 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.prism;
/**
* Prism for the enum {@link org.mapstruct.MappingInheritanceStrategy}
*
* @author Andreas Gudian
*/
public enum MappingInheritanceStrategyPrism {
EXPLICIT,
AUTO_INHERIT_FROM_CONFIG,
DEFAULT;
}

View File

@ -24,7 +24,6 @@ import java.util.List;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
@ -46,6 +45,7 @@ import org.mapstruct.ap.model.MappingBuilderContext;
import org.mapstruct.ap.model.MappingMethod; import org.mapstruct.ap.model.MappingMethod;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.model.source.MappingOptions;
import org.mapstruct.ap.model.source.SourceMethod; import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.option.Options; import org.mapstruct.ap.option.Options;
import org.mapstruct.ap.prism.DecoratedWithPrism; import org.mapstruct.ap.prism.DecoratedWithPrism;
@ -53,11 +53,16 @@ import org.mapstruct.ap.prism.InheritConfigurationPrism;
import org.mapstruct.ap.prism.InheritInverseConfigurationPrism; import org.mapstruct.ap.prism.InheritInverseConfigurationPrism;
import org.mapstruct.ap.prism.MapperPrism; import org.mapstruct.ap.prism.MapperPrism;
import org.mapstruct.ap.processor.creation.MappingResolverImpl; import org.mapstruct.ap.processor.creation.MappingResolverImpl;
import org.mapstruct.ap.util.Message; import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.MapperConfig;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
import org.mapstruct.ap.version.VersionInformation; import org.mapstruct.ap.version.VersionInformation;
import static org.mapstruct.ap.prism.MappingInheritanceStrategyPrism.AUTO_INHERIT_FROM_CONFIG;
import static org.mapstruct.ap.util.Collections.first;
import static org.mapstruct.ap.util.Collections.join;
/** /**
* A {@link ModelElementProcessor} which creates a {@link Mapper} from the given * A {@link ModelElementProcessor} which creates a {@link Mapper} from the given
* list of {@link SourceMethod}s. * list of {@link SourceMethod}s.
@ -83,7 +88,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
this.versionInformation = context.getVersionInformation(); this.versionInformation = context.getVersionInformation();
this.typeFactory = context.getTypeFactory(); this.typeFactory = context.getTypeFactory();
List<MapperReference> mapperReferences = initReferencedMappers( mapperTypeElement ); MapperConfig mapperConfig = MapperConfig.getInstanceOn( mapperTypeElement );
List<MapperReference> mapperReferences = initReferencedMappers( mapperTypeElement, mapperConfig );
MappingBuilderContext ctx = new MappingBuilderContext( MappingBuilderContext ctx = new MappingBuilderContext(
typeFactory, typeFactory,
@ -104,7 +110,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
mapperReferences mapperReferences
); );
this.mappingContext = ctx; this.mappingContext = ctx;
return getMapper( mapperTypeElement, sourceModel ); return getMapper( mapperTypeElement, mapperConfig, sourceModel );
} }
@Override @Override
@ -112,13 +118,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
return 1000; return 1000;
} }
private List<MapperReference> initReferencedMappers(TypeElement element) { private List<MapperReference> initReferencedMappers(TypeElement element, MapperConfig mapperConfig) {
List<MapperReference> result = new LinkedList<MapperReference>(); List<MapperReference> result = new LinkedList<MapperReference>();
List<String> variableNames = new LinkedList<String>(); List<String> variableNames = new LinkedList<String>();
MapperConfig mapperPrism = MapperConfig.getInstanceOn( element ); for ( TypeMirror usedMapper : mapperConfig.uses() ) {
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
DefaultMapperReference mapperReference = DefaultMapperReference.getInstance( DefaultMapperReference mapperReference = DefaultMapperReference.getInstance(
typeFactory.getType( usedMapper ), typeFactory.getType( usedMapper ),
MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null, MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null,
@ -133,9 +137,9 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
return result; return result;
} }
private Mapper getMapper(TypeElement element, List<SourceMethod> methods) { private Mapper getMapper(TypeElement element, MapperConfig mapperConfig, List<SourceMethod> methods) {
List<MapperReference> mapperReferences = mappingContext.getMapperReferences(); List<MapperReference> mapperReferences = mappingContext.getMapperReferences();
List<MappingMethod> mappingMethods = getMappingMethods( methods ); List<MappingMethod> mappingMethods = getMappingMethods( mapperConfig, methods );
mappingMethods.addAll( mappingContext.getUsedVirtualMappings() ); mappingMethods.addAll( mappingContext.getUsedVirtualMappings() );
mappingMethods.addAll( mappingContext.getMappingsToGenerate() ); mappingMethods.addAll( mappingContext.getMappingsToGenerate() );
@ -194,7 +198,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
else if ( constructor.getParameters().size() == 1 ) { else if ( constructor.getParameters().size() == 1 ) {
if ( typeUtils.isAssignable( if ( typeUtils.isAssignable(
element.asType(), element.asType(),
constructor.getParameters().iterator().next().asType() first( constructor.getParameters() ).asType()
) ) { ) ) {
hasDelegateConstructor = true; hasDelegateConstructor = true;
} }
@ -233,7 +237,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
return extraImports; return extraImports;
} }
private List<MappingMethod> getMappingMethods(List<SourceMethod> methods) { private List<MappingMethod> getMappingMethods(MapperConfig mapperConfig, List<SourceMethod> methods) {
List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>(); List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>();
for ( SourceMethod method : methods ) { for ( SourceMethod method : methods ) {
@ -241,29 +245,23 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
continue; continue;
} }
SourceMethod inverseMappingMethod = getInverseMappingMethod( methods, method ); mergeInheritedOptions( method, mapperConfig, methods, new ArrayList<SourceMethod>() );
SourceMethod templateMappingMethod = getTemplateMappingMethod( methods, method );
MappingOptions mappingOptions = method.getMappingOptions();
boolean hasFactoryMethod = false; boolean hasFactoryMethod = false;
if ( method.isIterableMapping() ) { if ( method.isIterableMapping() ) {
IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder(); IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder();
if ( method.getIterableMapping() == null ) {
if ( inverseMappingMethod != null && inverseMappingMethod.getIterableMapping() != null ) {
method.setIterableMapping( inverseMappingMethod.getIterableMapping() );
}
else if ( templateMappingMethod != null && templateMappingMethod.getIterableMapping() != null ) {
method.setIterableMapping( templateMappingMethod.getIterableMapping() );
}
}
String dateFormat = null; String dateFormat = null;
List<TypeMirror> qualifiers = null; List<TypeMirror> qualifiers = null;
TypeMirror qualifyingElementTargetType = null; TypeMirror qualifyingElementTargetType = null;
if ( method.getIterableMapping() != null ) { if ( mappingOptions.getIterableMapping() != null ) {
dateFormat = method.getIterableMapping().getDateFormat(); dateFormat = mappingOptions.getIterableMapping().getDateFormat();
qualifiers = method.getIterableMapping().getQualifiers(); qualifiers = mappingOptions.getIterableMapping().getQualifiers();
qualifyingElementTargetType = method.getIterableMapping().getQualifyingElementTargetType(); qualifyingElementTargetType = mappingOptions.getIterableMapping().getQualifyingElementTargetType();
} }
IterableMappingMethod iterableMappingMethod = builder IterableMappingMethod iterableMappingMethod = builder
@ -281,27 +279,19 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
MapMappingMethod.Builder builder = new MapMappingMethod.Builder(); MapMappingMethod.Builder builder = new MapMappingMethod.Builder();
if ( method.getMapMapping() == null ) {
if ( inverseMappingMethod != null && inverseMappingMethod.getMapMapping() != null ) {
method.setMapMapping( inverseMappingMethod.getMapMapping() );
}
else if ( templateMappingMethod != null && templateMappingMethod.getMapMapping() != null ) {
method.setMapMapping( templateMappingMethod.getMapMapping() );
}
}
String keyDateFormat = null; String keyDateFormat = null;
String valueDateFormat = null; String valueDateFormat = null;
List<TypeMirror> keyQualifiers = null; List<TypeMirror> keyQualifiers = null;
List<TypeMirror> valueQualifiers = null; List<TypeMirror> valueQualifiers = null;
TypeMirror keyQualifyingTargetType = null; TypeMirror keyQualifyingTargetType = null;
TypeMirror valueQualifyingTargetType = null; TypeMirror valueQualifyingTargetType = null;
if ( method.getMapMapping() != null ) { if ( mappingOptions.getMapMapping() != null ) {
keyDateFormat = method.getMapMapping().getKeyFormat(); keyDateFormat = mappingOptions.getMapMapping().getKeyFormat();
valueDateFormat = method.getMapMapping().getValueFormat(); valueDateFormat = mappingOptions.getMapMapping().getValueFormat();
keyQualifiers = method.getMapMapping().getKeyQualifiers(); keyQualifiers = mappingOptions.getMapMapping().getKeyQualifiers();
valueQualifiers = method.getMapMapping().getValueQualifiers(); valueQualifiers = mappingOptions.getMapMapping().getValueQualifiers();
keyQualifyingTargetType = method.getMapMapping().getKeyQualifyingTargetType(); keyQualifyingTargetType = mappingOptions.getMapMapping().getKeyQualifyingTargetType();
valueQualifyingTargetType = method.getMapMapping().getValueQualifyingTargetType(); valueQualifyingTargetType = mappingOptions.getMapMapping().getValueQualifyingTargetType();
} }
MapMappingMethod mapMappingMethod = builder MapMappingMethod mapMappingMethod = builder
@ -321,7 +311,6 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
else if ( method.isEnumMapping() ) { else if ( method.isEnumMapping() ) {
EnumMappingMethod.Builder builder = new EnumMappingMethod.Builder(); EnumMappingMethod.Builder builder = new EnumMappingMethod.Builder();
method.mergeWithInverseMappings( inverseMappingMethod, templateMappingMethod );
MappingMethod enumMappingMethod = builder MappingMethod enumMappingMethod = builder
.mappingContext( mappingContext ) .mappingContext( mappingContext )
.souceMethod( method ) .souceMethod( method )
@ -334,7 +323,6 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
else { else {
BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder(); BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder();
method.mergeWithInverseMappings( inverseMappingMethod, templateMappingMethod );
BeanMappingMethod beanMappingMethod = builder BeanMappingMethod beanMappingMethod = builder
.mappingContext( mappingContext ) .mappingContext( mappingContext )
.souceMethod( method ) .souceMethod( method )
@ -356,6 +344,61 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
return mappingMethods; return mappingMethods;
} }
private void mergeInheritedOptions(SourceMethod method, MapperConfig mapperConfig,
List<SourceMethod> availableMethods, List<SourceMethod> initializingMethods) {
if ( initializingMethods.contains( method ) ) {
// cycle detected
initializingMethods.add( method );
messager.printMessage(
method.getExecutable(),
Message.INHERITCONFIGURATION_CYCLE,
Strings.join( initializingMethods, " -> " ) );
return;
}
initializingMethods.add( method );
MappingOptions mappingOptions = method.getMappingOptions();
List<SourceMethod> applicablePrototypeMethods = method.getApplicablePrototypeMethods();
MappingOptions inverseMappingOptions =
getInverseMappingOptions( availableMethods, method, initializingMethods, mapperConfig );
MappingOptions templateMappingOptions =
getTemplateMappingOptions(
join( availableMethods, applicablePrototypeMethods ),
method,
initializingMethods,
mapperConfig );
if ( templateMappingOptions != null ) {
mappingOptions.applyInheritedOptions( templateMappingOptions, false, method, messager, typeFactory );
}
else if ( inverseMappingOptions != null ) {
mappingOptions.applyInheritedOptions( inverseMappingOptions, true, method, messager, typeFactory );
}
else if ( mapperConfig.getMappingInheritanceStrategy() == AUTO_INHERIT_FROM_CONFIG ) {
if ( applicablePrototypeMethods.size() == 1 ) {
mappingOptions.applyInheritedOptions(
first( applicablePrototypeMethods ).getMappingOptions(),
false,
method,
messager,
typeFactory );
}
else if ( applicablePrototypeMethods.size() > 1 ) {
messager.printMessage(
method.getExecutable(),
Message.INHERITCONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH,
Strings.join( applicablePrototypeMethods, ", " ) );
}
}
mappingOptions.markAsFullyInitialized();
}
private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType(SourceMethod method) { private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType(SourceMethod method) {
if ( method.getReturnType().getTypeMirror().getKind() != TypeKind.VOID && if ( method.getReturnType().getTypeMirror().getKind() != TypeKind.VOID &&
method.getReturnType().isInterface() && method.getReturnType().isInterface() &&
@ -365,12 +408,13 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
} }
/** /**
* Returns the configuring inverse method in case the given method is annotated with * Returns the configuring inverse method's options in case the given method is annotated with
* {@code @InheritInverseConfiguration} and exactly one such configuring method can unambiguously be selected (as * {@code @InheritInverseConfiguration} and exactly one such configuring method can unambiguously be selected (as
* per the source/target type and optionally the name given via {@code @InheritInverseConfiguration}). * per the source/target type and optionally the name given via {@code @InheritInverseConfiguration}).
*/ */
private SourceMethod getInverseMappingMethod(List<SourceMethod> rawMethods, SourceMethod method) { private MappingOptions getInverseMappingOptions(List<SourceMethod> rawMethods, SourceMethod method,
SourceMethod result = null; List<SourceMethod> initializingMethods, MapperConfig mapperConfig) {
SourceMethod resultMethod = null;
InheritInverseConfigurationPrism reversePrism = InheritInverseConfigurationPrism.getInstanceOn( InheritInverseConfigurationPrism reversePrism = InheritInverseConfigurationPrism.getInstanceOn(
method.getExecutable() method.getExecutable()
); );
@ -389,10 +433,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
if ( candidates.size() == 1 ) { if ( candidates.size() == 1 ) {
// no ambiguity: if no configuredBy is specified, or configuredBy specified and match // no ambiguity: if no configuredBy is specified, or configuredBy specified and match
if ( name.isEmpty() ) { if ( name.isEmpty() ) {
result = candidates.get( 0 ); resultMethod = candidates.get( 0 );
} }
else if ( candidates.get( 0 ).getName().equals( name ) ) { else if ( candidates.get( 0 ).getName().equals( name ) ) {
result = candidates.get( 0 ); resultMethod = candidates.get( 0 );
} }
else { else {
reportErrorWhenNonMatchingName( candidates.get( 0 ), method, reversePrism ); reportErrorWhenNonMatchingName( candidates.get( 0 ), method, reversePrism );
@ -409,35 +453,46 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
} }
if ( nameFilteredcandidates.size() == 1 ) { if ( nameFilteredcandidates.size() == 1 ) {
result = nameFilteredcandidates.get( 0 ); resultMethod = nameFilteredcandidates.get( 0 );
} }
else if ( nameFilteredcandidates.size() > 1 ) { else if ( nameFilteredcandidates.size() > 1 ) {
reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, reversePrism ); reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, reversePrism );
} }
if ( result == null ) { if ( resultMethod == null ) {
reportErrorWhenAmbigousReverseMapping( candidates, method, reversePrism ); reportErrorWhenAmbigousReverseMapping( candidates, method, reversePrism );
} }
} }
if ( result != null ) {
reportErrorIfInverseMethodHasInheritAnnotation( result, method, reversePrism );
}
} }
return result;
return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods );
} }
/** private MappingOptions extractInitializedOptions(SourceMethod resultMethod,
* Returns the configuring forward method in case the given method is annotated with List<SourceMethod> rawMethods,
* {@code @InheritConfiguration} and exactly one such configuring method can unambiguously be selected (as MapperConfig mapperConfig,
* per the source/target type and optionally the name given via {@code @InheritConfiguration}). List<SourceMethod> initializingMethods) {
* if ( resultMethod != null ) {
* The method cannot be marked forward mapping itself (hence 'ohter'). And neither can it contain an if ( !resultMethod.getMappingOptions().isFullyInitialized() ) {
* {@code @InheritReverseConfiguration} mergeInheritedOptions( resultMethod, mapperConfig, rawMethods, initializingMethods );
}
return resultMethod.getMappingOptions();
}
return null;
}
/**
* Returns the configuring forward method's options in case the given method is annotated with
* {@code @InheritConfiguration} and exactly one such configuring method can unambiguously be selected (as per the
* source/target type and optionally the name given via {@code @InheritConfiguration}). The method cannot be marked
* forward mapping itself (hence 'ohter'). And neither can it contain an {@code @InheritReverseConfiguration}
*/ */
private SourceMethod getTemplateMappingMethod(List<SourceMethod> rawMethods, SourceMethod method) { private MappingOptions getTemplateMappingOptions(List<SourceMethod> rawMethods, SourceMethod method,
SourceMethod result = null; List<SourceMethod> initializingMethods,
MapperConfig mapperConfig) {
SourceMethod resultMethod = null;
InheritConfigurationPrism forwardPrism = InheritConfigurationPrism.getInstanceOn( InheritConfigurationPrism forwardPrism = InheritConfigurationPrism.getInstanceOn(
method.getExecutable() method.getExecutable()
); );
@ -445,11 +500,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
if (forwardPrism != null) { if (forwardPrism != null) {
reportErrorWhenInheritForwardAlsoHasInheritReverseMapping( method ); reportErrorWhenInheritForwardAlsoHasInheritReverseMapping( method );
// method is configured as being reverse method, collect candidates
List<SourceMethod> candidates = new ArrayList<SourceMethod>(); List<SourceMethod> candidates = new ArrayList<SourceMethod>();
for ( SourceMethod oneMethod : rawMethods ) { for ( SourceMethod oneMethod : rawMethods ) {
// method must be similar but not equal // method must be similar but not equal
if ( oneMethod.isSimilar( method ) && !( oneMethod.equals( method ) ) ) { if ( method.canInheritFrom( oneMethod ) && !( oneMethod.equals( method ) ) ) {
candidates.add( oneMethod ); candidates.add( oneMethod );
} }
} }
@ -457,14 +511,15 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
String name = forwardPrism.name(); String name = forwardPrism.name();
if ( candidates.size() == 1 ) { if ( candidates.size() == 1 ) {
// no ambiguity: if no configuredBy is specified, or configuredBy specified and match // no ambiguity: if no configuredBy is specified, or configuredBy specified and match
SourceMethod sourceMethod = first( candidates );
if ( name.isEmpty() ) { if ( name.isEmpty() ) {
result = candidates.get( 0 ); resultMethod = sourceMethod;
} }
else if ( candidates.get( 0 ).getName().equals( name ) ) { else if ( sourceMethod.getName().equals( name ) ) {
result = candidates.get( 0 ); resultMethod = sourceMethod;
} }
else { else {
reportErrorWhenNonMatchingName( candidates.get( 0 ), method, forwardPrism ); reportErrorWhenNonMatchingName( sourceMethod, method, forwardPrism );
} }
} }
else if ( candidates.size() > 1 ) { else if ( candidates.size() > 1 ) {
@ -478,23 +533,19 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
} }
if ( nameFilteredcandidates.size() == 1 ) { if ( nameFilteredcandidates.size() == 1 ) {
result = nameFilteredcandidates.get( 0 ); resultMethod = first( nameFilteredcandidates );
} }
else if ( nameFilteredcandidates.size() > 1 ) { else if ( nameFilteredcandidates.size() > 1 ) {
reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, forwardPrism ); reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, forwardPrism );
} }
if ( result == null ) { if ( resultMethod == null ) {
reportErrorWhenAmbigousMapping( candidates, method, forwardPrism ); reportErrorWhenAmbigousMapping( candidates, method, forwardPrism );
} }
} }
if ( result != null ) {
reportErrorIfMethodHasAnnotation( result, method, forwardPrism );
}
} }
return result;
return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods );
} }
private void reportErrorWhenInheritForwardAlsoHasInheritReverseMapping(SourceMethod method) { private void reportErrorWhenInheritForwardAlsoHasInheritReverseMapping(SourceMethod method) {
@ -507,34 +558,6 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
} }
private void reportErrorIfInverseMethodHasInheritAnnotation(SourceMethod candidate,
SourceMethod method,
InheritInverseConfigurationPrism reversePrism) {
InheritInverseConfigurationPrism candidatePrism = InheritInverseConfigurationPrism.getInstanceOn(
candidate.getExecutable()
);
if ( candidatePrism != null ) {
messager.printMessage( method.getExecutable(),
reversePrism.mirror,
Message.INHERITINVERSECONFIGURATION_REFERENCE_HAS_INVERSE,
candidate.getName()
);
}
InheritConfigurationPrism candidateTemplatePrism = InheritConfigurationPrism.getInstanceOn(
candidate.getExecutable()
);
if ( candidateTemplatePrism != null ) {
messager.printMessage( method.getExecutable(),
reversePrism.mirror,
Message.INHERITINVERSECONFIGURATION_REFERENCE_HAS_FORWARD,
candidate.getName()
);
}
}
private void reportErrorWhenAmbigousReverseMapping(List<SourceMethod> candidates, SourceMethod method, private void reportErrorWhenAmbigousReverseMapping(List<SourceMethod> candidates, SourceMethod method,
InheritInverseConfigurationPrism reversePrism) { InheritInverseConfigurationPrism reversePrism) {
@ -570,7 +593,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
reversePrism.mirror, reversePrism.mirror,
Message.INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES, Message.INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES,
reversePrism.name(), reversePrism.name(),
Strings.join( candidates, "(), " ) Strings.join( candidates, ", " )
); );
} }
@ -586,35 +609,6 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
); );
} }
private void reportErrorIfMethodHasAnnotation(SourceMethod candidate,
SourceMethod method,
InheritConfigurationPrism prism) {
InheritConfigurationPrism candidatePrism = InheritConfigurationPrism.getInstanceOn(
candidate.getExecutable()
);
if ( candidatePrism != null ) {
messager.printMessage( method.getExecutable(),
prism.mirror,
Message.INHERITCONFIGURATION_REFERENCE_HAS_FORWARD,
candidate.getName()
);
}
InheritInverseConfigurationPrism candidateInversePrism = InheritInverseConfigurationPrism.getInstanceOn(
candidate.getExecutable()
);
if ( candidateInversePrism != null ) {
messager.printMessage( method.getExecutable(),
prism.mirror,
Message.INHERITCONFIGURATION_REFERENCE_HAS_INVERSE,
candidate.getName()
);
}
}
private void reportErrorWhenAmbigousMapping(List<SourceMethod> candidates, SourceMethod method, private void reportErrorWhenAmbigousMapping(List<SourceMethod> candidates, SourceMethod method,
InheritConfigurationPrism prism) { InheritConfigurationPrism prism) {

View File

@ -18,14 +18,12 @@
*/ */
package org.mapstruct.ap.processor; package org.mapstruct.ap.processor;
import static org.mapstruct.ap.util.Executables.getAllEnclosedExecutableElements;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
@ -48,8 +46,11 @@ import org.mapstruct.ap.prism.MapMappingPrism;
import org.mapstruct.ap.prism.MappingPrism; import org.mapstruct.ap.prism.MappingPrism;
import org.mapstruct.ap.prism.MappingsPrism; import org.mapstruct.ap.prism.MappingsPrism;
import org.mapstruct.ap.util.AnnotationProcessingException; import org.mapstruct.ap.util.AnnotationProcessingException;
import org.mapstruct.ap.util.Message; import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.MapperConfig;
import org.mapstruct.ap.util.Message;
import static org.mapstruct.ap.util.Executables.getAllEnclosedExecutableElements;
/** /**
* A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s * A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s
@ -72,7 +73,19 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
this.typeFactory = context.getTypeFactory(); this.typeFactory = context.getTypeFactory();
this.typeUtils = context.getTypeUtils(); this.typeUtils = context.getTypeUtils();
this.elementUtils = context.getElementUtils(); this.elementUtils = context.getElementUtils();
return retrieveMethods( mapperTypeElement, mapperTypeElement );
MapperConfig mapperConfig = MapperConfig.getInstanceOn( mapperTypeElement );
if ( !mapperConfig.isValid() ) {
throw new AnnotationProcessingException(
"Couldn't retrieve @Mapper annotation",
mapperTypeElement,
mapperConfig.getAnnotationMirror() );
}
List<SourceMethod> prototypeMethods =
retrievePrototypeMethods( mapperConfig.getMapperConfigMirror(), mapperConfig );
return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperConfig, prototypeMethods );
} }
@Override @Override
@ -80,19 +93,60 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return 1; return 1;
} }
private List<SourceMethod> retrievePrototypeMethods(TypeMirror typeMirror, MapperConfig mapperConfig) {
if ( typeMirror == null || typeMirror.getKind() == TypeKind.VOID ) {
return Collections.emptyList();
}
TypeElement typeElement = asTypeElement( typeMirror );
List<SourceMethod> methods = new ArrayList<SourceMethod>();
for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, typeElement ) ) {
ExecutableType methodType = typeFactory.getMethodType( typeElement, executable );
List<Parameter> parameters = typeFactory.getParameters( methodType, executable );
boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters );
// prototype methods don't have prototypes themselves
List<SourceMethod> prototypeMethods = Collections.emptyList();
SourceMethod method =
getMethodRequiringImplementation(
methodType,
executable,
parameters,
containsTargetTypeParameter,
mapperConfig,
prototypeMethods );
if ( method != null ) {
methods.add( method );
}
}
return methods;
}
/** /**
* Retrieves the mapping methods declared by the given mapper type. * Retrieves the mapping methods declared by the given mapper type.
* *
* @param usedMapper The type of interest (either the mapper to implement or a used mapper via @uses annotation) * @param usedMapper The type of interest (either the mapper to implement or a used mapper via @uses annotation)
* @param mapperToImplement the top level type (mapper) that requires implementation * @param mapperToImplement the top level type (mapper) that requires implementation
* * @param mapperConfig the mapper config
* @param prototypeMethods prototype methods defined in mapper config type
* @return All mapping methods declared by the given type * @return All mapping methods declared by the given type
*/ */
private List<SourceMethod> retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement) { private List<SourceMethod> retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement,
MapperConfig mapperConfig, List<SourceMethod> prototypeMethods) {
List<SourceMethod> methods = new ArrayList<SourceMethod>(); List<SourceMethod> methods = new ArrayList<SourceMethod>();
for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, usedMapper ) ) { for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, usedMapper ) ) {
SourceMethod method = getMethod( usedMapper, executable, mapperToImplement ); SourceMethod method = getMethod(
usedMapper,
executable,
mapperToImplement,
mapperConfig,
prototypeMethods );
if ( method != null ) { if ( method != null ) {
methods.add( method ); methods.add( method );
} }
@ -100,15 +154,12 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
//Add all methods of used mappers in order to reference them in the aggregated model //Add all methods of used mappers in order to reference them in the aggregated model
if ( usedMapper.equals( mapperToImplement ) ) { if ( usedMapper.equals( mapperToImplement ) ) {
MapperConfig mapperSettings = MapperConfig.getInstanceOn( usedMapper ); for ( TypeMirror mapper : mapperConfig.uses() ) {
if ( !mapperSettings.isValid() ) { methods.addAll( retrieveMethods(
throw new AnnotationProcessingException( asTypeElement( mapper ),
"Couldn't retrieve @Mapper annotation", usedMapper, mapperSettings.getAnnotationMirror() mapperToImplement,
); mapperConfig,
} prototypeMethods ) );
for ( TypeMirror mapper : mapperSettings.uses() ) {
methods.addAll( retrieveMethods( asTypeElement( mapper ), mapperToImplement ) );
} }
} }
@ -121,7 +172,9 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
private SourceMethod getMethod(TypeElement usedMapper, private SourceMethod getMethod(TypeElement usedMapper,
ExecutableElement method, ExecutableElement method,
TypeElement mapperToImplement) { TypeElement mapperToImplement,
MapperConfig mapperConfig,
List<SourceMethod> prototypeMethods) {
ExecutableType methodType = typeFactory.getMethodType( usedMapper, method ); ExecutableType methodType = typeFactory.getMethodType( usedMapper, method );
List<Parameter> parameters = typeFactory.getParameters( methodType, method ); List<Parameter> parameters = typeFactory.getParameters( methodType, method );
@ -134,7 +187,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
method, method,
parameters, parameters,
containsTargetTypeParameter, containsTargetTypeParameter,
mapperToImplement ); mapperConfig,
prototypeMethods );
} }
//otherwise add reference to existing mapper method //otherwise add reference to existing mapper method
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters ) ) { else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters ) ) {
@ -148,7 +202,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, ExecutableElement method, private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, ExecutableElement method,
List<Parameter> parameters, List<Parameter> parameters,
boolean containsTargetTypeParameter, boolean containsTargetTypeParameter,
TypeElement mapperToImplement) { MapperConfig mapperConfig,
List<SourceMethod> prototypeMethods) {
Type returnType = typeFactory.getReturnType( methodType ); Type returnType = typeFactory.getReturnType( methodType );
List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType ); List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType );
List<Parameter> sourceParameters = extractSourceParameters( parameters ); List<Parameter> sourceParameters = extractSourceParameters( parameters );
@ -181,8 +236,9 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
.setTypeUtils( typeUtils ) .setTypeUtils( typeUtils )
.setMessager( messager ) .setMessager( messager )
.setTypeFactory( typeFactory ) .setTypeFactory( typeFactory )
.setMapperConfig( MapperConfig.getInstanceOn( mapperToImplement ) ) .setMapperConfig( mapperConfig )
.createSourceMethod(); .setPrototypeMethods( prototypeMethods )
.buildSourceMethod();
} }
private SourceMethod getReferencedMethod(TypeElement usedMapper, ExecutableType methodType, private SourceMethod getReferencedMethod(TypeElement usedMapper, ExecutableType methodType,
@ -205,7 +261,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
.setExceptionTypes( exceptionTypes ) .setExceptionTypes( exceptionTypes )
.setTypeUtils( typeUtils ) .setTypeUtils( typeUtils )
.setTypeFactory( typeFactory ) .setTypeFactory( typeFactory )
.createSourceMethod(); .buildSourceMethod();
} }
private boolean isValidReferencedMethod(List<Parameter> parameters) { private boolean isValidReferencedMethod(List<Parameter> parameters) {

View File

@ -72,4 +72,17 @@ public class Collections {
return set; return set;
} }
public static <T> T first(Collection<T> collection) {
return collection.iterator().next();
}
public static <T> List<T> join(List<T> a, List<T> b) {
List<T> result = new ArrayList<T>( a.size() + b.size() );
result.addAll( a );
result.addAll( b );
return result;
}
} }

View File

@ -33,11 +33,10 @@ import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.MapperConfigPrism; import org.mapstruct.ap.prism.MapperConfigPrism;
import org.mapstruct.ap.prism.MapperPrism; import org.mapstruct.ap.prism.MapperPrism;
import org.mapstruct.ap.prism.MappingInheritanceStrategyPrism;
import org.mapstruct.ap.prism.NullValueMappingPrism; import org.mapstruct.ap.prism.NullValueMappingPrism;
import org.mapstruct.ap.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.prism.NullValueMappingStrategyPrism;
import static org.mapstruct.ap.prism.CollectionMappingStrategyPrism.valueOf;
/** /**
* Class decorating the {@link MapperPrism} with the 'default' configuration. * Class decorating the {@link MapperPrism} with the 'default' configuration.
* *
@ -58,10 +57,6 @@ public class MapperConfig {
return new MapperConfig( MapperPrism.getInstanceOn( e ) ); return new MapperConfig( MapperPrism.getInstanceOn( e ) );
} }
public static MapperConfig getInstance(AnnotationMirror mirror) {
return new MapperConfig( MapperPrism.getInstance( mirror ) );
}
private MapperConfig(MapperPrism mapperPrism) { private MapperConfig(MapperPrism mapperPrism) {
this.mapperPrism = mapperPrism; this.mapperPrism = mapperPrism;
TypeMirror typeMirror = mapperPrism.config(); TypeMirror typeMirror = mapperPrism.config();
@ -100,7 +95,8 @@ public class MapperConfig {
} }
public CollectionMappingStrategyPrism getCollectionMappingStrategy() { public CollectionMappingStrategyPrism getCollectionMappingStrategy() {
CollectionMappingStrategyPrism mapperPolicy = valueOf( mapperPrism.collectionMappingStrategy() ); CollectionMappingStrategyPrism mapperPolicy =
CollectionMappingStrategyPrism.valueOf( mapperPrism.collectionMappingStrategy() );
if ( mapperPolicy != CollectionMappingStrategyPrism.DEFAULT ) { if ( mapperPolicy != CollectionMappingStrategyPrism.DEFAULT ) {
// it is not the default mapper configuration, so return the mapper configured value // it is not the default mapper configuration, so return the mapper configured value
@ -108,7 +104,8 @@ public class MapperConfig {
} }
else if ( mapperConfigPrism != null ) { else if ( mapperConfigPrism != null ) {
// try the config mapper configuration // try the config mapper configuration
CollectionMappingStrategyPrism configPolicy = valueOf( mapperConfigPrism.collectionMappingStrategy() ); CollectionMappingStrategyPrism configPolicy =
CollectionMappingStrategyPrism.valueOf( mapperConfigPrism.collectionMappingStrategy() );
if ( configPolicy != CollectionMappingStrategyPrism.DEFAULT ) { if ( configPolicy != CollectionMappingStrategyPrism.DEFAULT ) {
// its not the default configuration, so return the mapper config configured value // its not the default configuration, so return the mapper config configured value
return configPolicy; return configPolicy;
@ -118,6 +115,24 @@ public class MapperConfig {
return CollectionMappingStrategyPrism.ACCESSOR_ONLY; return CollectionMappingStrategyPrism.ACCESSOR_ONLY;
} }
public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() {
MappingInheritanceStrategyPrism mapperPolicy =
MappingInheritanceStrategyPrism.valueOf( mapperPrism.mappingInheritanceStrategy() );
if ( mapperPolicy != MappingInheritanceStrategyPrism.DEFAULT ) {
return mapperPolicy;
}
else if ( mapperConfigPrism != null ) {
MappingInheritanceStrategyPrism configPolicy =
MappingInheritanceStrategyPrism.valueOf( mapperConfigPrism.mappingInheritanceStrategy() );
if ( configPolicy != MappingInheritanceStrategyPrism.DEFAULT ) {
return configPolicy;
}
}
return MappingInheritanceStrategyPrism.EXPLICIT;
}
public boolean isMapToDefault(NullValueMappingPrism mapNullToDefault) { public boolean isMapToDefault(NullValueMappingPrism mapNullToDefault) {
// check on method level // check on method level
@ -165,6 +180,10 @@ public class MapperConfig {
} }
} }
public TypeMirror getMapperConfigMirror() {
return mapperPrism.config();
}
public boolean isValid() { public boolean isValid() {
return mapperPrism.isValid; return mapperPrism.isValid;
} }

View File

@ -86,18 +86,16 @@ public enum Message {
RETRIEVAL_NON_ENUM_TO_ENUM( "Can't generate mapping method from non-enum type to enum type." ), RETRIEVAL_NON_ENUM_TO_ENUM( "Can't generate mapping method from non-enum type to enum type." ),
INHERITCONFIGURATION_BOTH( "Method cannot be annotated with both a @InheritConfiguration and @InheritInverseConfiguration." ), INHERITCONFIGURATION_BOTH( "Method cannot be annotated with both a @InheritConfiguration and @InheritInverseConfiguration." ),
INHERITINVERSECONFIGURATION_REFERENCE_HAS_INVERSE( "Resolved inverse mapping method %s() should not carry the @InheritInverseConfiguration annotation itself." ),
INHERITINVERSECONFIGURATION_REFERENCE_HAS_FORWARD( "Resolved inverse mapping method %s() should not carry the @InheritConfiguration annotation." ),
INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ), INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ),
INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ), INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ),
INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ), INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ),
INHERITINVERSECONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ), INHERITINVERSECONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ),
INHERITCONFIGURATION_REFERENCE_HAS_FORWARD( "Resolved mapping method %s() should not carry the @InheritConfiguration annotation itself." ),
INHERITCONFIGURATION_REFERENCE_HAS_INVERSE( "Resolved mapping method %s() should not carry the @InheritInverseConfiguration annotation." ),
INHERITCONFIGURATION_DUPLICATES( "Several matching methods exist: %s(). Specify a name explicitly." ), INHERITCONFIGURATION_DUPLICATES( "Several matching methods exist: %s(). Specify a name explicitly." ),
INHERITCONFIGURATION_INVALIDNAME( "None of the candidates %s() matches given name: \"%s\"." ), INHERITCONFIGURATION_INVALIDNAME( "None of the candidates %s() matches given name: \"%s\"." ),
INHERITCONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ), INHERITCONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ),
INHERITCONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ); INHERITCONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ),
INHERITCONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH( "More than one configuration prototype method is applicable. Use @InheritConfiguration to select one of them explicitly: %s." ),
INHERITCONFIGURATION_CYCLE( "Cycle detected while evaluating inherited configurations. Inheritance path: %s" );
// CHECKSTYLE:ON // CHECKSTYLE:ON
private final String description; private final String description;

View File

@ -0,0 +1,40 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
import org.mapstruct.MappingInheritanceStrategy;
import org.mapstruct.Mappings;
import org.mapstruct.ReportingPolicy;
/**
* @author Andreas Gudian
*/
@MapperConfig(
mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG,
unmappedTargetPolicy = ReportingPolicy.ERROR
)
public interface AutoInheritedConfig {
@Mappings( {
@Mapping( target = "primaryKey", source = "id" ),
@Mapping( target = "auditTrail", ignore = true )
} )
BaseVehicleEntity baseDtoToEntity(BaseVehicleDto dto);
}

View File

@ -0,0 +1,41 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
import org.mapstruct.MappingInheritanceStrategy;
import org.mapstruct.Mappings;
import org.mapstruct.ReportingPolicy;
/**
* @author Andreas Gudian
*/
@MapperConfig(
mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG,
unmappedTargetPolicy = ReportingPolicy.ERROR
)
public interface AutoInheritedDriverConfig {
@Mappings( {
@Mapping( target = "primaryKey", source = "dto.id" ),
@Mapping( target = "auditTrail", ignore = true ),
@Mapping( target = "driverName", source = "drv.name" )
} )
CarWithDriverEntity baseDtoToEntity(DriverDto drv, BaseVehicleDto dto);
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
/**
* @author Andreas Gudian
*
*/
public abstract class BaseVehicleDto {
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}

View File

@ -0,0 +1,44 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
/**
* @author Andreas Gudian
*
*/
public abstract class BaseVehicleEntity {
private long primaryKey;
private String auditTrail;
public long getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(long primaryKey) {
this.primaryKey = primaryKey;
}
public String getAuditTrail() {
return auditTrail;
}
public void setAuditTrail(String auditTrail) {
this.auditTrail = auditTrail;
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
/**
* @author Andreas Gudian
*
*/
public class CarDto extends BaseVehicleDto {
private String colour;
public String getColour() {
return colour;
}
public void setColour(String colour) {
this.colour = colour;
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
/**
* @author Andreas Gudian
*
*/
public class CarEntity extends BaseVehicleEntity {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.test.template; package org.mapstruct.ap.test.inheritfromconfig;
import org.mapstruct.InheritConfiguration; import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.InheritInverseConfiguration;
@ -27,28 +27,30 @@ import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
/** /**
* @author Sjaak Derksen * @author Andreas Gudian
*
*/ */
@Mapper @Mapper(
public interface SourceTargetMapperErroneouslyAnnotated2 { config = AutoInheritedConfig.class
)
public interface CarMapperWithAutoInheritance {
CarMapperWithAutoInheritance INSTANCE = Mappers.getMapper( CarMapperWithAutoInheritance.class );
SourceTargetMapperErroneouslyAnnotated2 INSTANCE = @Mapping( target = "color", source = "colour" )
Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated2.class ); CarEntity toCarEntity(CarDto carDto);
@Mappings({ @InheritInverseConfiguration( name = "toCarEntity" )
@Mapping(target = "stringPropY", source = "stringPropX"), CarDto toCarDto(CarEntity entity);
@Mapping(target = "integerPropY", source = "integerPropX"),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate(Source source);
@InheritInverseConfiguration(name = "forwardCreate") @Mappings( {
@Mapping(target = "nestedSourceProp", ignore = true) @Mapping( target = "color", source = "colour" ),
Source forwardCreate(Target source); @Mapping( target = "auditTrail", constant = "fixed" )
} )
CarEntity toCarEntityWithFixedAuditTrail(CarDto carDto);
@InheritConfiguration( name = "forwardCreate" ) @Mapping( target = "color", source = "colour" )
void forwardUpdate(Target source, @MappingTarget Source target); void intoCarEntityOnItsOwn(CarDto carDto, @MappingTarget CarEntity entity);
@InheritConfiguration( name = "toCarEntity" )
void intoCarEntity(CarDto carDto, @MappingTarget CarEntity entity);
} }

View File

@ -0,0 +1,49 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingInheritanceStrategy;
import org.mapstruct.factory.Mappers;
/**
* @author Andreas Gudian
*
*/
@Mapper(
config = AutoInheritedConfig.class,
mappingInheritanceStrategy = MappingInheritanceStrategy.EXPLICIT
)
public interface CarMapperWithExplicitInheritance {
CarMapperWithExplicitInheritance INSTANCE = Mappers.getMapper( CarMapperWithExplicitInheritance.class );
@InheritConfiguration( name = "baseDtoToEntity" )
@Mapping( target = "color", source = "colour" )
CarEntity toCarEntity(CarDto carDto);
@InheritInverseConfiguration( name = "toCarEntity" )
CarDto toCarDto(CarEntity entity);
@InheritConfiguration( name = "toCarEntity" )
@Mapping( target = "auditTrail", constant = "fixed" )
CarEntity toCarEntityWithFixedAuditTrail(CarDto carDto);
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
/**
* @author Andreas Gudian
*
*/
public class CarWithDriverEntity extends CarEntity {
private String driverName;
public String getDriverName() {
return driverName;
}
public void setDriverName(String driverName) {
this.driverName = driverName;
}
}

View File

@ -0,0 +1,37 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author Andreas Gudian
*
*/
@Mapper(
config = AutoInheritedDriverConfig.class
)
public interface CarWithDriverMapperWithAutoInheritance {
CarWithDriverMapperWithAutoInheritance INSTANCE = Mappers.getMapper( CarWithDriverMapperWithAutoInheritance.class );
@Mapping( target = "color", source = "carDto.colour" )
CarWithDriverEntity toCarWithDriverEntity(CarDto carDto, DriverDto driverDto);
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
/**
* @author Andreas Gudian
*
*/
public class DriverDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,45 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
import org.mapstruct.MappingInheritanceStrategy;
import org.mapstruct.Mappings;
import org.mapstruct.ReportingPolicy;
/**
* Leads to ambiguous prototype methods error.
*
* @author Andreas Gudian
*/
@MapperConfig(
mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG,
unmappedTargetPolicy = ReportingPolicy.WARN
)
public interface Erroneous1Config {
@Mappings( {
@Mapping( target = "primaryKey", source = "id" ),
@Mapping( target = "auditTrail", ignore = true )
} )
BaseVehicleEntity baseDtoToEntity(BaseVehicleDto dto);
@Mapping( target = "primaryKey", ignore = true )
BaseVehicleEntity anythingToEntity(Object anyting);
}

View File

@ -0,0 +1,44 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Andreas Gudian
*
*/
@Mapper(
config = Erroneous1Config.class
)
public interface Erroneous1Mapper {
Erroneous1Mapper INSTANCE = Mappers.getMapper( Erroneous1Mapper.class );
@Mapping( target = "color", source = "colour" )
CarEntity toCarEntity(CarDto carDto);
@Mappings( {
@Mapping( target = "color", source = "colour" ),
@Mapping( target = "auditTrail", constant = "fixed" )
} )
CarEntity toCarEntityWithFixedAuditTrail(CarDto carDto);
}

View File

@ -16,34 +16,36 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.test.reverse; package org.mapstruct.ap.test.inheritfromconfig;
import org.mapstruct.InheritConfiguration; import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
/** /**
* @author Sjaak Derksen * @author Andreas Gudian
*
*/ */
@Mapper @Mapper(
public interface SourceTargetMapperErroneouslyAnnotated2 { config = AutoInheritedConfig.class
)
public interface Erroneous2Mapper {
Erroneous2Mapper INSTANCE = Mappers.getMapper( Erroneous2Mapper.class );
SourceTargetMapperErroneouslyAnnotated2 INSTANCE @InheritConfiguration( name = "toCarEntity2" )
= Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated2.class ); CarEntity toCarEntity1(CarDto carDto);
@Mappings({ @InheritConfiguration( name = "toCarEntity3" )
@Mapping(source = "stringPropX", target = "stringPropY"), CarEntity toCarEntity2(CarDto carDto);
@Mapping(source = "integerPropX", target = "integerPropY"),
@Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream")
})
Target forward(Source source);
@InheritConfiguration(name = "forward2") @InheritConfiguration( name = "toCarEntity1" )
Source forward2(Target target); @Mappings( {
@Mapping( target = "color", ignore = true ),
@InheritInverseConfiguration(name = "forward2") @Mapping( target = "auditTrail", ignore = true ),
Target reverse(Source source); @Mapping( target = "primaryKey", ignore = true )
} )
void toCarEntity3(CarDto carDto, @MappingTarget CarEntity entity);
} }

View File

@ -0,0 +1,216 @@
/**
* Copyright 2012-2015 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.inheritfromconfig;
import javax.tools.Diagnostic.Kind;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.fest.assertions.Assertions.assertThat;
/**
* @author Andreas Gudian
*
*/
@RunWith( AnnotationProcessorTestRunner.class )
@WithClasses( {
BaseVehicleDto.class,
BaseVehicleEntity.class,
CarDto.class,
CarEntity.class,
CarMapperWithAutoInheritance.class,
CarMapperWithExplicitInheritance.class,
AutoInheritedConfig.class } )
@IssueKey( "168" )
public class InheritFromConfigTest {
@Test
public void autoInheritedMappingIsApplied() {
CarDto carDto = newTestDto();
CarEntity carEntity = CarMapperWithAutoInheritance.INSTANCE.toCarEntity( carDto );
assertEntity( carEntity );
}
@Test
public void autoInheritedMappingIsAppliedForMappingTarget() {
CarDto carDto = newTestDto();
CarEntity carEntity = new CarEntity();
CarMapperWithAutoInheritance.INSTANCE.intoCarEntityOnItsOwn( carDto, carEntity );
assertEntity( carEntity );
}
@Test
public void autoInheritedMappingIsAppliedForMappingTargetWithTwoStepInheritance() {
CarDto carDto = newTestDto();
CarEntity carEntity = new CarEntity();
CarMapperWithAutoInheritance.INSTANCE.intoCarEntity( carDto, carEntity );
assertEntity( carEntity );
}
private void assertEntity(CarEntity carEntity) {
assertThat( carEntity.getColor() ).isEqualTo( "red" );
assertThat( carEntity.getPrimaryKey() ).isEqualTo( 42L );
assertThat( carEntity.getAuditTrail() ).isNull();
}
private CarDto newTestDto() {
CarDto carDto = new CarDto();
carDto.setColour( "red" );
carDto.setId( 42L );
return carDto;
}
@Test
public void autoInheritedMappingIsOverriddenAtMethodLevel() {
CarDto carDto = newTestDto();
CarEntity carEntity = CarMapperWithAutoInheritance.INSTANCE.toCarEntityWithFixedAuditTrail( carDto );
assertThat( carEntity.getColor() ).isEqualTo( "red" );
assertThat( carEntity.getPrimaryKey() ).isEqualTo( 42L );
assertThat( carEntity.getAuditTrail() ).isEqualTo( "fixed" );
}
@Test
public void autoInheritedMappingIsAppliedInReverse() {
CarEntity carEntity = new CarEntity();
carEntity.setColor( "red" );
carEntity.setPrimaryKey( 42L );
CarDto carDto = CarMapperWithAutoInheritance.INSTANCE.toCarDto( carEntity );
assertThat( carDto.getColour() ).isEqualTo( "red" );
assertThat( carDto.getId() ).isEqualTo( 42L );
}
@Test
public void explicitInheritedMappingIsAppliedInReverse() {
CarEntity carEntity = new CarEntity();
carEntity.setColor( "red" );
carEntity.setPrimaryKey( 42L );
CarDto carDto = CarMapperWithExplicitInheritance.INSTANCE.toCarDto( carEntity );
assertThat( carDto.getColour() ).isEqualTo( "red" );
assertThat( carDto.getId() ).isEqualTo( 42L );
}
@Test
public void explicitInheritedMappingWithTwoLevelsIsOverriddenAtMethodLevel() {
CarDto carDto = newTestDto();
CarEntity carEntity = CarMapperWithExplicitInheritance.INSTANCE.toCarEntityWithFixedAuditTrail( carDto );
assertThat( carEntity.getColor() ).isEqualTo( "red" );
assertThat( carEntity.getPrimaryKey() ).isEqualTo( 42L );
assertThat( carEntity.getAuditTrail() ).isEqualTo( "fixed" );
}
@Test
public void explicitInheritedMappingIsApplied() {
CarDto carDto = newTestDto();
CarEntity carEntity = CarMapperWithExplicitInheritance.INSTANCE.toCarEntity( carDto );
assertEntity( carEntity );
}
@Test
@WithClasses( {
DriverDto.class,
CarWithDriverEntity.class,
CarWithDriverMapperWithAutoInheritance.class,
AutoInheritedDriverConfig.class } )
public void autoInheritedFromMultipleSources() {
CarDto carDto = newTestDto();
DriverDto driverDto = new DriverDto();
driverDto.setName( "Malcroft" );
CarWithDriverEntity carWithDriverEntity =
CarWithDriverMapperWithAutoInheritance.INSTANCE.toCarWithDriverEntity( carDto, driverDto );
assertEntity( carWithDriverEntity );
assertThat( carWithDriverEntity.getDriverName() ).isEqualTo( "Malcroft" );
}
@Test
@WithClasses( { Erroneous1Mapper.class, Erroneous1Config.class } )
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic( type = Erroneous1Mapper.class,
kind = Kind.ERROR,
line = 37,
messageRegExp = "More than one configuration prototype method is applicable. Use @InheritConfiguration"
+ " to select one of them explicitly:"
+ " .*BaseVehicleEntity baseDtoToEntity\\(.*BaseVehicleDto dto\\),"
+ " .*BaseVehicleEntity anythingToEntity\\(java.lang.Object anyting\\)\\." ),
@Diagnostic( type = Erroneous1Mapper.class,
kind = Kind.WARNING,
line = 37,
messageRegExp = "Unmapped target properties: \"auditTrail, primaryKey\"\\." ),
@Diagnostic( type = Erroneous1Mapper.class,
kind = Kind.ERROR,
line = 43,
messageRegExp = "More than one configuration prototype method is applicable. Use @InheritConfiguration"
+ " to select one of them explicitly:"
+ " .*BaseVehicleEntity baseDtoToEntity\\(.*BaseVehicleDto dto\\),"
+ " .*BaseVehicleEntity anythingToEntity\\(java.lang.Object anyting\\)\\." ),
@Diagnostic( type = Erroneous1Mapper.class,
kind = Kind.WARNING,
line = 43,
messageRegExp = "Unmapped target property: \"primaryKey\"\\." )
}
)
public void erroneous1MultiplePrototypeMethodsMatch() {
}
@Test
@WithClasses( { Erroneous2Mapper.class } )
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic( type = Erroneous2Mapper.class,
kind = Kind.ERROR,
line = 39,
messageRegExp = "Cycle detected while evaluating inherited configurations. Inheritance path:"
+ " .*CarEntity toCarEntity1\\(.*CarDto carDto\\)"
+ " -> .*CarEntity toCarEntity2\\(.*CarDto carDto\\)"
+ " -> void toCarEntity3\\(.*CarDto carDto, @MappingTarget .*CarEntity entity\\)"
+ " -> .*CarEntity toCarEntity1\\(.*CarDto carDto\\)" )
}
)
public void erroneous2InheritanceCycle() {
}
}

View File

@ -23,11 +23,13 @@ import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.mapstruct.CollectionMappingStrategy; import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.MappingInheritanceStrategy;
import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.MappingInheritanceStrategyPrism;
import org.mapstruct.ap.prism.NullValueMappingStrategyPrism;
import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Assertions.assertThat;
import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.ap.prism.NullValueMappingStrategyPrism;
/** /**
* Test for manually created prisms on enumeration types * Test for manually created prisms on enumeration types
@ -47,6 +49,12 @@ public class EnumPrismsTest {
namesOf( NullValueMappingStrategyPrism.values() ) ); namesOf( NullValueMappingStrategyPrism.values() ) );
} }
@Test
public void mapMappingInheritanceStrategyPrismIsCorrect() {
assertThat( namesOf( MappingInheritanceStrategy.values() ) ).isEqualTo(
namesOf( MappingInheritanceStrategyPrism.values() ) );
}
private static List<String> namesOf(Enum<?>[] values) { private static List<String> namesOf(Enum<?>[] values) {
List<String> names = new ArrayList<String>( values.length ); List<String> names = new ArrayList<String>( values.length );

View File

@ -108,8 +108,8 @@ public class InheritInverseConfigurationTest {
@Diagnostic(type = SourceTargetMapperAmbiguous3.class, @Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.ERROR, kind = Kind.ERROR,
line = 50, line = 50,
messageRegExp = "Given name \"forward\" matches several candidate methods: .*forward.*\\(\\), " messageRegExp = "Given name \"forward\" matches several candidate methods: .*forward\\(.*\\), "
+ ".*forward.*\\(\\)"), + ".*forward\\(.*\\)"),
@Diagnostic(type = SourceTargetMapperAmbiguous3.class, @Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.WARNING, kind = Kind.WARNING,
line = 55, line = 55,
@ -119,46 +119,6 @@ public class InheritInverseConfigurationTest {
public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() {
} }
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated1.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated1.class,
kind = Kind.ERROR,
line = 50,
messageRegExp = "Resolved inverse mapping method reverse\\(\\) should not carry the "
+ "@InheritInverseConfiguration annotation itself.")
}
)
public void shouldUseWronglyAnnotatedError1() {
}
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated2.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.WARNING,
line = 45,
messageRegExp = "Unmapped target properties: \"stringPropX, integerPropX, "
+ "someConstantDownstream, propertyToIgnoreDownstream\""),
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.ERROR,
line = 47,
messageRegExp = "Resolved inverse mapping method forward2\\(\\) should not carry "
+ "the @InheritConfiguration annotation"),
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.WARNING,
line = 48,
messageRegExp = "Unmapped target properties: \"stringPropY, integerPropY, "
+ "propertyNotToIgnoreUpstream\"")
}
)
public void shouldUseWronglyAnnotatedError2() {
}
@Test @Test
@WithClasses({ SourceTargetMapperNonMatchingName.class }) @WithClasses({ SourceTargetMapperNonMatchingName.class })
@ExpectedCompilationOutcome( @ExpectedCompilationOutcome(

View File

@ -1,57 +0,0 @@
/**
* Copyright 2012-2015 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.reverse;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperErroneouslyAnnotated1 {
SourceTargetMapperErroneouslyAnnotated1 INSTANCE =
Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated1.class );
@Mappings({
@Mapping(source = "stringPropX", target = "stringPropY"),
@Mapping(source = "integerPropX", target = "integerPropY"),
@Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream")
})
Target forward(Source source);
@InheritInverseConfiguration(name = "forward")
@Mappings({
@Mapping(target = "someConstantDownstream", constant = "test"),
@Mapping(target = "propertyToIgnoreDownstream", ignore = true)
})
Source reverse(Target target);
@InheritInverseConfiguration(name = "reverse")
@Mappings({
@Mapping(source = "stringPropX", target = "stringPropY"),
@Mapping(source = "integerPropX", target = "integerPropY"),
@Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream")
})
Target forward2(Source source);
}

View File

@ -182,36 +182,6 @@ public class InheritConfigurationTest {
public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() {
} }
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated1.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated1.class,
kind = Kind.ERROR,
line = 49,
messageRegExp = "Resolved mapping method forwardCreate1\\(\\) should not carry the "
+ "@InheritConfiguration annotation itself.")
}
)
public void shouldUseWronglyAnnotatedError1() {
}
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated2.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.ERROR,
line = 51,
messageRegExp = "Resolved mapping method forwardCreate\\(\\) should not carry the "
+ "@InheritInverseConfiguration annotation.")
}
)
public void shouldUseWronglyAnnotatedError2() {
}
@Test @Test
@WithClasses({ SourceTargetMapperNonMatchingName.class }) @WithClasses({ SourceTargetMapperNonMatchingName.class })
@ExpectedCompilationOutcome( @ExpectedCompilationOutcome(

View File

@ -1,51 +0,0 @@
/**
* Copyright 2012-2015 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.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperErroneouslyAnnotated1 {
SourceTargetMapperErroneouslyAnnotated1 INSTANCE =
Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated1.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX"),
@Mapping(target = "integerPropY", source = "integerPropX"),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate(Source source);
@InheritConfiguration( name = "forwardCreate" )
Target forwardCreate1(Source source);
@InheritConfiguration( name = "forwardCreate1" )
void forwardUpdate(Source source, @MappingTarget Target target);
}

View File

@ -34,11 +34,11 @@ public interface SourceTargetMapperSeveralArgs {
SourceTargetMapperSeveralArgs INSTANCE = Mappers.getMapper( SourceTargetMapperSeveralArgs.class ); SourceTargetMapperSeveralArgs INSTANCE = Mappers.getMapper( SourceTargetMapperSeveralArgs.class );
@Mappings({ @Mappings({
@Mapping(target = "stringPropY", source = "source.stringPropX"), @Mapping( target = "stringPropY", source = "s1.stringPropX" ),
@Mapping(target = "integerPropY", source = "source.integerPropX"), @Mapping( target = "integerPropY", source = "s1.integerPropX" ),
@Mapping(target = "nestedResultProp", source = "source.nestedSourceProp.nested") @Mapping( target = "nestedResultProp", source = "s1.nestedSourceProp.nested" )
}) })
Target forwardCreate(Source source, String constantProp, String expressionProp); Target forwardCreate(Source s1, String constantProp, String expressionProp);
@InheritConfiguration @InheritConfiguration
void forwardUpdate(Source source, String constantProp, String expressionProp, @MappingTarget Target target); void forwardUpdate(Source source, String constantProp, String expressionProp, @MappingTarget Target target);

View File

@ -40,7 +40,7 @@ public interface SourceTargetMapperSingle {
@Mapping(target = "constantProp", constant = "constant"), @Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")"), @Mapping(target = "expressionProp", expression = "java(\"expression\")"),
}) })
Target forwardCreate(Source source); Target forwardCreate(Source s1);
@InheritConfiguration @InheritConfiguration