mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#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:
parent
874a9b3a52
commit
76603416e7
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user