#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.

This commit is contained in:
Andreas Gudian 2015-11-12 21:18:13 +01:00
parent 874a9b3a52
commit 76603416e7
3 changed files with 87 additions and 88 deletions

View File

@ -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<Type> {
* @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;
}
}

View File

@ -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:
* <ol>
* <li>{@code<? extends Number>}, returns Number</li>
* <li>{@code<? super Number>}, returns Number</li>
* <li>{@code<?>}, returns Object</li>
* <li>{@code<T extends Number>, returns Number}</li>
* </ol>
*
* @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;

View File

@ -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,16 +518,8 @@ public class MappingResolverImpl implements MappingResolver {
private boolean isPropertyMappable(Type sourceType, Type targetType) {
boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor(
sourceType,
targetType.getImplementationType() != null
? targetType.getImplementationType() : targetType
);
}
if ( sourceType.isMapType() && targetType.isMapType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
if ( sourceType.isCollectionType() || targetType.isMapType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleCopyConstructor(
sourceType,
targetType.getImplementationType() != null
? 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.
*/
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<ExecutableElement> targetTypeConstructors = ElementFilter.constructorsIn(
targetType.getTypeElement().getEnclosedElements() );
TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty()
? typeFactory.getType( Object.class ).getTypeMirror()
: sourceType.getTypeParameters().get( 0 ).getTypeMirror();
TypeMirror targetElementType = targetType.getTypeParameters().isEmpty()
? typeFactory.getType( Object.class ).getTypeMirror()
: targetType.getTypeParameters().get( 0 ).getTypeMirror();
return typeUtils.isAssignable( sourceElementType, targetElementType );
for ( ExecutableElement constructor : targetTypeConstructors ) {
if ( constructor.getParameters().size() != 1 ) {
continue;
}
/**
* 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
// get the constructor resolved against the type arguments of specific target type
ExecutableType typedConstructor = (ExecutableType) typeUtils.asMemberOf(
(DeclaredType) targetType.getTypeMirror(),
constructor );
TypeMirror sourceKeyType;
TypeMirror targetKeyType;
TypeMirror sourceValueType;
TypeMirror targetValueType;
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...
if ( sourceType.getTypeParameters().isEmpty() ) {
sourceKeyType = typeFactory.getType( Object.class ).getTypeMirror();
sourceValueType = typeFactory.getType( Object.class ).getTypeMirror();
DeclaredType p = (DeclaredType) parameterType;
List<TypeMirror> typeArguments = new ArrayList<TypeMirror>( p.getTypeArguments().size() );
for ( TypeMirror tArg : p.getTypeArguments() ) {
typeArguments.add( typeFactory.getTypeBound( tArg ) );
}
else {
sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror();
sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror();
parameterType = typeUtils.getDeclaredType(
(TypeElement) p.asElement(),
typeArguments.toArray( new TypeMirror[typeArguments.size()] ) );
}
if ( targetType.getTypeParameters().isEmpty() ) {
targetKeyType = typeFactory.getType( Object.class ).getTypeMirror();
targetValueType = typeFactory.getType( Object.class ).getTypeMirror();
if ( typeUtils.isAssignable( sourceType.getTypeMirror(), parameterType ) ) {
return true;
}
else {
targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror();
targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror();
}
return typeUtils.isAssignable( sourceKeyType, targetKeyType )
&& typeUtils.isAssignable( sourceValueType, targetValueType );
return false;
}
}
}