From 76603416e761c93d335d86a2a6eebf448168c9a9 Mon Sep 17 00:00:00 2001 From: Andreas Gudian Date: Thu, 12 Nov 2015 21:18:13 +0100 Subject: [PATCH] #644 Basically reverting changes made for #127, with a fix for handling super-bounds in JDK (failed after wrapping Types in TypesDecorator). Moved most of the logic of Types.getTypeBounds() to TypeFactory. --- .../ap/internal/model/common/Type.java | 29 +---- .../ap/internal/model/common/TypeFactory.java | 41 +++++++ .../creation/MappingResolverImpl.java | 105 +++++++----------- 3 files changed, 87 insertions(+), 88 deletions(-) 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 10ac290d0..904a7e90e 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 @@ -37,7 +37,6 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; @@ -725,34 +724,12 @@ public class Type extends ModelElement implements Comparable { * @return the bound for this parameter */ public Type getTypeBound() { - if ( boundingBase != null ) { return boundingBase; } - boundingBase = this; - if ( typeMirror.getKind() == TypeKind.WILDCARD ) { - WildcardType wildCardType = (WildcardType) typeMirror; - if ( wildCardType.getExtendsBound() != null ) { - boundingBase = typeFactory.getType( wildCardType.getExtendsBound() ); - } - else if ( wildCardType.getSuperBound() != null ) { - boundingBase = typeFactory.getType( wildCardType.getSuperBound() ); - } - else { - String wildCardName = wildCardType.toString(); - if ( "?".equals( wildCardName ) ) { - boundingBase = typeFactory.getType( Object.class ); - } - } - } - else if ( typeMirror.getKind() == TypeKind.TYPEVAR ) { - TypeVariable typeVariableType = (TypeVariable) typeMirror; - if ( typeVariableType.getUpperBound() != null ) { - boundingBase = typeFactory.getType( typeVariableType.getUpperBound() ); - } - // Lowerbounds intentionally left out: Type variables otherwise have a lower bound of NullType. - } + + boundingBase = typeFactory.getType( typeFactory.getTypeBound( getTypeMirror() ) ); + return boundingBase; } - } 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 072efa11e..90e9ce881 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 @@ -47,6 +47,8 @@ import javax.lang.model.type.ExecutableType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; @@ -447,6 +449,45 @@ public class TypeFactory { return collectionOrMap; } + /** + * Establishes the type bound: + *
    + *
  1. {@code}, returns Number
  2. + *
  3. {@code}, returns Number
  4. + *
  5. {@code}, returns Object
  6. + *
  7. {@code, returns Number}
  8. + *
