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 dc4c005bb7..2b91071458 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 @@ -50,7 +50,6 @@ 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.ExecutableElementAccessor; /** * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally @@ -590,7 +589,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 2f892e6b5e..ca540ef789 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 @@ -12,7 +12,6 @@ import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.DeclaredType; import org.mapstruct.ap.internal.model.assignment.AdderWrapper; import org.mapstruct.ap.internal.model.assignment.ArrayCopyWrapper; @@ -40,14 +39,13 @@ import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.util.AccessorNamingUtils; -import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.MapperConfiguration; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.NativeTypes; 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; @@ -73,37 +71,11 @@ 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 Accessor targetReadAccessor; protected String targetPropertyName; @@ -125,7 +97,7 @@ public T targetProperty(PropertyEntry targetProp) { this.targetReadAccessor = targetProp.getReadAccessor(); this.targetWriteAccessor = targetProp.getWriteAccessor(); this.targetType = targetProp.getType(); - this.targetWriteAccessorType = TargetWriteAccessorType.of( ctx.getAccessorNaming(), targetWriteAccessor ); + this.targetWriteAccessorType = targetWriteAccessor.getAccessorType(); return (T) this; } @@ -136,7 +108,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; @@ -148,25 +120,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(); - if ( !method.isUpdateMethod() ) { - mappingType = mappingType.getEffectiveType(); - } - 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 +144,7 @@ public T existingVariableNames(Set existingVariableNames) { } protected boolean isFieldAssignment() { - return targetWriteAccessorType == TargetWriteAccessorType.FIELD; + return targetWriteAccessorType == AccessorType.FIELD; } } @@ -300,11 +254,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 +400,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 +483,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 +691,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 +717,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 +852,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 +968,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 5992a687bd..14d16245d2 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 @@ -34,11 +34,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 org.mapstruct.ap.spi.BuilderInfo; import static org.mapstruct.ap.internal.util.Collections.first; @@ -89,9 +90,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; @@ -100,6 +102,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, @@ -157,6 +161,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 ); this.builderType = BuilderType.create( builderInfo, this, this.typeFactory, this.typeUtils ); } //CHECKSTYLE:ON @@ -505,30 +510,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 ); @@ -541,15 +546,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 ); } @@ -577,7 +579,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 ); @@ -593,12 +595,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 ); @@ -608,7 +610,7 @@ else if ( accessorNaming.isGetterMethod( candidate ) ) { candidate = adderMethod; } } - else if ( Executables.isFieldAccessor( candidate ) && ( Executables.isFinal( candidate ) || + else if ( candidate.getAccessorType() == AccessorType.FIELD && ( isFinal( candidate ) || result.containsKey( targetPropertyName ) ) ) { // if the candidate is a field and a mapping already exists, then use that one, skip it. continue; @@ -636,20 +638,43 @@ 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 allFields; + } + + private String getPropertyName(Accessor accessor ) { + if ( accessor.getAccessorType() == AccessorType.FIELD ) { + return accessorNaming.getPropertyName( (VariableElement) accessor.getElement() ); + } + else { + return accessorNaming.getPropertyName( (ExecutableElement) accessor.getElement() ); } + } - return allAccessors; + private static boolean isFinal(Accessor accessor) { + return accessor != null && accessor.getModifiers().contains( Modifier.FINAL ); } + /** * Tries to find an addMethod in this type for given collection property in this type. * @@ -709,19 +734,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 ); @@ -746,11 +766,12 @@ 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; } + /** * Alternative accessors could be a getter for a collection / map. By means of the * {@link Collection#addAll(Collection) } or {@link Map#putAll(Map)} this getter can still be used as @@ -761,7 +782,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; } @@ -781,10 +802,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. @@ -794,7 +814,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 ); } @@ -807,10 +827,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 567053986e..003582777a 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; @@ -354,10 +355,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 ) { @@ -369,7 +370,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<>(); @@ -427,10 +428,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 03bfe25c65..4bd3895aad 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 @@ -10,7 +10,6 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; /** @@ -22,7 +21,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; /** @@ -34,7 +33,7 @@ public class PropertyEntry { * @param type */ private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor writeAccessor, - ExecutableElementAccessor presenceChecker, Type type) { + Accessor presenceChecker, Type type) { this.fullName = fullName; this.readAccessor = readAccessor; this.writeAccessor = writeAccessor; @@ -66,7 +65,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 ); } @@ -82,7 +81,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..cd76dfff26 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 @@ -24,7 +24,6 @@ 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.ExecutableElementAccessor; /** * This class describes the source side of a property mapping. @@ -261,7 +260,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 +296,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 +310,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 0edfb362d0..6fcfda9ebe 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 @@ -18,11 +18,11 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; 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.FormattingMessager; 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 +198,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). @@ -228,8 +228,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) typeBasedOnMethod( 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..96e1fa9b7e 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 @@ -6,7 +6,9 @@ package org.mapstruct.ap.internal.util; 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; @@ -14,12 +16,10 @@ import javax.lang.model.util.SimpleTypeVisitor6; 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 org.mapstruct.ap.spi.AccessorNamingStrategy; import org.mapstruct.ap.spi.MethodType; -import static org.mapstruct.ap.internal.util.Executables.isPublicNotStatic; - /** * Utils for working with the {@link AccessorNamingStrategy}. * @@ -33,46 +33,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 +78,24 @@ 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 boolean isPublicNotStatic(ExecutableElement method) { + return isPublic( method ) && isNotStatic( method ); + } + + private static boolean isPublic(ExecutableElement method) { + return method.getModifiers().contains( Modifier.PUBLIC ); + } + + private static boolean isNotStatic(ExecutableElement method) { + return !method.getModifiers().contains( Modifier.STATIC ); } 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..d67d14b64b 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,32 +5,26 @@ */ 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; import javax.lang.model.util.Elements; 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.Types.asTypeElement; +import static org.mapstruct.ap.internal.util.Types.hasNonObjectSuperclass; +import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary; + /** * Provides functionality around {@link ExecutableElement}s. * @@ -54,35 +48,6 @@ 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(Accessor accessor) { - return isPublic( accessor ) && isNotStatic( accessor ); - } - - static boolean isPublic(Accessor method) { - return method.getModifiers().contains( Modifier.PUBLIC ); - } - - private static boolean isNotStatic(Accessor accessor) { - return !accessor.getModifiers().contains( Modifier.STATIC ); - } - - public static boolean isFinal(Accessor accessor) { - return accessor != null && accessor.getModifiers().contains( Modifier.FINAL ); - } - public static boolean isDefaultMethod(ExecutableElement method) { try { return DEFAULT_METHOD != null && Boolean.TRUE.equals( DEFAULT_METHOD.invoke( method ) ); @@ -92,15 +57,6 @@ public static boolean isDefaultMethod(ExecutableElement method) { } } - /** - * @param mirror the type positionHint - * - * @return the corresponding type element - */ - private static TypeElement asTypeElement(TypeMirror mirror) { - return (TypeElement) ( (DeclaredType) mirror ).asElement(); - } - /** * Finds all executable elements within the given type element, including executable elements defined in super * classes and implemented interfaces. Methods defined in {@link java.lang.Object} are ignored, as well as @@ -112,34 +68,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 +86,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,7 +134,7 @@ 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; } @@ -245,20 +170,6 @@ else if ( elementUtils.overrides( executable, executableInSubtype, parentType ) return true; } - /** - * @param element the type element to check - * - * @return {@code true}, iff the type has a super-class that is not java.lang.Object - */ - 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" ); - } - /** * @param executableElement the element to check * @return {@code true}, if the executable element is a method annotated with {@code @BeforeMapping} or 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..089fc9b75e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java @@ -0,0 +1,94 @@ +/* + * 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.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +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.Types.asTypeElement; +import static org.mapstruct.ap.internal.util.Types.hasNonObjectSuperclass; +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 ); + } + +} 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/Types.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Types.java new file mode 100644 index 0000000000..e575b6cab3 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Types.java @@ -0,0 +1,47 @@ +/* + * 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 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 org.mapstruct.ap.spi.TypeHierarchyErroneousException; + +/** + * Provides functionality around {@link VariableElement}s. + * + * @author Sjaak Derksen + */ +public class Types { + + private Types() { + } + + /** + * @param element the type element + * @return returns true if the element has a superclass not equal to {@link java.lang.Object} + */ + public 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" ); + } + + /** + * + * @param mirror the mirror + * @return the {@link TypeElement} for the provided mirror. + */ + public static TypeElement asTypeElement(TypeMirror mirror) { + return (TypeElement) ( (DeclaredType) mirror ).asElement(); + } +} 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..8b1b01dbd6 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 @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.internal.util.accessor; -import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; @@ -26,12 +25,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; } + }