#127 Working around broken Eclipse implementation of Types#asMemberOf()

This commit is contained in:
Gunnar Morling 2014-02-12 23:47:52 +01:00
parent cce558827d
commit f69069c2f8

View File

@ -31,11 +31,8 @@ import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
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;
import javax.tools.Diagnostic.Kind;
@ -818,20 +815,21 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) {
boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
if ( property.getTargetType().isCollectionType() || property.getTargetType().isMapType() ) {
if ( property.getTargetType().getImplementationType() != null ) {
collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleConstructor(
if ( property.getSourceType().isCollectionType() && property.getTargetType().isCollectionType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor(
property.getSourceType(),
property.getTargetType().getImplementationType()
property.getTargetType().getImplementationType() != null ?
property.getTargetType().getImplementationType() : property.getTargetType()
);
}
else {
collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleConstructor(
if ( property.getSourceType().isMapType() && property.getTargetType().isMapType() ) {
collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
property.getSourceType(),
property.getTargetType()
property.getTargetType().getImplementationType() != null ?
property.getTargetType().getImplementationType() : property.getTargetType()
);
}
}
if ( property.getSourceType().isAssignableTo( property.getTargetType() ) ||
property.getMappingMethod() != null ||
@ -863,31 +861,63 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
* @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
* otherwise.
*/
private boolean hasCompatibleConstructor(Type sourceType, Type targetType) {
List<ExecutableElement> targetTypeConstructors = ElementFilter.constructorsIn(
targetType.getTypeElement()
.getEnclosedElements()
);
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
for ( ExecutableElement constructor : targetTypeConstructors ) {
if ( constructor.getParameters().size() != 1 ) {
continue;
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 );
}
//get the constructor resolved against the type arguments of specific target type
ExecutableType typedConstructor = (ExecutableType) typeUtils.asMemberOf(
(DeclaredType) targetType.getTypeMirror(), constructor
);
/**
* 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
if ( typeUtils.isAssignable(
sourceType.getTypeMirror(),
typedConstructor.getParameterTypes().iterator().next()
) ) {
return true;
TypeMirror sourceKeyType = null;
TypeMirror targetKeyType = null;
TypeMirror sourceValueType = null;
TypeMirror targetValueType = null;
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();
}
return false;
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 );
}
/**