diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java index 3730ac13f..01a256993 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java @@ -91,12 +91,6 @@ public class Mapping { return mappings; } - private static boolean isEnumType(TypeMirror mirror) { - return mirror.getKind() == TypeKind.DECLARED && - ( (DeclaredType) mirror ).asElement().getKind() == ElementKind.ENUM; - } - - public static Mapping fromMappingPrism(MappingPrism mappingPrism, ExecutableElement element, Messager messager) { if ( mappingPrism.target().isEmpty() ) { @@ -154,6 +148,22 @@ public class Mapping { ); } + private Mapping(String sourceName, String constant, String javaExpression, String targetName, + String dateFormat, List qualifiers, + boolean isIgnored, AnnotationMirror mirror, + AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) { + this.sourceName = sourceName; + this.constant = constant; + this.javaExpression = javaExpression; + this.targetName = targetName; + this.dateFormat = dateFormat; + this.qualifiers = qualifiers; + this.isIgnored = isIgnored; + this.mirror = mirror; + this.sourceAnnotationValue = sourceAnnotationValue; + this.targetAnnotationValue = targetAnnotationValue; + } + private static String getExpression(MappingPrism mappingPrism, ExecutableElement element, Messager messager) { if ( mappingPrism.expression().isEmpty() ) { return null; @@ -175,20 +185,9 @@ public class Mapping { return javaExpressionMatcher.group( 1 ).trim(); } - private Mapping(String sourceName, String constant, String javaExpression, String targetName, - String dateFormat, List qualifiers, - boolean isIgnored, AnnotationMirror mirror, - AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) { - this.sourceName = sourceName; - this.constant = constant; - this.javaExpression = javaExpression; - this.targetName = targetName; - this.dateFormat = dateFormat; - this.qualifiers = qualifiers; - this.isIgnored = isIgnored; - this.mirror = mirror; - this.sourceAnnotationValue = sourceAnnotationValue; - this.targetAnnotationValue = targetAnnotationValue; + private static boolean isEnumType(TypeMirror mirror) { + return mirror.getKind() == TypeKind.DECLARED && + ( (DeclaredType) mirror ).asElement().getKind() == ElementKind.ENUM; } public void init(SourceMethod method, Messager messager, TypeFactory typeFactory) { @@ -253,21 +252,20 @@ public class Mapping { return sourceReference; } - private boolean hasPropertyInReverseMethod( String name, SourceMethod method ) { - boolean match = false; + private boolean hasPropertyInReverseMethod(String name, SourceMethod method) { for ( ExecutableElement getter : method.getResultType().getGetters() ) { if ( Executables.getPropertyName( getter ).equals( name ) ) { - match = true; - break; + return true; } } + for ( ExecutableElement getter : method.getResultType().getAlternativeTargetAccessors() ) { if ( Executables.getPropertyName( getter ).equals( name ) ) { - match = true; - break; + return true; } } - return match; + + return false; } public Mapping reverse(SourceMethod method, Messager messager, TypeFactory typeFactory) { @@ -285,18 +283,18 @@ public class Mapping { } // should not reverse a nested property - if (sourceReference != null && sourceReference.getPropertyEntries().size() > 1 ) { + if ( sourceReference != null && sourceReference.getPropertyEntries().size() > 1 ) { return null; } // should generate error when parameter - if (sourceReference != null && sourceReference.getPropertyEntries().isEmpty() ) { - // parameter mapping only, apparantly the @InheritReverseConfiguration is intentional + if ( sourceReference != null && sourceReference.getPropertyEntries().isEmpty() ) { + // parameter mapping only, apparently the @InheritReverseConfiguration is intentional // but erroneous. Lets raise an error to warn. messager.printMessage( - Diagnostic.Kind.ERROR, - String.format( "Parameter %s cannot be reversed", sourceReference.getParameter() ), - method.getExecutable() + Diagnostic.Kind.ERROR, + String.format( "Parameter %s cannot be reversed", sourceReference.getParameter() ), + method.getExecutable() ); return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java index f97a46274..576baf676 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java @@ -20,10 +20,11 @@ package org.mapstruct.ap.model.source; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.processing.Messager; +import javax.annotation.processing.Messager; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.util.Types; @@ -47,20 +48,22 @@ import org.mapstruct.ap.util.Strings; */ public class SourceMethod implements Method { + private final Types typeUtils; + private final TypeFactory typeFactory; + private final Messager messager; + private final Type declaringMapper; private final ExecutableElement executable; private final List parameters; private final Parameter targetParameter; private final Type returnType; private final Accessibility accessibility; - private final Types typeUtils; private final List exceptionTypes; private Map> mappings; private IterableMapping iterableMapping; private MapMapping mapMapping; - public static SourceMethod forMethodRequiringImplementation(ExecutableElement executable, List parameters, Type returnType, @@ -69,7 +72,7 @@ public class SourceMethod implements Method { IterableMapping iterableMapping, MapMapping mapMapping, Types typeUtils, Messager messager, - TypeFactory typeFactory ) { + TypeFactory typeFactory) { SourceMethod sourceMethod = new SourceMethod( null, @@ -80,7 +83,11 @@ public class SourceMethod implements Method { mappings, iterableMapping, mapMapping, - typeUtils ); + typeUtils, + typeFactory, + messager + ); + for ( Map.Entry> entry : sourceMethod.getMappings().entrySet() ) { for ( Mapping mapping : entry.getValue() ) { mapping.init( sourceMethod, messager, typeFactory ); @@ -102,7 +109,9 @@ public class SourceMethod implements Method { Collections.>emptyMap(), null, null, - typeUtils + typeUtils, + null, + null ); } @@ -118,13 +127,17 @@ public class SourceMethod implements Method { Collections.>emptyMap(), null, null, - typeUtils + typeUtils, + null, + null ); } + //CHECKSTYLE:OFF private SourceMethod(Type declaringMapper, ExecutableElement executable, List parameters, Type returnType, List exceptionTypes, Map> mappings, - IterableMapping iterableMapping, MapMapping mapMapping, Types typeUtils) { + IterableMapping iterableMapping, MapMapping mapMapping, Types typeUtils, + TypeFactory typeFactory, Messager messager) { this.declaringMapper = declaringMapper; this.executable = executable; this.parameters = parameters; @@ -138,7 +151,10 @@ public class SourceMethod implements Method { this.targetParameter = determineTargetParameter( parameters ); this.typeUtils = typeUtils; + this.typeFactory = typeFactory; + this.messager = messager; } + //CHECKSTYLE:ON private Parameter determineTargetParameter(Iterable parameters) { for ( Parameter parameter : parameters ) { @@ -306,7 +322,9 @@ public class SourceMethod implements Method { /** * Returns the {@link Mapping}s for the given source property. + * * @param sourcePropertyName the source property name + * * @return list of mappings */ public List getMappingBySourcePropertyName(String sourcePropertyName) { @@ -367,7 +385,7 @@ public class SourceMethod implements Method { /** * @param parameters the parameter list to check * - * @return true, iff the parameter list contains a parameter annotated with {@code @TargetType} + * @return {@code true} if the parameter list contains a parameter annotated with {@code @TargetType} */ public static boolean containsTargetTypeParameter(List parameters) { for ( Parameter param : parameters ) { @@ -383,4 +401,40 @@ public class SourceMethod implements Method { public List getThrownTypes() { return exceptionTypes; } + + /** + * Merges in all the mappings configured via the given inverse mapping method, giving the locally defined mappings + * precedence. + */ + public void mergeWithInverseMappings(SourceMethod inverseMethod) { + Map> newMappings = new HashMap>(); + + if ( inverseMethod != null && !inverseMethod.getMappings().isEmpty() ) { + for ( List mappings : inverseMethod.getMappings().values() ) { + for ( Mapping inverseMapping : mappings ) { + Mapping reversed = inverseMapping.reverse( this, messager, typeFactory ); + if ( reversed != null ) { + List mappingsOfProperty = newMappings.get( reversed.getTargetName() ); + if ( mappingsOfProperty == null ) { + mappingsOfProperty = new ArrayList(); + newMappings.put( reversed.getTargetName(), mappingsOfProperty ); + } + mappingsOfProperty.add( reversed ); + } + } + } + } + + 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 ); + } + } } diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java index 0a183ac7b..fe4dace1d 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java @@ -21,6 +21,7 @@ package org.mapstruct.ap.model.source; import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import javax.annotation.processing.Messager; import javax.lang.model.element.ExecutableElement; import javax.tools.Diagnostic; @@ -38,8 +39,6 @@ import org.mapstruct.ap.util.Strings; * mapping method: * * {@code - * - * @author Sjaak Derksen * @Mapping( source = "in.propA.propB" target = "propC" ) * TypeB mappingMethod ( TypeA in ); * } @@ -52,6 +51,8 @@ import org.mapstruct.ap.util.Strings; * * * After building, {@link #isValid()} will return true when when no problems are detected during building. + * + * @author Sjaak Derksen */ public class SourceReference { diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java index 3e073fb16..4dbbf3e79 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -19,10 +19,8 @@ package org.mapstruct.ap.processor; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import javax.annotation.processing.Messager; @@ -49,7 +47,6 @@ import org.mapstruct.ap.model.MappingBuilderContext; import org.mapstruct.ap.model.MappingMethod; import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.TypeFactory; -import org.mapstruct.ap.model.source.Mapping; import org.mapstruct.ap.model.source.SourceMethod; import org.mapstruct.ap.option.Options; import org.mapstruct.ap.prism.DecoratedWithPrism; @@ -249,15 +246,15 @@ public class MapperCreationProcessor implements ModelElementProcessor> newMappings = new HashMap>(); - - if ( forwardMappingMethod != null && !forwardMappingMethod.getMappings().isEmpty() ) { - for ( List mappings : forwardMappingMethod.getMappings().values() ) { - for ( Mapping forwardMapping : mappings ) { - Mapping reversed = forwardMapping.reverse( method, messager, typeFactory ); - if ( reversed != null ) { - List mappingsOfProperty = newMappings.get( reversed.getTargetName() ); - if ( mappingsOfProperty == null ) { - mappingsOfProperty = new ArrayList(); - newMappings.put( reversed.getTargetName(), mappingsOfProperty ); - } - mappingsOfProperty.add( reversed ); - } - } - } - } - - if ( method.getMappings().isEmpty() ) { - // the mapping method is configuredByReverseMappingMethod, see SourceMethod#setMappings() - method.setMappings( newMappings ); - } - else { - // now add all of its own mappings - newMappings.putAll( method.getMappings() ); - method.getMappings().clear(); - // the mapping method is NOT configuredByReverseMappingMethod, - method.getMappings().putAll( newMappings ); - } - } - - private SourceMethod getReverseMappingMethod(List rawMethods, SourceMethod method) { + /** + * Returns the configuring inverse method in case the given method is annotated with + * {@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}). + */ + private SourceMethod getInverseMappingMethod(List rawMethods, SourceMethod method) { SourceMethod result = null; InheritInverseConfigurationPrism reversePrism = InheritInverseConfigurationPrism.getInstanceOn( method.getExecutable()