From 2215f17250b4953ada44d36135be6c8a293ae962 Mon Sep 17 00:00:00 2001 From: sjaakd Date: Sun, 28 Apr 2019 20:55:43 +0200 Subject: [PATCH] #1742 refactor accessors (make consistent) to have accessed type always available --- .../ap/internal/model/BeanMappingMethod.java | 2 +- .../model/CollectionAssignmentBuilder.java | 18 +-- .../ap/internal/model/PropertyMapping.java | 74 +++--------- .../ap/internal/model/common/Type.java | 99 +++++++++------- .../ap/internal/model/common/TypeFactory.java | 11 +- .../internal/model/source/PropertyEntry.java | 8 +- .../model/source/SourceReference.java | 6 +- .../model/source/TargetReference.java | 8 +- .../ap/internal/util/AccessorNamingUtils.java | 44 +++---- .../ap/internal/util/Executables.java | 103 +++++------------ .../mapstruct/ap/internal/util/Fields.java | 107 ++++++++++++++++++ .../mapstruct/ap/internal/util/Filters.java | 74 ++++++++---- .../ap/internal/util/ValueProvider.java | 3 +- .../util/accessor/AbstractAccessor.java | 1 + .../ap/internal/util/accessor/Accessor.java | 13 ++- .../internal/util/accessor/AccessorType.java | 15 +++ .../accessor/ExecutableElementAccessor.java | 17 ++- .../accessor/VariableElementAccessor.java | 9 +- 18 files changed, 354 insertions(+), 258 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 5324f4b904..393d799cf1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -657,7 +657,7 @@ private void applyPropertyNameBasedMapping() { Accessor sourceReadAccessor = sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName ); - ExecutableElementAccessor sourcePresenceChecker = + Accessor sourcePresenceChecker = sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName ); if ( sourceReadAccessor != null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index ad9defb9d2..6422a354d4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -20,6 +20,7 @@ import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT; import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL; @@ -57,7 +58,7 @@ public class CollectionAssignmentBuilder { private Accessor targetReadAccessor; private Type targetType; private String targetPropertyName; - private PropertyMapping.TargetWriteAccessorType targetAccessorType; + private AccessorType targetAccessorType; private Assignment assignment; private SourceRHS sourceRHS; private NullValueCheckStrategyPrism nvcs; @@ -88,7 +89,7 @@ public CollectionAssignmentBuilder targetPropertyName(String targetPropertyName) return this; } - public CollectionAssignmentBuilder targetAccessorType(PropertyMapping.TargetWriteAccessorType targetAccessorType) { + public CollectionAssignmentBuilder targetAccessorType(AccessorType targetAccessorType) { this.targetAccessorType = targetAccessorType; return this; } @@ -129,8 +130,7 @@ public Assignment build() { CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy(); boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE || targetReadAccessor == null; - if ( targetAccessorType == PropertyMapping.TargetWriteAccessorType.SETTER || - targetAccessorType == PropertyMapping.TargetWriteAccessorType.FIELD ) { + if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) { if ( result.isCallingUpdateMethod() && !targetImmutable ) { @@ -149,7 +149,7 @@ public Assignment build() { result, method.getThrownTypes(), factoryMethod, - PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ), + targetAccessorType == AccessorType.FIELD, targetType, true, nvpms == SET_TO_NULL && !targetType.isPrimitive(), @@ -165,7 +165,7 @@ else if ( method.isUpdateMethod() && !targetImmutable ) { nvcs, nvpms, ctx.getTypeFactory(), - PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ) + targetAccessorType == AccessorType.FIELD ); } else if ( result.getType() == Assignment.AssignmentType.DIRECT || @@ -176,7 +176,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT || method.getThrownTypes(), targetType, ctx.getTypeFactory(), - PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ) + targetAccessorType == AccessorType.FIELD ); } else { @@ -185,7 +185,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT || result, method.getThrownTypes(), targetType, - PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ) + targetAccessorType == AccessorType.FIELD ); } } @@ -203,7 +203,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT || result, method.getThrownTypes(), targetType, - PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ) + targetAccessorType == AccessorType.FIELD ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 3dadd81161..57cb84bb80 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -49,6 +49,7 @@ import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.ValueProvider; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT; import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT; @@ -74,37 +75,12 @@ public class PropertyMapping extends ModelElement { private final List dependsOn; private final Assignment defaultValueAssignment; - public enum TargetWriteAccessorType { - FIELD, - GETTER, - SETTER, - ADDER; - - public static TargetWriteAccessorType of(AccessorNamingUtils accessorNaming, Accessor accessor) { - if ( accessorNaming.isSetterMethod( accessor ) ) { - return TargetWriteAccessorType.SETTER; - } - else if ( accessorNaming.isAdderMethod( accessor ) ) { - return TargetWriteAccessorType.ADDER; - } - else if ( accessorNaming.isGetterMethod( accessor ) ) { - return TargetWriteAccessorType.GETTER; - } - else { - return TargetWriteAccessorType.FIELD; - } - } - - public static boolean isFieldAssignment(TargetWriteAccessorType accessorType) { - return accessorType == FIELD; - } - } @SuppressWarnings("unchecked") private static class MappingBuilderBase> extends AbstractBaseBuilder { protected Accessor targetWriteAccessor; - protected TargetWriteAccessorType targetWriteAccessorType; + protected AccessorType targetWriteAccessorType; protected Type targetType; protected BuilderType targetBuilderType; protected Accessor targetReadAccessor; @@ -128,7 +104,7 @@ public T targetProperty(PropertyEntry targetProp) { this.targetWriteAccessor = targetProp.getWriteAccessor(); this.targetType = targetProp.getType(); this.targetBuilderType = targetProp.getBuilderType(); - this.targetWriteAccessorType = TargetWriteAccessorType.of( ctx.getAccessorNaming(), targetWriteAccessor ); + this.targetWriteAccessorType = targetWriteAccessor.getAccessorType(); return (T) this; } @@ -139,7 +115,7 @@ public T targetReadAccessor(Accessor targetReadAccessor) { public T targetWriteAccessor(Accessor targetWriteAccessor) { this.targetWriteAccessor = targetWriteAccessor; - this.targetWriteAccessorType = TargetWriteAccessorType.of( ctx.getAccessorNaming(), targetWriteAccessor ); + this.targetWriteAccessorType = targetWriteAccessor.getAccessorType(); this.targetType = determineTargetType(); return (T) this; @@ -151,22 +127,7 @@ T mirror(AnnotationMirror mirror) { } private Type determineTargetType() { - // This is a bean mapping method, so we know the result is a declared type - Type mappingType = method.getResultType(); - DeclaredType resultType = (DeclaredType) mappingType.getTypeMirror(); - - switch ( targetWriteAccessorType ) { - case ADDER: - case SETTER: - return ctx.getTypeFactory() - .getSingleParameter( resultType, targetWriteAccessor ) - .getType(); - case GETTER: - case FIELD: - default: - return ctx.getTypeFactory() - .getReturnType( resultType, targetWriteAccessor ); - } + return ctx.getTypeFactory().getType( targetWriteAccessor.getAccessedType() ); } public T targetPropertyName(String targetPropertyName) { @@ -190,7 +151,7 @@ public T existingVariableNames(Set existingVariableNames) { } protected boolean isFieldAssignment() { - return targetWriteAccessorType == TargetWriteAccessorType.FIELD; + return targetWriteAccessorType == AccessorType.FIELD; } } @@ -300,11 +261,11 @@ public PropertyMapping build() { ctx.getMessager().note( 2, Message.PROPERTYMAPPING_MAPPING_NOTE, rightHandSide, targetWriteAccessor ); rightHandSide.setUseElementAsSourceTypeForMatching( - targetWriteAccessorType == TargetWriteAccessorType.ADDER ); + targetWriteAccessorType == AccessorType.ADDER ); // all the tricky cases will be excluded for the time being. boolean preferUpdateMethods; - if ( targetWriteAccessorType == TargetWriteAccessorType.ADDER ) { + if ( targetWriteAccessorType == AccessorType.ADDER ) { preferUpdateMethods = false; } else { @@ -446,13 +407,12 @@ private Assignment getDefaultValueAssignment( Assignment rhs ) { return null; } - private Assignment assignToPlain(Type targetType, TargetWriteAccessorType targetAccessorType, + private Assignment assignToPlain(Type targetType, AccessorType targetAccessorType, Assignment rightHandSide) { Assignment result; - if ( targetAccessorType == TargetWriteAccessorType.SETTER || - targetAccessorType == TargetWriteAccessorType.FIELD ) { + if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) { result = assignToPlainViaSetter( targetType, rightHandSide ); } else { @@ -530,7 +490,7 @@ else if ( result.getSourceType().isStreamType() ) { return result; } - private Assignment assignToCollection(Type targetType, TargetWriteAccessorType targetAccessorType, + private Assignment assignToCollection(Type targetType, AccessorType targetAccessorType, Assignment rhs) { return new CollectionAssignmentBuilder() .mappingBuilderContext( ctx ) @@ -738,7 +698,7 @@ private Assignment forgeMapMapping(Type sourceType, Type targetType, SourceRHS s private Assignment forgeMapping(SourceRHS sourceRHS) { Type sourceType; - if ( targetWriteAccessorType == TargetWriteAccessorType.ADDER ) { + if ( targetWriteAccessorType == AccessorType.ADDER ) { sourceType = sourceRHS.getSourceTypeForMatching(); } else { @@ -764,7 +724,7 @@ private Assignment forgeMapping(SourceRHS sourceRHS) { // because we are forging a Mapping for a method with multiple source parameters. // If the target type is enum, then we can't create an update method if ( !targetType.isEnumType() && ( method.isUpdateMethod() || forceUpdateMethod ) - && targetWriteAccessorType != TargetWriteAccessorType.ADDER) { + && targetWriteAccessorType != AccessorType.ADDER) { parameters.add( Parameter.forForgedMappingTarget( targetType ) ); returnType = ctx.getTypeFactory().createVoidType(); } @@ -899,8 +859,8 @@ public PropertyMapping build() { if ( assignment != null ) { - if ( ctx.getAccessorNaming().isSetterMethod( targetWriteAccessor ) || - Executables.isFieldAccessor( targetWriteAccessor ) ) { + if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER || + targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) { // target accessor is setter, so decorate assignment as setter if ( assignment.isCallingUpdateMethod() ) { @@ -1015,8 +975,8 @@ public JavaExpressionMappingBuilder javaExpression(String javaExpression) { public PropertyMapping build() { Assignment assignment = new SourceRHS( javaExpression, null, existingVariableNames, "" ); - if ( ctx.getAccessorNaming().isSetterMethod( targetWriteAccessor ) || - Executables.isFieldAccessor( targetWriteAccessor ) ) { + if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER || + targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) { // setter, so wrap in setter assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index a4f1e2df1c..1b8f233106 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; @@ -34,11 +35,12 @@ import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.Executables; +import org.mapstruct.ap.internal.util.Fields; import org.mapstruct.ap.internal.util.Filters; import org.mapstruct.ap.internal.util.JavaStreamConstants; import org.mapstruct.ap.internal.util.Nouns; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; import static org.mapstruct.ap.internal.util.Collections.first; import org.mapstruct.ap.internal.util.NativeTypes; @@ -87,9 +89,10 @@ public class Type extends ModelElement implements Comparable { private Boolean isToBeImported; private Map readAccessors = null; - private Map presenceCheckers = null; + private Map presenceCheckers = null; - private List allAccessors = null; + private List allMethods = null; + private List allFields = null; private List setters = null; private List adders = null; private List alternativeTargetAccessors = null; @@ -98,6 +101,8 @@ public class Type extends ModelElement implements Comparable { private Boolean hasEmptyAccessibleContructor; + private final Filters filters; + //CHECKSTYLE:OFF public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory, AccessorNamingUtils accessorNaming, @@ -154,6 +159,7 @@ public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory, this.isToBeImported = isToBeImported; this.toBeImportedTypes = toBeImportedTypes; this.notToBeImportedTypes = notToBeImportedTypes; + this.filters = new Filters( accessorNaming, typeUtils, typeMirror ); } //CHECKSTYLE:ON @@ -487,30 +493,30 @@ public boolean isAssignableTo(Type other) { */ public Map getPropertyReadAccessors() { if ( readAccessors == null ) { - List getterList = Filters.getterMethodsIn( accessorNaming, getAllAccessors() ); + List getterList = filters.getterMethodsIn( getAllMethods() ); Map modifiableGetters = new LinkedHashMap<>(); for ( Accessor getter : getterList ) { - String propertyName = accessorNaming.getPropertyName( getter ); + String propertyName = getPropertyName( getter ); if ( modifiableGetters.containsKey( propertyName ) ) { // In the DefaultAccessorNamingStrategy, this can only be the case for Booleans: isFoo() and // getFoo(); The latter is preferred. if ( !getter.getSimpleName().toString().startsWith( "is" ) ) { - modifiableGetters.put( accessorNaming.getPropertyName( getter ), getter ); + modifiableGetters.put( getPropertyName( getter ),getter ); } } else { - modifiableGetters.put( accessorNaming.getPropertyName( getter ), getter ); + modifiableGetters.put( getPropertyName( getter ), getter ); } } - List fieldsList = Filters.fieldsIn( getAllAccessors() ); + List fieldsList = filters.fieldsIn( getAllFields() ); for ( Accessor field : fieldsList ) { - String propertyName = accessorNaming.getPropertyName( field ); + String propertyName = getPropertyName( field ); if ( !modifiableGetters.containsKey( propertyName ) ) { // If there was no getter or is method for booleans, then resort to the field. // If a field was already added do not add it again. - modifiableGetters.put( propertyName, field ); + modifiableGetters.put( propertyName, field ); } } readAccessors = Collections.unmodifiableMap( modifiableGetters ); @@ -523,15 +529,12 @@ public Map getPropertyReadAccessors() { * * @return an unmodifiable map of all presence checkers, indexed by property name */ - public Map getPropertyPresenceCheckers() { + public Map getPropertyPresenceCheckers() { if ( presenceCheckers == null ) { - List checkerList = Filters.presenceCheckMethodsIn( - accessorNaming, - getAllAccessors() - ); - Map modifiableCheckers = new LinkedHashMap<>(); - for ( ExecutableElementAccessor checker : checkerList ) { - modifiableCheckers.put( accessorNaming.getPropertyName( checker ), checker ); + List checkerList = filters.presenceCheckMethodsIn( getAllMethods() ); + Map modifiableCheckers = new LinkedHashMap<>(); + for ( Accessor checker : checkerList ) { + modifiableCheckers.put( getPropertyName( checker ), checker ); } presenceCheckers = Collections.unmodifiableMap( modifiableCheckers ); } @@ -559,7 +562,7 @@ public Map getPropertyWriteAccessors( CollectionMappingStrateg Map result = new LinkedHashMap<>(); for ( Accessor candidate : candidates ) { - String targetPropertyName = accessorNaming.getPropertyName( candidate ); + String targetPropertyName = getPropertyName( candidate ); Accessor readAccessor = getPropertyReadAccessors().get( targetPropertyName ); @@ -575,12 +578,12 @@ public Map getPropertyWriteAccessors( CollectionMappingStrateg // first check if there's a setter method. Accessor adderMethod = null; - if ( accessorNaming.isSetterMethod( candidate ) + if ( candidate.getAccessorType() == AccessorType.SETTER // ok, the current accessor is a setter. So now the strategy determines what to use && cmStrategy == CollectionMappingStrategyPrism.ADDER_PREFERRED ) { adderMethod = getAdderForType( targetType, targetPropertyName ); } - else if ( accessorNaming.isGetterMethod( candidate ) ) { + else if ( candidate.getAccessorType() == AccessorType.GETTER ) { // the current accessor is a getter (no setter available). But still, an add method is according // to the above strategy (SETTER_PREFERRED || ADDER_PREFERRED) preferred over the getter. adderMethod = getAdderForType( targetType, targetPropertyName ); @@ -590,7 +593,7 @@ else if ( accessorNaming.isGetterMethod( candidate ) ) { candidate = adderMethod; } } - else if ( Executables.isFieldAccessor( candidate ) && ( Executables.isFinal( candidate ) || + else if ( candidate.getAccessorType() == AccessorType.FIELD && ( Executables.isFinal( candidate ) || result.containsKey( targetPropertyName ) ) ) { // if the candidate is a field and a mapping already exists, then use that one, skip it. continue; @@ -618,18 +621,36 @@ private Type determineTargetType(Accessor candidate) { if ( parameter != null ) { return parameter.getType(); } - else if ( accessorNaming.isGetterMethod( candidate ) || Executables.isFieldAccessor( candidate ) ) { + else if ( candidate.getAccessorType() == AccessorType.GETTER + || candidate.getAccessorType() == AccessorType.FIELD ) { return typeFactory.getReturnType( (DeclaredType) typeMirror, candidate ); } return null; } - private List getAllAccessors() { - if ( allAccessors == null ) { - allAccessors = Executables.getAllEnclosedAccessors( elementUtils, typeElement ); + private List getAllMethods() { + if ( allMethods == null ) { + allMethods = Executables.getAllEnclosedExecutableElements( elementUtils, typeElement ); + } + + return allMethods; + } + + private List getAllFields() { + if ( allFields == null ) { + allFields = Fields.getAllEnclosedFields( elementUtils, typeElement ); } - return allAccessors; + return allFields; + } + + private String getPropertyName(Accessor accessor ) { + if ( accessor.getAccessorType() == AccessorType.FIELD ) { + return accessorNaming.getPropertyName( (VariableElement) accessor.getElement() ); + } + else { + return accessorNaming.getPropertyName( (ExecutableElement) accessor.getElement() ); + } } /** @@ -691,19 +712,14 @@ else if ( collectionProperty.isStreamType() ) { * @return accessor candidates */ private List getAccessorCandidates(Type property, Class superclass) { - TypeMirror typeArg = first( property.determineTypeArguments( superclass ) ).getTypeBound() - .getTypeMirror(); + TypeMirror typeArg = first( property.determineTypeArguments( superclass ) ).getTypeBound().getTypeMirror(); // now, look for a method that // 1) starts with add, // 2) and has typeArg as one and only arg List adderList = getAdders(); List candidateList = new ArrayList<>(); for ( Accessor adder : adderList ) { - ExecutableElement executable = adder.getExecutable(); - if ( executable == null ) { - // it should not be null, but to be safe - continue; - } + ExecutableElement executable = (ExecutableElement) adder.getElement(); VariableElement arg = executable.getParameters().get( 0 ); if ( typeUtils.isSameType( boxed( arg.asType() ), boxed( typeArg ) ) ) { candidateList.add( adder ); @@ -728,7 +744,7 @@ private TypeMirror boxed(TypeMirror possiblePrimitive) { */ private List getSetters() { if ( setters == null ) { - setters = Collections.unmodifiableList( Filters.setterMethodsIn( accessorNaming, getAllAccessors() ) ); + setters = Collections.unmodifiableList( filters.setterMethodsIn( getAllMethods() ) ); } return setters; } @@ -743,7 +759,7 @@ private List getSetters() { */ private List getAdders() { if ( adders == null ) { - adders = Collections.unmodifiableList( Filters.adderMethodsIn( accessorNaming, getAllAccessors() ) ); + adders = Collections.unmodifiableList( filters.adderMethodsIn( getAllMethods() ) ); } return adders; } @@ -763,10 +779,9 @@ private List getAlternativeTargetAccessors() { List result = new ArrayList<>(); List setterMethods = getSetters(); - List readAccessors = - new ArrayList<>( getPropertyReadAccessors().values() ); + List readAccessors = new ArrayList<>( getPropertyReadAccessors().values() ); // All the fields are also alternative accessors - readAccessors.addAll( Filters.fieldsIn( getAllAccessors() ) ); + readAccessors.addAll( filters.fieldsIn( getAllFields() ) ); // there could be a read accessor (field or method) for a list/map that is not present as setter. // an accessor could substitute the setter in that case and act as setter. @@ -776,7 +791,7 @@ private List getAlternativeTargetAccessors() { !correspondingSetterMethodExists( readAccessor, setterMethods ) ) { result.add( readAccessor ); } - else if ( Executables.isFieldAccessor( readAccessor ) && + else if ( readAccessor.getAccessorType() == AccessorType.FIELD && !correspondingSetterMethodExists( readAccessor, setterMethods ) ) { result.add( readAccessor ); } @@ -789,10 +804,10 @@ else if ( Executables.isFieldAccessor( readAccessor ) && private boolean correspondingSetterMethodExists(Accessor getterMethod, List setterMethods) { - String getterPropertyName = accessorNaming.getPropertyName( getterMethod ); + String getterPropertyName = getPropertyName( getterMethod ); for ( Accessor setterMethod : setterMethods ) { - String setterPropertyName = accessorNaming.getPropertyName( setterMethod ); + String setterPropertyName = getPropertyName( setterMethod ); if ( getterPropertyName.equals( setterPropertyName ) ) { return true; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 02677906a9..8f3da0b915 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -49,6 +49,7 @@ import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; import org.mapstruct.ap.spi.BuilderInfo; import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException; @@ -352,10 +353,10 @@ public TypeMirror getMethodType(DeclaredType includingType, Element method) { } public Parameter getSingleParameter(DeclaredType includingType, Accessor method) { - ExecutableElement executable = method.getExecutable(); - if ( executable == null ) { + if ( method.getAccessorType() == AccessorType.FIELD ) { return null; } + ExecutableElement executable = (ExecutableElement) method.getElement(); List parameters = executable.getParameters(); if ( parameters.size() != 1 ) { @@ -367,7 +368,7 @@ public Parameter getSingleParameter(DeclaredType includingType, Accessor method) } public List getParameters(DeclaredType includingType, Accessor accessor) { - ExecutableElement method = accessor.getExecutable(); + ExecutableElement method = (ExecutableElement) accessor.getElement(); TypeMirror methodType = getMethodType( includingType, accessor.getElement() ); if ( method == null || methodType.getKind() != TypeKind.EXECUTABLE ) { return new ArrayList<>(); @@ -425,10 +426,10 @@ public List getThrownTypes(ExecutableType method) { } public List getThrownTypes(Accessor accessor) { - if (accessor.getExecutable() == null) { + if (accessor.getAccessorType() == AccessorType.FIELD) { return new ArrayList<>(); } - return extractTypes( accessor.getExecutable().getThrownTypes() ); + return extractTypes( ( (ExecutableElement) accessor.getElement() ).getThrownTypes() ); } private List extractTypes(List typeMirrors) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/PropertyEntry.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/PropertyEntry.java index 4939e45e1a..e6212db685 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/PropertyEntry.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/PropertyEntry.java @@ -23,7 +23,7 @@ public class PropertyEntry { private final String[] fullName; private final Accessor readAccessor; private final Accessor writeAccessor; - private final ExecutableElementAccessor presenceChecker; + private final Accessor presenceChecker; private final Type type; private final BuilderType builderType; @@ -36,7 +36,7 @@ public class PropertyEntry { * @param type */ private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor writeAccessor, - ExecutableElementAccessor presenceChecker, Type type, BuilderType builderType) { + Accessor presenceChecker, Type type, BuilderType builderType) { this.fullName = fullName; this.readAccessor = readAccessor; this.writeAccessor = writeAccessor; @@ -70,7 +70,7 @@ public static PropertyEntry forTargetReference(String[] fullName, Accessor readA * @return the property entry for given parameters. */ public static PropertyEntry forSourceReference(String name, Accessor readAccessor, - ExecutableElementAccessor presenceChecker, Type type) { + Accessor presenceChecker, Type type) { return new PropertyEntry( new String[]{name}, readAccessor, null, presenceChecker, type, null ); } @@ -86,7 +86,7 @@ public Accessor getWriteAccessor() { return writeAccessor; } - public ExecutableElementAccessor getPresenceChecker() { + public Accessor getPresenceChecker() { return presenceChecker; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java index 30b80c8b2f..9688c2f0b7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java @@ -261,7 +261,7 @@ private List matchWithSourceAccessorTypes(Type type, String[] ent for ( String entryName : entryNames ) { boolean matchFound = false; Map sourceReadAccessors = newType.getPropertyReadAccessors(); - Map sourcePresenceCheckers = newType.getPropertyPresenceCheckers(); + Map sourcePresenceCheckers = newType.getPropertyPresenceCheckers(); for ( Map.Entry getter : sourceReadAccessors.entrySet() ) { if ( getter.getKey().equals( entryName ) ) { @@ -297,7 +297,7 @@ public static class BuilderFromProperty { private String name; private Accessor readAccessor; - private ExecutableElementAccessor presenceChecker; + private Accessor presenceChecker; private Type type; private Parameter sourceParameter; @@ -311,7 +311,7 @@ public BuilderFromProperty readAccessor(Accessor readAccessor) { return this; } - public BuilderFromProperty presenceChecker(ExecutableElementAccessor presenceChecker) { + public BuilderFromProperty presenceChecker(Accessor presenceChecker) { this.presenceChecker = presenceChecker; return this; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/TargetReference.java index da44695baf..768be2174e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/TargetReference.java @@ -24,6 +24,7 @@ import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; /** * This class describes the target side of a property mapping. @@ -198,8 +199,8 @@ private List getTargetEntries(Type type, String[] entryNames) { break; } - if ( isLast || ( accessorNaming.isSetterMethod( targetWriteAccessor ) - || Executables.isFieldAccessor( targetWriteAccessor ) ) ) { + if ( isLast || ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER || + targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) ) { // only intermediate nested properties when they are a true setter or field accessor // the last may be other readAccessor (setter / getter / adder). @@ -229,8 +230,7 @@ private List getTargetEntries(Type type, String[] entryNames) { private Type findNextType(Type initial, Accessor targetWriteAccessor, Accessor targetReadAccessor) { Type nextType; Accessor toUse = targetWriteAccessor != null ? targetWriteAccessor : targetReadAccessor; - if ( accessorNaming.isGetterMethod( toUse ) || - Executables.isFieldAccessor( toUse ) ) { + if ( toUse.getAccessorType() == AccessorType.GETTER || toUse.getAccessorType() == AccessorType.FIELD ) { nextType = typeFactory.getReturnType( (DeclaredType) initial.getTypeMirror(), toUse diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java index 3d0d11686b..6cdb83152e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java @@ -7,6 +7,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -14,6 +15,7 @@ import javax.lang.model.util.SimpleTypeVisitor6; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; import org.mapstruct.ap.spi.AccessorNamingStrategy; import org.mapstruct.ap.spi.MethodType; @@ -33,46 +35,42 @@ public AccessorNamingUtils(AccessorNamingStrategy accessorNamingStrategy) { this.accessorNamingStrategy = accessorNamingStrategy; } - public boolean isGetterMethod(Accessor method) { - ExecutableElement executable = method.getExecutable(); - return executable != null && isPublicNotStatic( method ) && + public boolean isGetterMethod(ExecutableElement executable) { + return executable != null && isPublicNotStatic( executable ) && executable.getParameters().isEmpty() && accessorNamingStrategy.getMethodType( executable ) == MethodType.GETTER; } - public boolean isPresenceCheckMethod(Accessor method) { - if ( !( method instanceof ExecutableElementAccessor ) ) { - return false; - } - ExecutableElement executable = method.getExecutable(); + public boolean isPresenceCheckMethod(ExecutableElement executable) { + return executable != null - && isPublicNotStatic( method ) + && isPublicNotStatic( executable ) && executable.getParameters().isEmpty() && ( executable.getReturnType().getKind() == TypeKind.BOOLEAN || "java.lang.Boolean".equals( getQualifiedName( executable.getReturnType() ) ) ) && accessorNamingStrategy.getMethodType( executable ) == MethodType.PRESENCE_CHECKER; } - public boolean isSetterMethod(Accessor method) { - ExecutableElement executable = method.getExecutable(); + public boolean isSetterMethod(ExecutableElement executable) { return executable != null - && isPublicNotStatic( method ) + && isPublicNotStatic( executable ) && executable.getParameters().size() == 1 && accessorNamingStrategy.getMethodType( executable ) == MethodType.SETTER; } - public boolean isAdderMethod(Accessor method) { - ExecutableElement executable = method.getExecutable(); + public boolean isAdderMethod(ExecutableElement executable) { return executable != null - && isPublicNotStatic( method ) + && isPublicNotStatic( executable ) && executable.getParameters().size() == 1 && accessorNamingStrategy.getMethodType( executable ) == MethodType.ADDER; } - public String getPropertyName(Accessor accessor) { - ExecutableElement executable = accessor.getExecutable(); - return executable != null ? accessorNamingStrategy.getPropertyName( executable ) : - accessor.getSimpleName().toString(); + public String getPropertyName(ExecutableElement executable) { + return accessorNamingStrategy.getPropertyName( executable ); + } + + public String getPropertyName(VariableElement variable) { + return variable.getSimpleName().toString(); } /** @@ -82,8 +80,12 @@ public String getPropertyName(Accessor accessor) { * {@code addChild(Child v)}, the element name would be 'Child'. */ public String getElementNameForAdder(Accessor adderMethod) { - ExecutableElement executable = adderMethod.getExecutable(); - return executable != null ? accessorNamingStrategy.getElementName( executable ) : null; + if ( adderMethod.getAccessorType() == AccessorType.ADDER ) { + return accessorNamingStrategy.getElementName( (ExecutableElement) adderMethod.getElement() ); + } + else { + return null; + } } private static String getQualifiedName(TypeMirror type) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java index 6c16b6a8a0..3d01138f72 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java @@ -5,20 +5,14 @@ */ package org.mapstruct.ap.internal.util; -import static javax.lang.model.util.ElementFilter.fieldsIn; -import static javax.lang.model.util.ElementFilter.methodsIn; -import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; - import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -27,10 +21,11 @@ import org.mapstruct.ap.internal.prism.AfterMappingPrism; import org.mapstruct.ap.internal.prism.BeforeMappingPrism; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; -import org.mapstruct.ap.internal.util.accessor.VariableElementAccessor; import org.mapstruct.ap.spi.TypeHierarchyErroneousException; +import static javax.lang.model.util.ElementFilter.methodsIn; +import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary; + /** * Provides functionality around {@link ExecutableElement}s. * @@ -54,29 +49,16 @@ public class Executables { private Executables() { } - /** - * An {@link Accessor} is a field accessor, if it doesn't have an executable element, is public and it is not - * static. - * - * @param accessor the accessor to ber checked - * - * @return {@code true} if the {@code accessor} is for a {@code public} non {@code static} field. - */ - public static boolean isFieldAccessor(Accessor accessor) { - ExecutableElement executable = accessor.getExecutable(); - return executable == null && isPublic( accessor ) && isNotStatic( accessor ); + static boolean isPublicNotStatic(ExecutableElement method) { + return isPublic( method ) && isNotStatic( method ); } - static boolean isPublicNotStatic(Accessor accessor) { - return isPublic( accessor ) && isNotStatic( accessor ); - } - - static boolean isPublic(Accessor method) { + static boolean isPublic(ExecutableElement method) { return method.getModifiers().contains( Modifier.PUBLIC ); } - private static boolean isNotStatic(Accessor accessor) { - return !accessor.getModifiers().contains( Modifier.STATIC ); + private static boolean isNotStatic(ExecutableElement method) { + return !method.getModifiers().contains( Modifier.STATIC ); } public static boolean isFinal(Accessor accessor) { @@ -112,34 +94,14 @@ private static TypeElement asTypeElement(TypeMirror mirror) { * @return the executable elements usable in the type */ public static List getAllEnclosedExecutableElements(Elements elementUtils, TypeElement element) { - List executables = new ArrayList<>(); - for ( Accessor accessor : getAllEnclosedAccessors( elementUtils, element ) ) { - if ( accessor.getExecutable() != null ) { - executables.add( accessor.getExecutable() ); - } - } - return executables; - } - - /** - * Finds all executable elements/variable elements within the given type element, including executable/variable - * elements defined in super classes and implemented interfaces and including the fields in the . Methods defined - * in {@link java.lang.Object} are ignored, as well as implementations of {@link java.lang.Object#equals(Object)}. - * - * @param elementUtils element helper - * @param element the element to inspect - * - * @return the executable elements usable in the type - */ - public static List getAllEnclosedAccessors(Elements elementUtils, TypeElement element) { - List enclosedElements = new ArrayList<>(); + List enclosedElements = new ArrayList<>(); element = replaceTypeElementIfNecessary( elementUtils, element ); addEnclosedElementsInHierarchy( elementUtils, enclosedElements, element, element ); return enclosedElements; } - private static void addEnclosedElementsInHierarchy(Elements elementUtils, List alreadyAdded, + private static void addEnclosedElementsInHierarchy(Elements elementUtils, List alreadyAdded, TypeElement element, TypeElement parentType) { if ( element != parentType ) { // otherwise the element was already checked for replacement element = replaceTypeElementIfNecessary( elementUtils, element ); @@ -150,23 +112,22 @@ private static void addEnclosedElementsInHierarchy(Elements elementUtils, List alreadyCollected, + private static void addNotYetOverridden(Elements elementUtils, List alreadyCollected, List methodsToAdd, TypeElement parentType) { - List safeToAdd = new ArrayList<>( methodsToAdd.size() ); + List safeToAdd = new ArrayList<>( methodsToAdd.size() ); for ( ExecutableElement toAdd : methodsToAdd ) { if ( isNotObjectEquals( toAdd ) - && wasNotYetOverridden( elementUtils, alreadyCollected, toAdd, parentType ) ) { - safeToAdd.add( new ExecutableElementAccessor( toAdd ) ); + && wasNotYetOverridden( elementUtils, alreadyCollected, toAdd, parentType ) ) { + safeToAdd.add( toAdd ); } } alreadyCollected.addAll( 0, safeToAdd ); } - private static void addFields(List alreadyCollected, List variablesToAdd) { - List safeToAdd = new ArrayList<>( variablesToAdd.size() ); - for ( VariableElement toAdd : variablesToAdd ) { - safeToAdd.add( new VariableElementAccessor( toAdd ) ); - } - - alreadyCollected.addAll( 0, safeToAdd ); - } - - /** * @param executable the executable to check * @@ -209,8 +160,8 @@ private static void addFields(List alreadyCollected, List alreadyCollected, + private static boolean wasNotYetOverridden(Elements elementUtils, List alreadyCollected, ExecutableElement executable, TypeElement parentType) { - for ( ListIterator it = alreadyCollected.listIterator(); it.hasNext(); ) { - ExecutableElement executableInSubtype = it.next().getExecutable(); + for ( ListIterator it = alreadyCollected.listIterator(); it.hasNext(); ) { + ExecutableElement executableInSubtype = it.next(); if ( executableInSubtype == null ) { continue; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java new file mode 100644 index 0000000000..0804f1735b --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java @@ -0,0 +1,107 @@ +/* + * 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.internal.util; + +import java.util.ArrayList; +import java.util.List; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; + +import org.mapstruct.ap.spi.TypeHierarchyErroneousException; + +import static javax.lang.model.util.ElementFilter.fieldsIn; +import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary; + +/** + * Provides functionality around {@link VariableElement}s. + * + * @author Sjaak Derksen + */ +public class Fields { + + private Fields() { + } + + public static boolean isFieldAccessor(VariableElement method) { + return isPublic( method ) && isNotStatic( method ); + } + + static boolean isPublic(VariableElement method) { + return method.getModifiers().contains( Modifier.PUBLIC ); + } + + private static boolean isNotStatic(VariableElement method) { + return !method.getModifiers().contains( Modifier.STATIC ); + } + + /** + * Finds all variable elements within the given type element, including variable + * elements defined in super classes and implemented interfaces and including the fields in the . + * + * @param elementUtils element helper + * @param element the element to inspect + * + * @return the executable elements usable in the type + */ + public static List getAllEnclosedFields(Elements elementUtils, TypeElement element) { + List enclosedElements = new ArrayList<>(); + element = replaceTypeElementIfNecessary( elementUtils, element ); + addEnclosedElementsInHierarchy( elementUtils, enclosedElements, element, element ); + + return enclosedElements; + } + + private static void addEnclosedElementsInHierarchy(Elements elementUtils, List alreadyAdded, + TypeElement element, TypeElement parentType) { + if ( element != parentType ) { // otherwise the element was already checked for replacement + element = replaceTypeElementIfNecessary( elementUtils, element ); + } + + if ( element.asType().getKind() == TypeKind.ERROR ) { + throw new TypeHierarchyErroneousException( element ); + } + + addFields( alreadyAdded, fieldsIn( element.getEnclosedElements() ) ); + + + if ( hasNonObjectSuperclass( element ) ) { + addEnclosedElementsInHierarchy( + elementUtils, + alreadyAdded, + asTypeElement( element.getSuperclass() ), + parentType + ); + } + } + + private static void addFields(List alreadyCollected, List variablesToAdd) { + List safeToAdd = new ArrayList<>( variablesToAdd.size() ); + for ( VariableElement toAdd : variablesToAdd ) { + safeToAdd.add( toAdd ); + } + + alreadyCollected.addAll( 0, safeToAdd ); + } + + private static TypeElement asTypeElement(TypeMirror mirror) { + return (TypeElement) ( (DeclaredType) mirror ).asElement(); + } + + private static boolean hasNonObjectSuperclass(TypeElement element) { + if ( element.getSuperclass().getKind() == TypeKind.ERROR ) { + throw new TypeHierarchyErroneousException( element ); + } + + return element.getSuperclass().getKind() == TypeKind.DECLARED + && !asTypeElement( element.getSuperclass() ).getQualifiedName().toString().equals( "java.lang.Object" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java index 2b784785a5..ec69b7131a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java @@ -7,11 +7,23 @@ import java.util.LinkedList; import java.util.List; - import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; +import org.mapstruct.ap.internal.util.accessor.VariableElementAccessor; + +import static org.mapstruct.ap.internal.util.Collections.first; +import static org.mapstruct.ap.internal.util.accessor.AccessorType.ADDER; +import static org.mapstruct.ap.internal.util.accessor.AccessorType.GETTER; +import static org.mapstruct.ap.internal.util.accessor.AccessorType.PRESENCE_CHECKER; +import static org.mapstruct.ap.internal.util.accessor.AccessorType.SETTER; /** * Filter methods for working with {@link Element} collections. @@ -20,66 +32,88 @@ */ public class Filters { - private Filters() { + private final AccessorNamingUtils accessorNaming; + private final Types typeUtils; + private final TypeMirror typeMirror; + + public Filters(AccessorNamingUtils accessorNaming, Types typeUtils, TypeMirror typeMirror) { + this.accessorNaming = accessorNaming; + this.typeUtils = typeUtils; + this.typeMirror = typeMirror; } - public static List getterMethodsIn(AccessorNamingUtils accessorNaming, List elements) { + public List getterMethodsIn(List elements) { List getterMethods = new LinkedList<>(); - for ( Accessor method : elements ) { + for ( ExecutableElement method : elements ) { if ( accessorNaming.isGetterMethod( method ) ) { - getterMethods.add( method ); + getterMethods.add( new ExecutableElementAccessor( method, getReturnType( method ), GETTER ) ); } } return getterMethods; } - public static List fieldsIn(List accessors) { + public List fieldsIn(List accessors) { List fieldAccessors = new LinkedList<>(); - for ( Accessor accessor : accessors ) { - if ( Executables.isFieldAccessor( accessor ) ) { - fieldAccessors.add( accessor ); + for ( VariableElement accessor : accessors ) { + if ( Fields.isFieldAccessor( accessor ) ) { + fieldAccessors.add( new VariableElementAccessor( accessor ) ); } } return fieldAccessors; } - public static List presenceCheckMethodsIn(AccessorNamingUtils accessorNaming, - List elements) { - List presenceCheckMethods = new LinkedList<>(); + public List presenceCheckMethodsIn(List elements) { + List presenceCheckMethods = new LinkedList<>(); - for ( Accessor method : elements ) { + for ( ExecutableElement method : elements ) { if ( accessorNaming.isPresenceCheckMethod( method ) ) { - presenceCheckMethods.add( (ExecutableElementAccessor) method ); + presenceCheckMethods.add( new ExecutableElementAccessor( + method, + getReturnType( method ), + PRESENCE_CHECKER + ) ); } } return presenceCheckMethods; } - public static List setterMethodsIn(AccessorNamingUtils accessorNaming, List elements) { + public List setterMethodsIn(List elements) { List setterMethods = new LinkedList<>(); - for ( Accessor method : elements ) { + for ( ExecutableElement method : elements ) { if ( accessorNaming.isSetterMethod( method ) ) { - setterMethods.add( method ); + setterMethods.add( new ExecutableElementAccessor( method, getFirstParameter( method ), SETTER ) ); } } return setterMethods; } - public static List adderMethodsIn(AccessorNamingUtils accessorNaming, List elements) { + public List adderMethodsIn( List elements) { List adderMethods = new LinkedList<>(); - for ( Accessor method : elements ) { + for ( ExecutableElement method : elements ) { if ( accessorNaming.isAdderMethod( method ) ) { - adderMethods.add( method ); + adderMethods.add( new ExecutableElementAccessor( method, getFirstParameter( method ), ADDER ) ); } } return adderMethods; } + + private TypeMirror getReturnType(ExecutableElement executableElement) { + return getWithinContext( executableElement ).getReturnType(); + } + + private TypeMirror getFirstParameter(ExecutableElement executableElement) { + return first( getWithinContext( executableElement ).getParameterTypes() ); + } + + private ExecutableType getWithinContext( ExecutableElement executableElement ) { + return (ExecutableType) typeUtils.asMemberOf( (DeclaredType) typeMirror, executableElement ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java index 28c706bef0..31a3a9c6cd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.util; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; /** * This a wrapper class which provides the value that needs to be used in the models. @@ -45,7 +46,7 @@ public static ValueProvider of(Accessor accessor) { return null; } String value = accessor.getSimpleName().toString(); - if ( accessor.getExecutable() != null ) { + if ( accessor.getAccessorType() != AccessorType.FIELD ) { value += "()"; } return new ValueProvider( value ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java index b5b671c2dd..5a16e45fa8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java @@ -38,4 +38,5 @@ public Set getModifiers() { public T getElement() { return element; } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java index 9fb0969807..f9dd435d5f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java @@ -8,6 +8,7 @@ import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.type.TypeMirror; @@ -23,7 +24,7 @@ public interface Accessor { * This returns the type that this accessor gives as a return. * * e.g. The {@link ExecutableElement#getReturnType()} if this is a method accessor, - * or {@link javax.lang.model.element.VariableElement#asType()} for field accessors. + * or {@link VariableElement#asType()} for field accessors. * * @return the type that the accessor gives as a return */ @@ -40,12 +41,14 @@ public interface Accessor { Set getModifiers(); /** - * @return the {@link ExecutableElement}, or {@code null} if the accessor does not have one + * @return the underlying {@link Element}, {@link VariableElement} or {@link ExecutableElement} */ - ExecutableElement getExecutable(); + Element getElement(); /** - * @return the underlying {@link Element} + * The accessor type + * + * @return */ - Element getElement(); + AccessorType getAccessorType(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java new file mode 100644 index 0000000000..9348de878c --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java @@ -0,0 +1,15 @@ +/* + * 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.internal.util.accessor; + +public enum AccessorType { + + FIELD, + GETTER, + SETTER, + ADDER, + PRESENCE_CHECKER; +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java index effd137b04..af37acbce1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java @@ -15,22 +15,27 @@ */ public class ExecutableElementAccessor extends AbstractAccessor { - public ExecutableElementAccessor(ExecutableElement element) { + private final TypeMirror accessedType; + private final AccessorType accessorType; + + public ExecutableElementAccessor(ExecutableElement element, TypeMirror accessedType, AccessorType accessorType) { super( element ); + this.accessedType = accessedType; + this.accessorType = accessorType; } @Override public TypeMirror getAccessedType() { - return element.getReturnType(); + return accessedType; } @Override - public ExecutableElement getExecutable() { - return element; + public String toString() { + return element.toString(); } @Override - public String toString() { - return element.toString(); + public AccessorType getAccessorType() { + return accessorType; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/VariableElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/VariableElementAccessor.java index cdbdf1f09f..0b82fdd42f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/VariableElementAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/VariableElementAccessor.java @@ -26,12 +26,13 @@ public TypeMirror getAccessedType() { } @Override - public ExecutableElement getExecutable() { - return null; + public String toString() { + return element.toString(); } @Override - public String toString() { - return element.toString(); + public AccessorType getAccessorType() { + return AccessorType.FIELD; } + }