#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.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; 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.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;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
@ -818,20 +815,21 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) { private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) {
boolean collectionOrMapTargetTypeHasCompatibleConstructor = false; boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
if ( property.getTargetType().isCollectionType() || property.getTargetType().isMapType() ) { if ( property.getSourceType().isCollectionType() && property.getTargetType().isCollectionType() ) {
if ( property.getTargetType().getImplementationType() != null ) { collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor(
collectionOrMapTargetTypeHasCompatibleConstructor = hasCompatibleConstructor(
property.getSourceType(), 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.getSourceType(),
property.getTargetType() property.getTargetType().getImplementationType() != null ?
property.getTargetType().getImplementationType() : property.getTargetType()
); );
} }
}
if ( property.getSourceType().isAssignableTo( property.getTargetType() ) || if ( property.getSourceType().isAssignableTo( property.getTargetType() ) ||
property.getMappingMethod() != null || 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} * @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
* otherwise. * otherwise.
*/ */
private boolean hasCompatibleConstructor(Type sourceType, Type targetType) { private boolean collectionTypeHasCompatibleConstructor(Type sourceType, Type targetType) {
List<ExecutableElement> targetTypeConstructors = ElementFilter.constructorsIn( // note (issue #127): actually this should check for the presence of a matching constructor, with help of
targetType.getTypeElement() // Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead we
.getEnclosedElements() // 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 ) { TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty() ?
if ( constructor.getParameters().size() != 1 ) { typeFactory.getType( Object.class ).getTypeMirror() :
continue; 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( * Whether the given target type has a single-argument constructor which accepts the given source type.
(DeclaredType) targetType.getTypeMirror(), constructor *
); * @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( TypeMirror sourceKeyType = null;
sourceType.getTypeMirror(), TypeMirror targetKeyType = null;
typedConstructor.getParameterTypes().iterator().next() TypeMirror sourceValueType = null;
) ) { TypeMirror targetValueType = null;
return true;
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 );
} }
/** /**