#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.element.VariableElement;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType; import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
@ -725,34 +724,12 @@ public class Type extends ModelElement implements Comparable<Type> {
* @return the bound for this parameter * @return the bound for this parameter
*/ */
public Type getTypeBound() { public Type getTypeBound() {
if ( boundingBase != null ) { if ( boundingBase != null ) {
return boundingBase; return boundingBase;
} }
boundingBase = this;
if ( typeMirror.getKind() == TypeKind.WILDCARD ) { boundingBase = typeFactory.getType( typeFactory.getTypeBound( getTypeMirror() ) );
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.
}
return boundingBase; 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.PrimitiveType;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; 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.Elements;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
@ -447,6 +449,45 @@ public class TypeFactory {
return collectionOrMap; 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) { static String trimSimpleClassName(String className) {
if ( className == null ) { if ( className == null ) {
return null; return null;

View File

@ -23,7 +23,13 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; 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.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; 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.builtin.BuiltInMethod;
import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; 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.FormattingMessager;
import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.Strings;
@ -511,19 +518,11 @@ public class MappingResolverImpl implements MappingResolver {
private boolean isPropertyMappable(Type sourceType, Type targetType) { private boolean isPropertyMappable(Type sourceType, Type targetType) {
boolean collectionOrMapTargetTypeHasCompatibleConstructor = false; boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
if ( sourceType.isCollectionType() && targetType.isCollectionType() ) { if ( sourceType.isCollectionType() || targetType.isMapType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor( collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleCopyConstructor(
sourceType, sourceType,
targetType.getImplementationType() != null targetType.getImplementationType() != null
? targetType.getImplementationType() : targetType ? targetType.getImplementationType() : targetType
);
}
if ( sourceType.isMapType() && targetType.isMapType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
sourceType,
targetType.getImplementationType() != null
? targetType.getImplementationType() : targetType
); );
} }
@ -540,67 +539,49 @@ public class MappingResolverImpl implements MappingResolver {
* *
* @param sourceType the source type * @param sourceType the source type
* @param targetType the target type * @param targetType the target type
*
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false} * @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) { private boolean hasCompatibleCopyConstructor(Type sourceType, Type targetType) {
// note (issue #127): actually this should check for the presence of a matching constructor, with help of List<ExecutableElement> targetTypeConstructors = ElementFilter.constructorsIn(
// Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead targetType.getTypeElement().getEnclosedElements() );
// we just check whether the target type is parameterized in a way that it implicitly should have a
// constructor which accepts the source type
TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty() for ( ExecutableElement constructor : targetTypeConstructors ) {
? typeFactory.getType( Object.class ).getTypeMirror() if ( constructor.getParameters().size() != 1 ) {
: sourceType.getTypeParameters().get( 0 ).getTypeMirror(); continue;
}
TypeMirror targetElementType = targetType.getTypeParameters().isEmpty() // get the constructor resolved against the type arguments of specific target type
? typeFactory.getType( Object.class ).getTypeMirror() ExecutableType typedConstructor = (ExecutableType) typeUtils.asMemberOf(
: targetType.getTypeParameters().get( 0 ).getTypeMirror(); (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...
/** DeclaredType p = (DeclaredType) parameterType;
* Whether the given target type has a single-argument constructor which accepts the given source type. List<TypeMirror> typeArguments = new ArrayList<TypeMirror>( p.getTypeArguments().size() );
*
* @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
TypeMirror sourceKeyType; for ( TypeMirror tArg : p.getTypeArguments() ) {
TypeMirror targetKeyType; typeArguments.add( typeFactory.getTypeBound( tArg ) );
TypeMirror sourceValueType; }
TypeMirror targetValueType; parameterType = typeUtils.getDeclaredType(
(TypeElement) p.asElement(),
typeArguments.toArray( new TypeMirror[typeArguments.size()] ) );
}
if ( sourceType.getTypeParameters().isEmpty() ) { if ( typeUtils.isAssignable( sourceType.getTypeMirror(), parameterType ) ) {
sourceKeyType = typeFactory.getType( Object.class ).getTypeMirror(); return true;
sourceValueType = typeFactory.getType( Object.class ).getTypeMirror(); }
}
else {
sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror();
sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror();
} }
if ( targetType.getTypeParameters().isEmpty() ) { return false;
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 );
} }
} }
} }