diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java index fc91cfa0a..ae15f741f 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java @@ -19,6 +19,7 @@ package org.mapstruct.ap.model.common; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -26,11 +27,16 @@ import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; import javax.lang.model.util.Types; +import org.mapstruct.ap.util.Executables; +import org.mapstruct.ap.util.Filters; +import org.mapstruct.ap.util.TypeUtilsJDK6Fix; /** * Represents (a reference to) the type of a bean property, parameter etc. Types are managed per generated source file. @@ -45,6 +51,7 @@ import javax.lang.model.util.Types; public class Type extends ModelElement implements Comparable { private final Types typeUtils; + private final Elements elementUtils; private final TypeMirror typeMirror; private final TypeElement typeElement; @@ -64,19 +71,31 @@ public class Type extends ModelElement implements Comparable { private final boolean isImported; private final List enumConstants; + private List getters = null; + private List setters = null; + private List alternativeTargetAccessors = null; + //CHECKSTYLE:OFF - public Type(Types typeUtils, TypeMirror typeMirror, TypeElement typeElement, List typeParameters, - Type implementationType, String packageName, String name, String qualifiedName, boolean isInterface, - boolean isEnumType, boolean isIterableType, boolean isCollectionType, boolean isMapType, - boolean isImported) { + public Type(Types typeUtils, Elements elementUtils, + TypeMirror typeMirror, TypeElement typeElement, List typeParameters, + Type implementationType, + String packageName, String name, String qualifiedName, + boolean isInterface, + boolean isEnumType, boolean isIterableType, boolean isCollectionType, boolean isMapType, + boolean isImported) { + this.typeUtils = typeUtils; + this.elementUtils = elementUtils; + this.typeMirror = typeMirror; this.typeElement = typeElement; this.typeParameters = typeParameters; this.implementationType = implementationType; + this.packageName = packageName; this.name = name; this.qualifiedName = qualifiedName; + this.isInterface = isInterface; this.isEnumType = isEnumType; this.isIterableType = isIterableType; @@ -206,6 +225,7 @@ public class Type extends ModelElement implements Comparable { public Type erasure() { return new Type( typeUtils, + elementUtils, typeUtils.erasure( typeMirror ), typeElement, typeParameters, @@ -239,6 +259,79 @@ public class Type extends ModelElement implements Comparable { return typeUtils.isAssignable( typeMirror, other.typeMirror ); } + /** + * getGetters + * + * @return an unmodifiable list of all getters (including 'is' for booleans). + */ + public List getGetters() { + if (getters == null) { + List members = elementUtils.getAllMembers( typeElement ); + getters = Collections.unmodifiableList( Filters.getterMethodsIn( members ) ); + } + return getters; + } + + /** + * getSetters + * + * @return an unmodifiable list of all setters + */ + public List getSetters() { + if (setters == null) { + List members = elementUtils.getAllMembers( typeElement ); + setters = Collections.unmodifiableList( Filters.setterMethodsIn( members ) ); + } + return setters; + } + + /** + * Alternative accessors could be a getter for a collection. By means of the + * {@link java.util.Collection#addAll(java.util.Collection) } this getter can still + * be used as targetAccessor. JAXB XJC tool generates such constructs. + * + * This method can be extended when new cases come along. + * + * @return an unmodifiable list of alternative target accessors. + */ + public List getAlternativeTargetAccessors() { + if ( alternativeTargetAccessors == null ) { + + List result = new ArrayList(); + List setterMethods = getSetters(); + List getterMethods = getGetters(); + + if ( getterMethods.size() > setterMethods.size() ) { + // there could be a getter method for a list that is not present as setter. + // a getter could substitute the setter in that case and act as setter. + // (assuming it is initialized) + for ( ExecutableElement getterMethod : getterMethods ) { + boolean matchFound = false; + String getterPropertyName = Executables.getPropertyName( getterMethod ); + for ( ExecutableElement setterMethod : setterMethods ) { + String setterPropertyName = Executables.getPropertyName( setterMethod ); + if ( getterPropertyName.equals( setterPropertyName ) ) { + matchFound = true; + break; + } + } + if ( !matchFound && isCollection( getterMethod.getReturnType() ) ) { + result.add( getterMethod ); + } + } + } + alternativeTargetAccessors = Collections.unmodifiableList( result ); + } + return alternativeTargetAccessors; + } + + private boolean isCollection( TypeMirror candidate ) { + String collectionName = Collection.class.getCanonicalName(); + TypeMirror collectionType = typeUtils.erasure( elementUtils.getTypeElement( collectionName ).asType() ); + return TypeUtilsJDK6Fix.isSubType( typeUtils, candidate, collectionType ); + } + + /** * Returns the length of the shortest path in the type hierarchy between this type and the specified other type. * Returns {@code -1} if this type is not assignable to the other type. Returns {@code 0} if this type is equal to diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java index 1721f978e..3f024c15b 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java @@ -162,7 +162,7 @@ public class TypeFactory { } return new Type( - typeUtils, + typeUtils, elementUtils, mirror, typeElement, getTypeParameters( mirror ), @@ -283,6 +283,7 @@ public class TypeFactory { if ( implementationType != null ) { return new Type( typeUtils, + elementUtils, typeUtils.getDeclaredType( implementationType.getTypeElement(), declaredType.getTypeArguments().toArray( new TypeMirror[] { } ) diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java index 61f3006c4..cc99ca682 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; 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.TypeKind; @@ -70,7 +69,6 @@ import org.mapstruct.ap.option.ReportingPolicy; import org.mapstruct.ap.prism.DecoratedWithPrism; import org.mapstruct.ap.prism.MapperPrism; import org.mapstruct.ap.util.Executables; -import org.mapstruct.ap.util.Filters; import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.Strings; @@ -403,10 +401,7 @@ public class MapperCreationProcessor implements ModelElementProcessor sourceGetters = Filters.getterMethodsIn( - elementUtils.getAllMembers( parameterElement ) - ); + List sourceGetters = parameter.getType().getGetters(); for ( ExecutableElement sourceAccessor : sourceGetters ) { @@ -454,15 +449,10 @@ public class MapperCreationProcessor implements ModelElementProcessor targetAccessors = Filters.setterMethodsIn( - elementUtils.getAllMembers( resultTypeElement ) - ); - targetAccessors.addAll( - alternativeTargetAccessorMethodsIn( - elementUtils.getAllMembers( resultTypeElement ) - ) - ); + // collect all target accessors + List targetAccessors = new ArrayList(); + targetAccessors.addAll( method.getResultType().getSetters() ); + targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() ); for ( ExecutableElement targetAccessor : targetAccessors ) { String targetPropertyName = Executables.getPropertyName( targetAccessor ); @@ -557,11 +547,7 @@ public class MapperCreationProcessor implements ModelElementProcessor getters = Filters.getterMethodsIn( - elementUtils.getAllMembers( parameterTypeElement ) - ); - + List getters = parameter.getType().getGetters(); return Executables.getPropertyNames( getters ).contains( propertyName ); } @@ -571,15 +557,11 @@ public class MapperCreationProcessor implements ModelElementProcessor targetAccessors = Filters.setterMethodsIn( - elementUtils.getAllMembers( resultTypeElement ) - ); - targetAccessors.addAll( - alternativeTargetAccessorMethodsIn( - elementUtils.getAllMembers( resultTypeElement ) - ) - ); + // collect all target accessors + List targetAccessors = new ArrayList(); + targetAccessors.addAll( method.getResultType().getSetters() ); + targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() ); + Set targetProperties = Executables.getPropertyNames( targetAccessors ); boolean foundUnmappedProperty = false; @@ -1361,42 +1343,4 @@ public class MapperCreationProcessor implements ModelElementProcessor alternativeTargetAccessorMethodsIn(Iterable elements) { - List setterMethods = Filters.setterMethodsIn( elements ); - List getterMethods = Filters.getterMethodsIn( elements ); - List alternativeTargetAccessorsMethods = new LinkedList(); - - if ( getterMethods.size() > setterMethods.size() ) { - // there could be a getter method for a list that is not present as setter. - // a getter could substitute the setter in that case and act as setter. - // (assuming it is initialized) - for ( ExecutableElement getterMethod : getterMethods ) { - boolean matchFound = false; - String getterPropertyName = Executables.getPropertyName( getterMethod ); - for ( ExecutableElement setterMethod : setterMethods ) { - String setterPropertyName = Executables.getPropertyName( setterMethod ); - if ( getterPropertyName.equals( setterPropertyName ) ) { - matchFound = true; - break; - } - } - if ( !matchFound && typeFactory.getReturnType( getterMethod ).isCollectionType() ) { - alternativeTargetAccessorsMethods.add( getterMethod ); - } - } - } - - return alternativeTargetAccessorsMethods; - } }