+ * + * @param typeMirror the type to return the bound for + * @return the bound for this parameter + */ + public TypeMirror getTypeBound(TypeMirror typeMirror) { + if ( typeMirror.getKind() == TypeKind.WILDCARD ) { + WildcardType wildCardType = (WildcardType) typeMirror; + if ( wildCardType.getExtendsBound() != null ) { + return wildCardType.getExtendsBound(); + } + + if ( wildCardType.getSuperBound() != null ) { + return wildCardType.getSuperBound(); + } + + String wildCardName = wildCardType.toString(); + if ( "?".equals( wildCardName ) ) { + return elementUtils.getTypeElement( Object.class.getCanonicalName() ).asType(); + } + } + else if ( typeMirror.getKind() == TypeKind.TYPEVAR ) { + TypeVariable typeVariableType = (TypeVariable) typeMirror; + if ( typeVariableType.getUpperBound() != null ) { + return typeVariableType.getUpperBound(); + } + // Lowerbounds intentionally left out: Type variables otherwise have a lower bound of NullType. + } + + return typeMirror; + } + static String trimSimpleClassName(String className) { if ( className == null ) { return null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 2ac8d1e05..e57793a91 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -23,7 +23,13 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; @@ -45,6 +51,7 @@ import org.mapstruct.ap.internal.model.source.builtin.BuiltInMappingMethods; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; +import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -511,19 +518,11 @@ public class MappingResolverImpl implements MappingResolver { private boolean isPropertyMappable(Type sourceType, Type targetType) { boolean collectionOrMapTargetTypeHasCompatibleConstructor = false; - if ( sourceType.isCollectionType() && targetType.isCollectionType() ) { - collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor( + if ( sourceType.isCollectionType() || targetType.isMapType() ) { + collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleCopyConstructor( sourceType, targetType.getImplementationType() != null - ? targetType.getImplementationType() : targetType - ); - } - - if ( sourceType.isMapType() && targetType.isMapType() ) { - collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor( - sourceType, - targetType.getImplementationType() != null - ? targetType.getImplementationType() : targetType + ? targetType.getImplementationType() : targetType ); } @@ -540,67 +539,49 @@ public class MappingResolverImpl implements MappingResolver { * * @param sourceType the source type * @param targetType the target type - * * @return {@code true} if the target type has a constructor accepting the given source type, {@code false} - * otherwise. + * otherwise. */ - private boolean collectionTypeHasCompatibleConstructor(Type sourceType, Type targetType) { - // note (issue #127): actually this should check for the presence of a matching constructor, with help of - // Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead - // we just check whether the target type is parameterized in a way that it implicitly should have a - // constructor which accepts the source type + private boolean hasCompatibleCopyConstructor(Type sourceType, Type targetType) { + List targetTypeConstructors = ElementFilter.constructorsIn( + targetType.getTypeElement().getEnclosedElements() ); - TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty() - ? typeFactory.getType( Object.class ).getTypeMirror() - : sourceType.getTypeParameters().get( 0 ).getTypeMirror(); + for ( ExecutableElement constructor : targetTypeConstructors ) { + if ( constructor.getParameters().size() != 1 ) { + continue; + } - TypeMirror targetElementType = targetType.getTypeParameters().isEmpty() - ? typeFactory.getType( Object.class ).getTypeMirror() - : targetType.getTypeParameters().get( 0 ).getTypeMirror(); + // get the constructor resolved against the type arguments of specific target type + ExecutableType typedConstructor = (ExecutableType) typeUtils.asMemberOf( + (DeclaredType) targetType.getTypeMirror(), + constructor ); - return typeUtils.isAssignable( sourceElementType, targetElementType ); - } + TypeMirror parameterType = Collections.first( typedConstructor.getParameterTypes() ); + if ( parameterType.getKind() == TypeKind.DECLARED ) { + // replace any possible type bounds in the type parameters of the parameter types, as in JDK super + // type bounds in the arguments are returned from asMemberOf with "? extends ? super XX" + // + // It might also be enough to just remove "? super" from type parameters of + // targetType.getTypeMirror() in case we're in JDK. And that would be something that should be + // handled in SpecificCompilerWorkarounds... - /** - * Whether the given target type has a single-argument constructor which accepts the given source type. - * - * @param sourceType the source type - * @param targetType the target type - * - * @return {@code true} if the target type has a constructor accepting the given source type, {@code false} - * otherwise. - */ - private boolean mapTypeHasCompatibleConstructor(Type sourceType, Type targetType) { - // note (issue #127): actually this should check for the presence of a matching constructor, with help of - // Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead - // we just check whether the target type is parameterized in a way that it implicitly should have a - // constructor which accepts the source type + DeclaredType p = (DeclaredType) parameterType; + List typeArguments = new ArrayList( p.getTypeArguments().size() ); - TypeMirror sourceKeyType; - TypeMirror targetKeyType; - TypeMirror sourceValueType; - TypeMirror targetValueType; + for ( TypeMirror tArg : p.getTypeArguments() ) { + typeArguments.add( typeFactory.getTypeBound( tArg ) ); + } + parameterType = typeUtils.getDeclaredType( + (TypeElement) p.asElement(), + typeArguments.toArray( new TypeMirror[typeArguments.size()] ) ); + } - if ( sourceType.getTypeParameters().isEmpty() ) { - sourceKeyType = typeFactory.getType( Object.class ).getTypeMirror(); - sourceValueType = typeFactory.getType( Object.class ).getTypeMirror(); - } - else { - sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror(); - sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror(); + if ( typeUtils.isAssignable( sourceType.getTypeMirror(), parameterType ) ) { + return true; + } } - if ( targetType.getTypeParameters().isEmpty() ) { - targetKeyType = typeFactory.getType( Object.class ).getTypeMirror(); - targetValueType = typeFactory.getType( Object.class ).getTypeMirror(); - } - else { - targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror(); - targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror(); - } - - return typeUtils.isAssignable( sourceKeyType, targetKeyType ) - && typeUtils.isAssignable( sourceValueType, targetValueType ); + return false; } } }