mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
2696: Invert @SubclassMappings
with @InheritInverseConfiguration
. (#2708)
* #2696: Added support for '@InheritInverseConfiguration' with '@SubclassMappings'. * #2696: Overriding of inverse inheritence implemented. New order has preference over inherited order.
This commit is contained in:
parent
2a2c11e871
commit
7bb85d05c0
@ -157,6 +157,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BeanMappingMethod build() {
|
public BeanMappingMethod build() {
|
||||||
|
|
||||||
BeanMappingOptions beanMapping = method.getOptions().getBeanMapping();
|
BeanMappingOptions beanMapping = method.getOptions().getBeanMapping();
|
||||||
|
@ -11,6 +11,7 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem;
|
import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
@ -35,7 +36,8 @@ public class MappingMethodOptions {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
Collections.emptySet()
|
Collections.emptySet(),
|
||||||
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
private MapperOptions mapper;
|
private MapperOptions mapper;
|
||||||
@ -46,14 +48,16 @@ public class MappingMethodOptions {
|
|||||||
private EnumMappingOptions enumMappingOptions;
|
private EnumMappingOptions enumMappingOptions;
|
||||||
private List<ValueMappingOptions> valueMappings;
|
private List<ValueMappingOptions> valueMappings;
|
||||||
private boolean fullyInitialized;
|
private boolean fullyInitialized;
|
||||||
private Set<SubclassMappingOptions> subclassMapping;
|
private Set<SubclassMappingOptions> subclassMappings;
|
||||||
|
|
||||||
|
private SubclassValidator subclassValidator;
|
||||||
|
|
||||||
public MappingMethodOptions(MapperOptions mapper, Set<MappingOptions> mappings,
|
public MappingMethodOptions(MapperOptions mapper, Set<MappingOptions> mappings,
|
||||||
IterableMappingOptions iterableMapping,
|
IterableMappingOptions iterableMapping,
|
||||||
MapMappingOptions mapMapping, BeanMappingOptions beanMapping,
|
MapMappingOptions mapMapping, BeanMappingOptions beanMapping,
|
||||||
EnumMappingOptions enumMappingOptions,
|
EnumMappingOptions enumMappingOptions,
|
||||||
List<ValueMappingOptions> valueMappings,
|
List<ValueMappingOptions> valueMappings,
|
||||||
Set<SubclassMappingOptions> subclassMapping) {
|
Set<SubclassMappingOptions> subclassMappings, SubclassValidator subclassValidator) {
|
||||||
this.mapper = mapper;
|
this.mapper = mapper;
|
||||||
this.mappings = mappings;
|
this.mappings = mappings;
|
||||||
this.iterableMapping = iterableMapping;
|
this.iterableMapping = iterableMapping;
|
||||||
@ -61,7 +65,8 @@ public class MappingMethodOptions {
|
|||||||
this.beanMapping = beanMapping;
|
this.beanMapping = beanMapping;
|
||||||
this.enumMappingOptions = enumMappingOptions;
|
this.enumMappingOptions = enumMappingOptions;
|
||||||
this.valueMappings = valueMappings;
|
this.valueMappings = valueMappings;
|
||||||
this.subclassMapping = subclassMapping;
|
this.subclassMappings = subclassMappings;
|
||||||
|
this.subclassValidator = subclassValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,7 +107,7 @@ public class MappingMethodOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Set<SubclassMappingOptions> getSubclassMappings() {
|
public Set<SubclassMappingOptions> getSubclassMappings() {
|
||||||
return subclassMapping;
|
return subclassMappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIterableMapping(IterableMappingOptions iterableMapping) {
|
public void setIterableMapping(IterableMappingOptions iterableMapping) {
|
||||||
@ -144,10 +149,13 @@ public class MappingMethodOptions {
|
|||||||
/**
|
/**
|
||||||
* Merges in all the mapping options configured, giving the already defined options precedence.
|
* Merges in all the mapping options configured, giving the already defined options precedence.
|
||||||
*
|
*
|
||||||
|
* @param sourceMethod the method which inherits the options.
|
||||||
* @param templateMethod the template method with the options to inherit, may be {@code null}
|
* @param templateMethod the template method with the options to inherit, may be {@code null}
|
||||||
* @param isInverse if {@code true}, the specified options are from an inverse method
|
* @param isInverse if {@code true}, the specified options are from an inverse method
|
||||||
|
* @param annotationMirror the annotation on which the compile errors will be shown.
|
||||||
*/
|
*/
|
||||||
public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse) {
|
public void applyInheritedOptions(SourceMethod sourceMethod, SourceMethod templateMethod, boolean isInverse,
|
||||||
|
AnnotationMirror annotationMirror) {
|
||||||
MappingMethodOptions templateOptions = templateMethod.getOptions();
|
MappingMethodOptions templateOptions = templateMethod.getOptions();
|
||||||
if ( null != templateOptions ) {
|
if ( null != templateOptions ) {
|
||||||
if ( !getIterableMapping().hasAnnotation() && templateOptions.getIterableMapping().hasAnnotation() ) {
|
if ( !getIterableMapping().hasAnnotation() && templateOptions.getIterableMapping().hasAnnotation() ) {
|
||||||
@ -184,7 +192,7 @@ public class MappingMethodOptions {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( templateOptions.getValueMappings() != null ) {
|
if ( templateOptions.getValueMappings() != null ) {
|
||||||
// iff there are also inherited mappings, we inverse and add them.
|
// if there are also inherited mappings, we inverse and add them.
|
||||||
for ( ValueMappingOptions inheritedValueMapping : templateOptions.getValueMappings() ) {
|
for ( ValueMappingOptions inheritedValueMapping : templateOptions.getValueMappings() ) {
|
||||||
ValueMappingOptions valueMapping =
|
ValueMappingOptions valueMapping =
|
||||||
isInverse ? inheritedValueMapping.inverse() : inheritedValueMapping;
|
isInverse ? inheritedValueMapping.inverse() : inheritedValueMapping;
|
||||||
@ -196,7 +204,13 @@ public class MappingMethodOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do NOT inherit subclass mapping options!!!
|
if ( isInverse ) {
|
||||||
|
// normal inheritence of subclass mappings will result runtime in infinite loops.
|
||||||
|
List<SubclassMappingOptions> inheritedMappings = SubclassMappingOptions.copyForInverseInheritance(
|
||||||
|
templateOptions.getSubclassMappings(),
|
||||||
|
getBeanMapping() );
|
||||||
|
addAllNonRedefined( sourceMethod, annotationMirror, inheritedMappings );
|
||||||
|
}
|
||||||
|
|
||||||
Set<MappingOptions> newMappings = new LinkedHashSet<>();
|
Set<MappingOptions> newMappings = new LinkedHashSet<>();
|
||||||
for ( MappingOptions mapping : templateOptions.getMappings() ) {
|
for ( MappingOptions mapping : templateOptions.getMappings() ) {
|
||||||
@ -218,6 +232,21 @@ public class MappingMethodOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addAllNonRedefined(SourceMethod sourceMethod, AnnotationMirror annotationMirror,
|
||||||
|
List<SubclassMappingOptions> inheritedMappings) {
|
||||||
|
Set<SubclassMappingOptions> redefinedSubclassMappings = new HashSet<>( subclassMappings );
|
||||||
|
for ( SubclassMappingOptions subclassMappingOption : inheritedMappings ) {
|
||||||
|
if ( !redefinedSubclassMappings.contains( subclassMappingOption ) ) {
|
||||||
|
if ( subclassValidator.isValidUsage(
|
||||||
|
sourceMethod.getExecutable(),
|
||||||
|
annotationMirror,
|
||||||
|
subclassMappingOption.getSource() ) ) {
|
||||||
|
subclassMappings.add( subclassMappingOption );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addAllNonRedefined(Set<MappingOptions> inheritedMappings) {
|
private void addAllNonRedefined(Set<MappingOptions> inheritedMappings) {
|
||||||
Set<String> redefinedSources = new HashSet<>();
|
Set<String> redefinedSources = new HashSet<>();
|
||||||
Set<String> redefinedTargets = new HashSet<>();
|
Set<String> redefinedTargets = new HashSet<>();
|
||||||
@ -328,7 +357,7 @@ public class MappingMethodOptions {
|
|||||||
/**
|
/**
|
||||||
* SubclassMappingOptions are not inherited to forged methods. They would result in an infinite loop if they were.
|
* SubclassMappingOptions are not inherited to forged methods. They would result in an infinite loop if they were.
|
||||||
*
|
*
|
||||||
* @return a MappingMethodOptions without SubclassMappingOptions.
|
* @return a MappingMethodOptions without SubclassMappingOptions or SubclassValidator.
|
||||||
*/
|
*/
|
||||||
public static MappingMethodOptions getForgedMethodInheritedOptions(MappingMethodOptions options) {
|
public static MappingMethodOptions getForgedMethodInheritedOptions(MappingMethodOptions options) {
|
||||||
return new MappingMethodOptions(
|
return new MappingMethodOptions(
|
||||||
@ -339,7 +368,8 @@ public class MappingMethodOptions {
|
|||||||
options.beanMapping,
|
options.beanMapping,
|
||||||
options.enumMappingOptions,
|
options.enumMappingOptions,
|
||||||
options.valueMappings,
|
options.valueMappings,
|
||||||
Collections.emptySet() );
|
Collections.emptySet(),
|
||||||
|
null );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,14 @@ import javax.lang.model.element.ExecutableElement;
|
|||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.gem.ConditionGem;
|
import org.mapstruct.ap.internal.gem.ConditionGem;
|
||||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
import org.mapstruct.ap.internal.gem.ObjectFactoryGem;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.common.Accessibility;
|
import org.mapstruct.ap.internal.model.common.Accessibility;
|
||||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
import org.mapstruct.ap.internal.gem.ObjectFactoryGem;
|
|
||||||
import org.mapstruct.ap.internal.util.Executables;
|
import org.mapstruct.ap.internal.util.Executables;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
|
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.model.source.MappingMethodUtils.isEnumMapping;
|
import static org.mapstruct.ap.internal.model.source.MappingMethodUtils.isEnumMapping;
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
@ -98,6 +97,7 @@ public class SourceMethod implements Method {
|
|||||||
private Set<SubclassMappingOptions> subclassMappings;
|
private Set<SubclassMappingOptions> subclassMappings;
|
||||||
|
|
||||||
private boolean verboseLogging;
|
private boolean verboseLogging;
|
||||||
|
private SubclassValidator subclassValidator;
|
||||||
|
|
||||||
public Builder setDeclaringMapper(Type declaringMapper) {
|
public Builder setDeclaringMapper(Type declaringMapper) {
|
||||||
this.declaringMapper = declaringMapper;
|
this.declaringMapper = declaringMapper;
|
||||||
@ -159,6 +159,11 @@ public class SourceMethod implements Method {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setSubclassValidator(SubclassValidator subclassValidator) {
|
||||||
|
this.subclassValidator = subclassValidator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setTypeUtils(TypeUtils typeUtils) {
|
public Builder setTypeUtils(TypeUtils typeUtils) {
|
||||||
this.typeUtils = typeUtils;
|
this.typeUtils = typeUtils;
|
||||||
return this;
|
return this;
|
||||||
@ -212,7 +217,8 @@ public class SourceMethod implements Method {
|
|||||||
beanMapping,
|
beanMapping,
|
||||||
enumMappingOptions,
|
enumMappingOptions,
|
||||||
valueMappings,
|
valueMappings,
|
||||||
subclassMappings
|
subclassMappings,
|
||||||
|
subclassValidator
|
||||||
);
|
);
|
||||||
|
|
||||||
this.typeParameters = this.executable.getTypeParameters()
|
this.typeParameters = this.executable.getTypeParameters()
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.model.source;
|
package org.mapstruct.ap.internal.model.source;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
@ -31,11 +32,13 @@ public class SubclassMappingOptions extends DelegatingOptions {
|
|||||||
|
|
||||||
private final TypeMirror source;
|
private final TypeMirror source;
|
||||||
private final TypeMirror target;
|
private final TypeMirror target;
|
||||||
|
private final TypeUtils typeUtils;
|
||||||
|
|
||||||
public SubclassMappingOptions(TypeMirror source, TypeMirror target, DelegatingOptions next) {
|
public SubclassMappingOptions(TypeMirror source, TypeMirror target, TypeUtils typeUtils, DelegatingOptions next) {
|
||||||
super( next );
|
super( next );
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
|
this.typeUtils = typeUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -84,7 +87,9 @@ public class SubclassMappingOptions extends DelegatingOptions {
|
|||||||
targetSubclass.toString() );
|
targetSubclass.toString() );
|
||||||
isConsistent = false;
|
isConsistent = false;
|
||||||
}
|
}
|
||||||
subclassValidator.isInCorrectOrder( method, gem.mirror(), targetSubclass );
|
if ( !subclassValidator.isValidUsage( method, gem.mirror(), sourceSubclass ) ) {
|
||||||
|
isConsistent = false;
|
||||||
|
}
|
||||||
return isConsistent;
|
return isConsistent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,10 +120,10 @@ public class SubclassMappingOptions extends DelegatingOptions {
|
|||||||
public static void addInstances(SubclassMappingsGem gem, ExecutableElement method,
|
public static void addInstances(SubclassMappingsGem gem, ExecutableElement method,
|
||||||
BeanMappingOptions beanMappingOptions, FormattingMessager messager,
|
BeanMappingOptions beanMappingOptions, FormattingMessager messager,
|
||||||
TypeUtils typeUtils, Set<SubclassMappingOptions> mappings,
|
TypeUtils typeUtils, Set<SubclassMappingOptions> mappings,
|
||||||
List<Parameter> sourceParameters, Type resultType) {
|
List<Parameter> sourceParameters, Type resultType,
|
||||||
SubclassValidator subclassValidator = new SubclassValidator( messager, typeUtils );
|
SubclassValidator subclassValidator) {
|
||||||
for ( SubclassMappingGem subclassMappingGem : gem.value().get() ) {
|
for ( SubclassMappingGem subclassMappingGem : gem.value().get() ) {
|
||||||
addAndValidateInstance(
|
addInstance(
|
||||||
subclassMappingGem,
|
subclassMappingGem,
|
||||||
method,
|
method,
|
||||||
beanMappingOptions,
|
beanMappingOptions,
|
||||||
@ -134,24 +139,8 @@ public class SubclassMappingOptions extends DelegatingOptions {
|
|||||||
public static void addInstance(SubclassMappingGem subclassMapping, ExecutableElement method,
|
public static void addInstance(SubclassMappingGem subclassMapping, ExecutableElement method,
|
||||||
BeanMappingOptions beanMappingOptions, FormattingMessager messager,
|
BeanMappingOptions beanMappingOptions, FormattingMessager messager,
|
||||||
TypeUtils typeUtils, Set<SubclassMappingOptions> mappings,
|
TypeUtils typeUtils, Set<SubclassMappingOptions> mappings,
|
||||||
List<Parameter> sourceParameters, Type resultType) {
|
List<Parameter> sourceParameters, Type resultType,
|
||||||
addAndValidateInstance(
|
SubclassValidator subclassValidator) {
|
||||||
subclassMapping,
|
|
||||||
method,
|
|
||||||
beanMappingOptions,
|
|
||||||
messager,
|
|
||||||
typeUtils,
|
|
||||||
mappings,
|
|
||||||
sourceParameters,
|
|
||||||
resultType,
|
|
||||||
new SubclassValidator( messager, typeUtils ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addAndValidateInstance(SubclassMappingGem subclassMapping, ExecutableElement method,
|
|
||||||
BeanMappingOptions beanMappingOptions, FormattingMessager messager,
|
|
||||||
TypeUtils typeUtils, Set<SubclassMappingOptions> mappings,
|
|
||||||
List<Parameter> sourceParameters, Type resultType,
|
|
||||||
SubclassValidator subclassValidator) {
|
|
||||||
if ( !isConsistent(
|
if ( !isConsistent(
|
||||||
subclassMapping,
|
subclassMapping,
|
||||||
method,
|
method,
|
||||||
@ -166,6 +155,41 @@ public class SubclassMappingOptions extends DelegatingOptions {
|
|||||||
TypeMirror sourceSubclass = subclassMapping.source().getValue();
|
TypeMirror sourceSubclass = subclassMapping.source().getValue();
|
||||||
TypeMirror targetSubclass = subclassMapping.target().getValue();
|
TypeMirror targetSubclass = subclassMapping.target().getValue();
|
||||||
|
|
||||||
mappings.add( new SubclassMappingOptions( sourceSubclass, targetSubclass, beanMappingOptions ) );
|
mappings
|
||||||
|
.add(
|
||||||
|
new SubclassMappingOptions(
|
||||||
|
sourceSubclass,
|
||||||
|
targetSubclass,
|
||||||
|
typeUtils,
|
||||||
|
beanMappingOptions ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SubclassMappingOptions> copyForInverseInheritance(Set<SubclassMappingOptions> subclassMappings,
|
||||||
|
BeanMappingOptions beanMappingOptions) {
|
||||||
|
// we are not interested in keeping it unique at this point.
|
||||||
|
List<SubclassMappingOptions> mappings = new ArrayList<>();
|
||||||
|
for ( SubclassMappingOptions subclassMapping : subclassMappings ) {
|
||||||
|
mappings.add(
|
||||||
|
new SubclassMappingOptions(
|
||||||
|
subclassMapping.target,
|
||||||
|
subclassMapping.source,
|
||||||
|
subclassMapping.typeUtils,
|
||||||
|
beanMappingOptions ) );
|
||||||
|
}
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if ( obj == null || !( obj instanceof SubclassMappingOptions ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SubclassMappingOptions other = (SubclassMappingOptions) obj;
|
||||||
|
return typeUtils.isSameType( source, other.source );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 1; // use a stable value because TypeMirror is not safe to use for hashCode.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,19 +20,28 @@ import org.mapstruct.ap.internal.util.TypeUtils;
|
|||||||
*
|
*
|
||||||
* @author Ben Zegveld
|
* @author Ben Zegveld
|
||||||
*/
|
*/
|
||||||
class SubclassValidator {
|
public class SubclassValidator {
|
||||||
|
|
||||||
private final FormattingMessager messager;
|
private final FormattingMessager messager;
|
||||||
private final List<TypeMirror> handledSubclasses = new ArrayList<>();
|
private final List<TypeMirror> handledSubclasses = new ArrayList<>();
|
||||||
private final TypeUtils typeUtils;
|
private final TypeUtils typeUtils;
|
||||||
|
|
||||||
SubclassValidator(FormattingMessager messager, TypeUtils typeUtils) {
|
public SubclassValidator(FormattingMessager messager, TypeUtils typeUtils) {
|
||||||
this.messager = messager;
|
this.messager = messager;
|
||||||
this.typeUtils = typeUtils;
|
this.typeUtils = typeUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInCorrectOrder(Element e, AnnotationMirror annotation, TypeMirror sourceType) {
|
public boolean isValidUsage(Element e, AnnotationMirror annotation, TypeMirror sourceType) {
|
||||||
for ( TypeMirror typeMirror : handledSubclasses ) {
|
for ( TypeMirror typeMirror : handledSubclasses ) {
|
||||||
|
if ( typeUtils.isSameType( sourceType, typeMirror ) ) {
|
||||||
|
messager
|
||||||
|
.printMessage(
|
||||||
|
e,
|
||||||
|
annotation,
|
||||||
|
Message.SUBCLASSMAPPING_DOUBLE_SOURCE_SUBCLASS,
|
||||||
|
sourceType );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ( typeUtils.isAssignable( sourceType, typeMirror ) ) {
|
if ( typeUtils.isAssignable( sourceType, typeMirror ) ) {
|
||||||
messager
|
messager
|
||||||
.printMessage(
|
.printMessage(
|
||||||
@ -43,11 +52,11 @@ class SubclassValidator {
|
|||||||
typeMirror,
|
typeMirror,
|
||||||
sourceType,
|
sourceType,
|
||||||
typeMirror );
|
typeMirror );
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handledSubclasses.add( sourceType );
|
handledSubclasses.add( sourceType );
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.ElementKind;
|
import javax.lang.model.element.ElementKind;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
@ -320,7 +321,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeInheritedOptions( method, mapperAnnotation, methods, new ArrayList<>() );
|
mergeInheritedOptions( method, mapperAnnotation, methods, new ArrayList<>(), null );
|
||||||
|
|
||||||
MappingMethodOptions mappingOptions = method.getOptions();
|
MappingMethodOptions mappingOptions = method.getOptions();
|
||||||
|
|
||||||
@ -462,7 +463,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConfig,
|
private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConfig,
|
||||||
List<SourceMethod> availableMethods, List<SourceMethod> initializingMethods) {
|
List<SourceMethod> availableMethods, List<SourceMethod> initializingMethods,
|
||||||
|
AnnotationMirror annotationMirror) {
|
||||||
if ( initializingMethods.contains( method ) ) {
|
if ( initializingMethods.contains( method ) ) {
|
||||||
// cycle detected
|
// cycle detected
|
||||||
|
|
||||||
@ -497,10 +499,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
|
|
||||||
// apply defined (@InheritConfiguration, @InheritInverseConfiguration) mappings
|
// apply defined (@InheritConfiguration, @InheritInverseConfiguration) mappings
|
||||||
if ( forwardTemplateMethod != null ) {
|
if ( forwardTemplateMethod != null ) {
|
||||||
mappingOptions.applyInheritedOptions( forwardTemplateMethod, false );
|
mappingOptions.applyInheritedOptions( method, forwardTemplateMethod, false, annotationMirror );
|
||||||
}
|
}
|
||||||
if ( inverseTemplateMethod != null ) {
|
if ( inverseTemplateMethod != null ) {
|
||||||
mappingOptions.applyInheritedOptions( inverseTemplateMethod, true );
|
mappingOptions.applyInheritedOptions( method, inverseTemplateMethod, true, annotationMirror );
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply auto inherited options
|
// apply auto inherited options
|
||||||
@ -510,7 +512,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
// but.. there should not be an @InheritedConfiguration
|
// but.. there should not be an @InheritedConfiguration
|
||||||
if ( forwardTemplateMethod == null && inheritanceStrategy.isApplyForward() ) {
|
if ( forwardTemplateMethod == null && inheritanceStrategy.isApplyForward() ) {
|
||||||
if ( applicablePrototypeMethods.size() == 1 ) {
|
if ( applicablePrototypeMethods.size() == 1 ) {
|
||||||
mappingOptions.applyInheritedOptions( first( applicablePrototypeMethods ), false );
|
mappingOptions.applyInheritedOptions( method, first( applicablePrototypeMethods ), false,
|
||||||
|
annotationMirror );
|
||||||
}
|
}
|
||||||
else if ( applicablePrototypeMethods.size() > 1 ) {
|
else if ( applicablePrototypeMethods.size() > 1 ) {
|
||||||
messager.printMessage(
|
messager.printMessage(
|
||||||
@ -523,7 +526,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
// or no @InheritInverseConfiguration
|
// or no @InheritInverseConfiguration
|
||||||
if ( inverseTemplateMethod == null && inheritanceStrategy.isApplyReverse() ) {
|
if ( inverseTemplateMethod == null && inheritanceStrategy.isApplyReverse() ) {
|
||||||
if ( applicableReversePrototypeMethods.size() == 1 ) {
|
if ( applicableReversePrototypeMethods.size() == 1 ) {
|
||||||
mappingOptions.applyInheritedOptions( first( applicableReversePrototypeMethods ), true );
|
mappingOptions.applyInheritedOptions( method, first( applicableReversePrototypeMethods ), true,
|
||||||
|
annotationMirror );
|
||||||
}
|
}
|
||||||
else if ( applicableReversePrototypeMethods.size() > 1 ) {
|
else if ( applicableReversePrototypeMethods.size() > 1 ) {
|
||||||
messager.printMessage(
|
messager.printMessage(
|
||||||
@ -607,16 +611,23 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods );
|
return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods,
|
||||||
|
getAnnotationMirror( inverseConfiguration ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnnotationMirror getAnnotationMirror(InheritInverseConfigurationGem inverseConfiguration) {
|
||||||
|
return inverseConfiguration == null ? null : inverseConfiguration.mirror();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceMethod extractInitializedOptions(SourceMethod resultMethod,
|
private SourceMethod extractInitializedOptions(SourceMethod resultMethod,
|
||||||
List<SourceMethod> rawMethods,
|
List<SourceMethod> rawMethods,
|
||||||
MapperOptions mapperConfig,
|
MapperOptions mapperConfig,
|
||||||
List<SourceMethod> initializingMethods) {
|
List<SourceMethod> initializingMethods,
|
||||||
|
AnnotationMirror annotationMirror) {
|
||||||
if ( resultMethod != null ) {
|
if ( resultMethod != null ) {
|
||||||
if ( !resultMethod.getOptions().isFullyInitialized() ) {
|
if ( !resultMethod.getOptions().isFullyInitialized() ) {
|
||||||
mergeInheritedOptions( resultMethod, mapperConfig, rawMethods, initializingMethods );
|
mergeInheritedOptions( resultMethod, mapperConfig, rawMethods, initializingMethods,
|
||||||
|
annotationMirror );
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultMethod;
|
return resultMethod;
|
||||||
@ -684,7 +695,12 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods );
|
return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods,
|
||||||
|
getAnnotationMirror( inheritConfiguration ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnnotationMirror getAnnotationMirror(InheritConfigurationGem inheritConfiguration) {
|
||||||
|
return inheritConfiguration == null ? null : inheritConfiguration.mirror();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportErrorWhenAmbigousReverseMapping(List<SourceMethod> candidates, SourceMethod method,
|
private void reportErrorWhenAmbigousReverseMapping(List<SourceMethod> candidates, SourceMethod method,
|
||||||
|
@ -45,6 +45,7 @@ import org.mapstruct.ap.internal.model.source.MappingOptions;
|
|||||||
import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods;
|
import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods;
|
||||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||||
import org.mapstruct.ap.internal.model.source.SubclassMappingOptions;
|
import org.mapstruct.ap.internal.model.source.SubclassMappingOptions;
|
||||||
|
import org.mapstruct.ap.internal.model.source.SubclassValidator;
|
||||||
import org.mapstruct.ap.internal.model.source.ValueMappingOptions;
|
import org.mapstruct.ap.internal.model.source.ValueMappingOptions;
|
||||||
import org.mapstruct.ap.internal.option.Options;
|
import org.mapstruct.ap.internal.option.Options;
|
||||||
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
||||||
@ -307,11 +308,13 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
|
|
||||||
// We want to get as much error reporting as possible.
|
// We want to get as much error reporting as possible.
|
||||||
// If targetParameter is not null it means we have an update method
|
// If targetParameter is not null it means we have an update method
|
||||||
|
SubclassValidator subclassValidator = new SubclassValidator( messager, typeUtils );
|
||||||
Set<SubclassMappingOptions> subclassMappingOptions = getSubclassMappings(
|
Set<SubclassMappingOptions> subclassMappingOptions = getSubclassMappings(
|
||||||
sourceParameters,
|
sourceParameters,
|
||||||
targetParameter != null ? null : resultType,
|
targetParameter != null ? null : resultType,
|
||||||
method,
|
method,
|
||||||
beanMappingOptions
|
beanMappingOptions,
|
||||||
|
subclassValidator
|
||||||
);
|
);
|
||||||
|
|
||||||
return new SourceMethod.Builder()
|
return new SourceMethod.Builder()
|
||||||
@ -327,6 +330,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
.setValueMappingOptionss( getValueMappings( method ) )
|
.setValueMappingOptionss( getValueMappings( method ) )
|
||||||
.setEnumMappingOptions( enumMappingOptions )
|
.setEnumMappingOptions( enumMappingOptions )
|
||||||
.setSubclassMappings( subclassMappingOptions )
|
.setSubclassMappings( subclassMappingOptions )
|
||||||
|
.setSubclassValidator( subclassValidator )
|
||||||
.setTypeUtils( typeUtils )
|
.setTypeUtils( typeUtils )
|
||||||
.setTypeFactory( typeFactory )
|
.setTypeFactory( typeFactory )
|
||||||
.setPrototypeMethods( prototypeMethods )
|
.setPrototypeMethods( prototypeMethods )
|
||||||
@ -601,8 +605,10 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
* @return The subclass mappings for the given method
|
* @return The subclass mappings for the given method
|
||||||
*/
|
*/
|
||||||
private Set<SubclassMappingOptions> getSubclassMappings(List<Parameter> sourceParameters, Type resultType,
|
private Set<SubclassMappingOptions> getSubclassMappings(List<Parameter> sourceParameters, Type resultType,
|
||||||
ExecutableElement method, BeanMappingOptions beanMapping) {
|
ExecutableElement method, BeanMappingOptions beanMapping,
|
||||||
return new RepeatableSubclassMappings( sourceParameters, resultType ).getMappings( method, beanMapping );
|
SubclassValidator validator) {
|
||||||
|
return new RepeatableSubclassMappings( sourceParameters, resultType, validator )
|
||||||
|
.getMappings( method, beanMapping );
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RepeatableMappings extends RepeatableMappingAnnotations<MappingGem, MappingsGem, MappingOptions> {
|
private class RepeatableMappings extends RepeatableMappingAnnotations<MappingGem, MappingsGem, MappingOptions> {
|
||||||
@ -637,11 +643,13 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
extends RepeatableMappingAnnotations<SubclassMappingGem, SubclassMappingsGem, SubclassMappingOptions> {
|
extends RepeatableMappingAnnotations<SubclassMappingGem, SubclassMappingsGem, SubclassMappingOptions> {
|
||||||
private final List<Parameter> sourceParameters;
|
private final List<Parameter> sourceParameters;
|
||||||
private final Type resultType;
|
private final Type resultType;
|
||||||
|
private SubclassValidator validator;
|
||||||
|
|
||||||
RepeatableSubclassMappings(List<Parameter> sourceParameters, Type resultType) {
|
RepeatableSubclassMappings(List<Parameter> sourceParameters, Type resultType, SubclassValidator validator) {
|
||||||
super( SUB_CLASS_MAPPING_FQN, SUB_CLASS_MAPPINGS_FQN );
|
super( SUB_CLASS_MAPPING_FQN, SUB_CLASS_MAPPINGS_FQN );
|
||||||
this.sourceParameters = sourceParameters;
|
this.sourceParameters = sourceParameters;
|
||||||
this.resultType = resultType;
|
this.resultType = resultType;
|
||||||
|
this.validator = validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -666,7 +674,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
typeUtils,
|
typeUtils,
|
||||||
mappings,
|
mappings,
|
||||||
sourceParameters,
|
sourceParameters,
|
||||||
resultType );
|
resultType,
|
||||||
|
validator );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -681,7 +690,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
typeUtils,
|
typeUtils,
|
||||||
mappings,
|
mappings,
|
||||||
sourceParameters,
|
sourceParameters,
|
||||||
resultType );
|
resultType,
|
||||||
|
validator );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ public enum Message {
|
|||||||
ENUMMAPPING_NO_ELEMENTS( "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are undefined in @EnumMapping, define at least one of them." ),
|
ENUMMAPPING_NO_ELEMENTS( "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are undefined in @EnumMapping, define at least one of them." ),
|
||||||
ENUMMAPPING_ILLEGAL_TRANSFORMATION( "Illegal transformation for '%s' EnumTransformationStrategy. Error: '%s'." ),
|
ENUMMAPPING_ILLEGAL_TRANSFORMATION( "Illegal transformation for '%s' EnumTransformationStrategy. Error: '%s'." ),
|
||||||
|
|
||||||
|
SUBCLASSMAPPING_DOUBLE_SOURCE_SUBCLASS( "Subclass '%s' is already defined as a source." ),
|
||||||
SUBCLASSMAPPING_ILLEGAL_SUBCLASS( "Class '%s' is not a subclass of '%s'." ),
|
SUBCLASSMAPPING_ILLEGAL_SUBCLASS( "Class '%s' is not a subclass of '%s'." ),
|
||||||
SUBCLASSMAPPING_NO_VALID_SUPERCLASS( "Could not find a parameter that is a superclass for '%s'." ),
|
SUBCLASSMAPPING_NO_VALID_SUPERCLASS( "Could not find a parameter that is a superclass for '%s'." ),
|
||||||
SUBCLASSMAPPING_UPDATE_METHODS_NOT_SUPPORTED( "SubclassMapping annotation can not be used for update methods." ),
|
SUBCLASSMAPPING_UPDATE_METHODS_NOT_SUPPORTED( "SubclassMapping annotation can not be used for update methods." ),
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.subclassmapping;
|
||||||
|
|
||||||
|
import org.mapstruct.InheritInverseConfiguration;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.SubclassMapping;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.Bike;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.Car;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousInverseSubclassMapper {
|
||||||
|
ErroneousInverseSubclassMapper INSTANCE = Mappers.getMapper( ErroneousInverseSubclassMapper.class );
|
||||||
|
|
||||||
|
@SubclassMapping( source = Bike.class, target = VehicleDto.class )
|
||||||
|
@SubclassMapping( source = Car.class, target = VehicleDto.class )
|
||||||
|
@Mapping( target = "maker", source = "vehicleManufacturingCompany" )
|
||||||
|
VehicleDto map(Vehicle vehicle);
|
||||||
|
|
||||||
|
@InheritInverseConfiguration
|
||||||
|
Vehicle mapInverse(VehicleDto dto);
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.subclassmapping;
|
||||||
|
|
||||||
|
import org.mapstruct.InheritInverseConfiguration;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.SubclassMapping;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.Bike;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.Car;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.CarDto;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.HatchBack;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollection;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollectionDto;
|
||||||
|
import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface InverseOrderSubclassMapper {
|
||||||
|
InverseOrderSubclassMapper INSTANCE = Mappers.getMapper( InverseOrderSubclassMapper.class );
|
||||||
|
|
||||||
|
VehicleCollectionDto map(VehicleCollection vehicles);
|
||||||
|
|
||||||
|
@SubclassMapping( source = HatchBack.class, target = CarDto.class )
|
||||||
|
@SubclassMapping( source = Car.class, target = CarDto.class )
|
||||||
|
@SubclassMapping( source = Bike.class, target = BikeDto.class )
|
||||||
|
@Mapping( source = "vehicleManufacturingCompany", target = "maker")
|
||||||
|
VehicleDto map(Vehicle vehicle);
|
||||||
|
|
||||||
|
VehicleCollection mapInverse(VehicleCollectionDto vehicles);
|
||||||
|
|
||||||
|
@SubclassMapping( source = CarDto.class, target = Car.class )
|
||||||
|
@InheritInverseConfiguration
|
||||||
|
Vehicle mapInverse(VehicleDto dto);
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.test.subclassmapping;
|
package org.mapstruct.ap.test.subclassmapping;
|
||||||
|
|
||||||
|
import org.mapstruct.InheritInverseConfiguration;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.SubclassMapping;
|
import org.mapstruct.SubclassMapping;
|
||||||
@ -28,4 +29,9 @@ public interface SimpleSubclassMapper {
|
|||||||
@SubclassMapping( source = Bike.class, target = BikeDto.class )
|
@SubclassMapping( source = Bike.class, target = BikeDto.class )
|
||||||
@Mapping( source = "vehicleManufacturingCompany", target = "maker")
|
@Mapping( source = "vehicleManufacturingCompany", target = "maker")
|
||||||
VehicleDto map(Vehicle vehicle);
|
VehicleDto map(Vehicle vehicle);
|
||||||
|
|
||||||
|
VehicleCollection mapInverse(VehicleCollectionDto vehicles);
|
||||||
|
|
||||||
|
@InheritInverseConfiguration
|
||||||
|
Vehicle mapInverse(VehicleDto dto);
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
VehicleCollectionDto.class,
|
VehicleCollectionDto.class,
|
||||||
Vehicle.class,
|
Vehicle.class,
|
||||||
VehicleDto.class,
|
VehicleDto.class,
|
||||||
SimpleSubclassMapper.class,
|
|
||||||
SubclassMapperUsingExistingMappings.class,
|
|
||||||
})
|
})
|
||||||
public class SubclassMappingTest {
|
public class SubclassMappingTest {
|
||||||
|
|
||||||
@ProcessorTest
|
@ProcessorTest
|
||||||
|
@WithClasses( SimpleSubclassMapper.class )
|
||||||
void mappingIsDoneUsingSubclassMapping() {
|
void mappingIsDoneUsingSubclassMapping() {
|
||||||
VehicleCollection vehicles = new VehicleCollection();
|
VehicleCollection vehicles = new VehicleCollection();
|
||||||
vehicles.getVehicles().add( new Car() );
|
vehicles.getVehicles().add( new Car() );
|
||||||
@ -54,6 +53,22 @@ public class SubclassMappingTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ProcessorTest
|
@ProcessorTest
|
||||||
|
@WithClasses( SimpleSubclassMapper.class )
|
||||||
|
void inverseMappingIsDoneUsingSubclassMapping() {
|
||||||
|
VehicleCollectionDto vehicles = new VehicleCollectionDto();
|
||||||
|
vehicles.getVehicles().add( new CarDto() );
|
||||||
|
vehicles.getVehicles().add( new BikeDto() );
|
||||||
|
|
||||||
|
VehicleCollection result = SimpleSubclassMapper.INSTANCE.mapInverse( vehicles );
|
||||||
|
|
||||||
|
assertThat( result.getVehicles() ).doesNotContainNull();
|
||||||
|
assertThat( result.getVehicles() ) // remove generic so that test works.
|
||||||
|
.extracting( vehicle -> (Class) vehicle.getClass() )
|
||||||
|
.containsExactly( Car.class, Bike.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
@WithClasses( SubclassMapperUsingExistingMappings.class )
|
||||||
void existingMappingsAreUsedWhenFound() {
|
void existingMappingsAreUsedWhenFound() {
|
||||||
VehicleCollection vehicles = new VehicleCollection();
|
VehicleCollection vehicles = new VehicleCollection();
|
||||||
vehicles.getVehicles().add( new Car() );
|
vehicles.getVehicles().add( new Car() );
|
||||||
@ -66,19 +81,38 @@ public class SubclassMappingTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ProcessorTest
|
@ProcessorTest
|
||||||
void subclassMappingInheritsMapping() {
|
@WithClasses( SimpleSubclassMapper.class )
|
||||||
VehicleCollection vehicles = new VehicleCollection();
|
void subclassMappingInheritsInverseMapping() {
|
||||||
Car car = new Car();
|
VehicleCollectionDto vehiclesDto = new VehicleCollectionDto();
|
||||||
car.setVehicleManufacturingCompany( "BenZ" );
|
CarDto carDto = new CarDto();
|
||||||
vehicles.getVehicles().add( car );
|
carDto.setMaker( "BenZ" );
|
||||||
|
vehiclesDto.getVehicles().add( carDto );
|
||||||
|
|
||||||
VehicleCollectionDto result = SimpleSubclassMapper.INSTANCE.map( vehicles );
|
VehicleCollection result = SimpleSubclassMapper.INSTANCE.mapInverse( vehiclesDto );
|
||||||
|
|
||||||
assertThat( result.getVehicles() )
|
assertThat( result.getVehicles() )
|
||||||
.extracting( VehicleDto::getMaker )
|
.extracting( Vehicle::getVehicleManufacturingCompany )
|
||||||
.containsExactly( "BenZ" );
|
.containsExactly( "BenZ" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
@WithClasses( {
|
||||||
|
HatchBack.class,
|
||||||
|
InverseOrderSubclassMapper.class
|
||||||
|
} )
|
||||||
|
void subclassMappingOverridesInverseInheritsMapping() {
|
||||||
|
VehicleCollectionDto vehicleDtos = new VehicleCollectionDto();
|
||||||
|
CarDto carDto = new CarDto();
|
||||||
|
carDto.setMaker( "BenZ" );
|
||||||
|
vehicleDtos.getVehicles().add( carDto );
|
||||||
|
|
||||||
|
VehicleCollection result = InverseOrderSubclassMapper.INSTANCE.mapInverse( vehicleDtos );
|
||||||
|
|
||||||
|
assertThat( result.getVehicles() ) // remove generic so that test works.
|
||||||
|
.extracting( vehicle -> (Class) vehicle.getClass() )
|
||||||
|
.containsExactly( Car.class );
|
||||||
|
}
|
||||||
|
|
||||||
@ProcessorTest
|
@ProcessorTest
|
||||||
@WithClasses({
|
@WithClasses({
|
||||||
HatchBack.class,
|
HatchBack.class,
|
||||||
@ -91,11 +125,11 @@ public class SubclassMappingTest {
|
|||||||
line = 28,
|
line = 28,
|
||||||
alternativeLine = 30,
|
alternativeLine = 30,
|
||||||
message = "SubclassMapping annotation for "
|
message = "SubclassMapping annotation for "
|
||||||
+ "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto' found after "
|
+ "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBack' found after "
|
||||||
+ "'org.mapstruct.ap.test.subclassmapping.mappables.CarDto', but all "
|
+ "'org.mapstruct.ap.test.subclassmapping.mappables.Car', but all "
|
||||||
+ "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto' "
|
+ "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBack' "
|
||||||
+ "objects are also instances of "
|
+ "objects are also instances of "
|
||||||
+ "'org.mapstruct.ap.test.subclassmapping.mappables.CarDto'.")
|
+ "'org.mapstruct.ap.test.subclassmapping.mappables.Car'.")
|
||||||
})
|
})
|
||||||
void subclassOrderWarning() {
|
void subclassOrderWarning() {
|
||||||
}
|
}
|
||||||
@ -136,4 +170,18 @@ public class SubclassMappingTest {
|
|||||||
})
|
})
|
||||||
void erroneousMethodWithSourceTargetType() {
|
void erroneousMethodWithSourceTargetType() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
@WithClasses({ ErroneousInverseSubclassMapper.class })
|
||||||
|
@ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = {
|
||||||
|
@Diagnostic(type = ErroneousInverseSubclassMapper.class,
|
||||||
|
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||||
|
line = 28,
|
||||||
|
message = "Subclass "
|
||||||
|
+ "'org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto'"
|
||||||
|
+ " is already defined as a source."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
void inverseSubclassMappingNotPossible() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.subclassmapping.fixture;
|
||||||
|
|
||||||
|
public class SubSourceOverride extends ImplementedParentSource implements InterfaceParentSource {
|
||||||
|
private final String finalValue;
|
||||||
|
|
||||||
|
public SubSourceOverride(String finalValue) {
|
||||||
|
this.finalValue = finalValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFinalValue() {
|
||||||
|
return finalValue;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.subclassmapping.fixture;
|
||||||
|
|
||||||
|
public class SubSourceSeparate extends ImplementedParentSource implements InterfaceParentSource {
|
||||||
|
private final String separateValue;
|
||||||
|
|
||||||
|
public SubSourceSeparate(String separateValue) {
|
||||||
|
this.separateValue = separateValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSeparateValue() {
|
||||||
|
return separateValue;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.subclassmapping.fixture;
|
||||||
|
|
||||||
|
public class SubTargetSeparate extends ImplementedParentTarget implements InterfaceParentTarget {
|
||||||
|
private final String separateValue;
|
||||||
|
|
||||||
|
public SubTargetSeparate(String separateValue) {
|
||||||
|
this.separateValue = separateValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSeparateValue() {
|
||||||
|
return separateValue;
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
package org.mapstruct.ap.test.subclassmapping.fixture;
|
package org.mapstruct.ap.test.subclassmapping.fixture;
|
||||||
|
|
||||||
import org.mapstruct.BeanMapping;
|
import org.mapstruct.BeanMapping;
|
||||||
|
import org.mapstruct.InheritInverseConfiguration;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.SubclassMapping;
|
import org.mapstruct.SubclassMapping;
|
||||||
|
|
||||||
@ -18,4 +19,9 @@ public interface SubclassAbstractMapper {
|
|||||||
@SubclassMapping( source = SubSource.class, target = SubTarget.class )
|
@SubclassMapping( source = SubSource.class, target = SubTarget.class )
|
||||||
@SubclassMapping( source = SubSourceOther.class, target = SubTargetOther.class )
|
@SubclassMapping( source = SubSourceOther.class, target = SubTargetOther.class )
|
||||||
AbstractParentTarget map(AbstractParentSource item);
|
AbstractParentTarget map(AbstractParentSource item);
|
||||||
|
|
||||||
|
@SubclassMapping( source = SubTargetSeparate.class, target = SubSourceSeparate.class )
|
||||||
|
@InheritInverseConfiguration
|
||||||
|
@SubclassMapping( source = SubTargetOther.class, target = SubSourceOverride.class )
|
||||||
|
AbstractParentSource map(AbstractParentTarget item);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package org.mapstruct.ap.test.subclassmapping.fixture;
|
package org.mapstruct.ap.test.subclassmapping.fixture;
|
||||||
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
import org.mapstruct.ap.testutil.ProcessorTest;
|
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||||
import org.mapstruct.ap.testutil.WithClasses;
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
import org.mapstruct.ap.testutil.runner.GeneratedSource;
|
import org.mapstruct.ap.testutil.runner.GeneratedSource;
|
||||||
@ -37,6 +38,9 @@ public class SubclassFixtureTest {
|
|||||||
|
|
||||||
@ProcessorTest
|
@ProcessorTest
|
||||||
@WithClasses( {
|
@WithClasses( {
|
||||||
|
SubSourceOverride.class,
|
||||||
|
SubSourceSeparate.class,
|
||||||
|
SubTargetSeparate.class,
|
||||||
SubclassAbstractMapper.class,
|
SubclassAbstractMapper.class,
|
||||||
} )
|
} )
|
||||||
void subclassAbstractParentFixture() {
|
void subclassAbstractParentFixture() {
|
||||||
|
@ -9,7 +9,7 @@ import javax.annotation.processing.Generated;
|
|||||||
|
|
||||||
@Generated(
|
@Generated(
|
||||||
value = "org.mapstruct.ap.MappingProcessor",
|
value = "org.mapstruct.ap.MappingProcessor",
|
||||||
date = "2021-09-12T14:37:10+0200",
|
date = "2022-01-31T19:35:15+0100",
|
||||||
comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.12 (Azul Systems, Inc.)"
|
comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.12 (Azul Systems, Inc.)"
|
||||||
)
|
)
|
||||||
public class SubclassAbstractMapperImpl implements SubclassAbstractMapper {
|
public class SubclassAbstractMapperImpl implements SubclassAbstractMapper {
|
||||||
@ -31,6 +31,26 @@ public class SubclassAbstractMapperImpl implements SubclassAbstractMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractParentSource map(AbstractParentTarget item) {
|
||||||
|
if ( item == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item instanceof SubTargetSeparate) {
|
||||||
|
return subTargetSeparateToSubSourceSeparate( (SubTargetSeparate) item );
|
||||||
|
}
|
||||||
|
else if (item instanceof SubTargetOther) {
|
||||||
|
return subTargetOtherToSubSourceOverride( (SubTargetOther) item );
|
||||||
|
}
|
||||||
|
else if (item instanceof SubTarget) {
|
||||||
|
return subTargetToSubSource( (SubTarget) item );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + item.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected SubTarget subSourceToSubTarget(SubSource subSource) {
|
protected SubTarget subSourceToSubTarget(SubSource subSource) {
|
||||||
if ( subSource == null ) {
|
if ( subSource == null ) {
|
||||||
return null;
|
return null;
|
||||||
@ -56,4 +76,44 @@ public class SubclassAbstractMapperImpl implements SubclassAbstractMapper {
|
|||||||
|
|
||||||
return subTargetOther;
|
return subTargetOther;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected SubSourceSeparate subTargetSeparateToSubSourceSeparate(SubTargetSeparate subTargetSeparate) {
|
||||||
|
if ( subTargetSeparate == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String separateValue = null;
|
||||||
|
|
||||||
|
separateValue = subTargetSeparate.getSeparateValue();
|
||||||
|
|
||||||
|
SubSourceSeparate subSourceSeparate = new SubSourceSeparate( separateValue );
|
||||||
|
|
||||||
|
return subSourceSeparate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SubSourceOverride subTargetOtherToSubSourceOverride(SubTargetOther subTargetOther) {
|
||||||
|
if ( subTargetOther == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String finalValue = null;
|
||||||
|
|
||||||
|
finalValue = subTargetOther.getFinalValue();
|
||||||
|
|
||||||
|
SubSourceOverride subSourceOverride = new SubSourceOverride( finalValue );
|
||||||
|
|
||||||
|
return subSourceOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SubSource subTargetToSubSource(SubTarget subTarget) {
|
||||||
|
if ( subTarget == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubSource subSource = new SubSource();
|
||||||
|
|
||||||
|
subSource.setValue( subTarget.getValue() );
|
||||||
|
|
||||||
|
return subSource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user