#327 Move MethodRetrievalProcessor#allEnclosingElementsIncludeSuper to Executables#getAllEnclosingExecutableElements and replace any Elements#getAllMembers calls with it (to create a fixed order of property mappings)

This commit is contained in:
Andreas Gudian 2014-11-18 17:58:54 +01:00
parent 9992801f28
commit 03535ecb18
10 changed files with 232 additions and 84 deletions

View File

@ -78,6 +78,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private final List<String> enumConstants; private final List<String> enumConstants;
private List<ExecutableElement> allExecutables = null;
private List<ExecutableElement> getters = null; private List<ExecutableElement> getters = null;
private List<ExecutableElement> setters = null; private List<ExecutableElement> setters = null;
private List<ExecutableElement> adders = null; private List<ExecutableElement> adders = null;
@ -280,12 +281,19 @@ public class Type extends ModelElement implements Comparable<Type> {
*/ */
public List<ExecutableElement> getGetters() { public List<ExecutableElement> getGetters() {
if ( getters == null ) { if ( getters == null ) {
List<? extends Element> members = elementUtils.getAllMembers( typeElement ); getters = Collections.unmodifiableList( Filters.getterMethodsIn( getAllExecutables() ) );
getters = Collections.unmodifiableList( Filters.getterMethodsIn( members ) );
} }
return getters; return getters;
} }
private List<ExecutableElement> getAllExecutables() {
if ( allExecutables == null ) {
allExecutables = Executables.getAllEnclosingExecutableElements( elementUtils, typeElement );
}
return allExecutables;
}
/** /**
* Tries to find an addMethod in this type for given collection property in this type. * Tries to find an addMethod in this type for given collection property in this type.
* *
@ -349,8 +357,7 @@ public class Type extends ModelElement implements Comparable<Type> {
*/ */
public List<ExecutableElement> getSetters() { public List<ExecutableElement> getSetters() {
if ( setters == null ) { if ( setters == null ) {
List<? extends Element> members = elementUtils.getAllMembers( typeElement ); setters = Collections.unmodifiableList( Filters.setterMethodsIn( getAllExecutables() ) );
setters = Collections.unmodifiableList( Filters.setterMethodsIn( members ) );
} }
return setters; return setters;
} }
@ -365,8 +372,7 @@ public class Type extends ModelElement implements Comparable<Type> {
*/ */
private List<ExecutableElement> getAdders() { private List<ExecutableElement> getAdders() {
if ( adders == null ) { if ( adders == null ) {
List<? extends Element> members = elementUtils.getAllMembers( typeElement ); adders = Collections.unmodifiableList( Filters.adderMethodsIn( getAllExecutables() ) );
adders = Collections.unmodifiableList( Filters.adderMethodsIn( members ) );
} }
return adders; return adders;
} }

View File

@ -22,8 +22,8 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.processing.Messager; import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
@ -48,7 +48,7 @@ import org.mapstruct.ap.prism.MappingsPrism;
import org.mapstruct.ap.util.AnnotationProcessingException; import org.mapstruct.ap.util.AnnotationProcessingException;
import org.mapstruct.ap.util.MapperConfig; import org.mapstruct.ap.util.MapperConfig;
import static javax.lang.model.util.ElementFilter.methodsIn; import static org.mapstruct.ap.util.Executables.getAllEnclosingExecutableElements;
/** /**
* A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s * A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s
@ -64,7 +64,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
private TypeFactory typeFactory; private TypeFactory typeFactory;
private Types typeUtils; private Types typeUtils;
private Elements elementUtils; private Elements elementUtils;
private TypeMirror javaLangObjectTypeMirror;
@Override @Override
public List<SourceMethod> process(ProcessorContext context, TypeElement mapperTypeElement, Void sourceModel) { public List<SourceMethod> process(ProcessorContext context, TypeElement mapperTypeElement, Void sourceModel) {
@ -72,7 +71,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
this.typeFactory = context.getTypeFactory(); this.typeFactory = context.getTypeFactory();
this.typeUtils = context.getTypeUtils(); this.typeUtils = context.getTypeUtils();
this.elementUtils = context.getElementUtils(); this.elementUtils = context.getElementUtils();
this.javaLangObjectTypeMirror = typeFactory.getType( Object.class ).getTypeMirror();
return retrieveMethods( mapperTypeElement, mapperTypeElement ); return retrieveMethods( mapperTypeElement, mapperTypeElement );
} }
@ -92,12 +90,10 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
private List<SourceMethod> retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement) { private List<SourceMethod> retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement) {
List<SourceMethod> methods = new ArrayList<SourceMethod>(); List<SourceMethod> methods = new ArrayList<SourceMethod>();
for ( ExecutableElement executable : methodsIn( allEnclosingElementsIncludeSuper( usedMapper ) ) ) { for ( ExecutableElement executable : getAllEnclosingExecutableElements( elementUtils, usedMapper ) ) {
if ( isNotObjectEquals( executable ) && wasNotYetOverridden( methods, executable ) ) { SourceMethod method = getMethod( usedMapper, executable, mapperToImplement );
SourceMethod method = getMethod( usedMapper, executable, mapperToImplement ); if ( method != null ) {
if ( method != null ) { methods.add( method );
methods.add( method );
}
} }
} }
@ -118,67 +114,10 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return methods; return methods;
} }
/**
* @param executable the executable to check
*
* @return <code>true</code>, iff the executable does not represent {@link java.lang.Object#equals(Object)} or an
* overridden version of it
*/
private boolean isNotObjectEquals(ExecutableElement executable) {
if ( "equals".equals( executable.getSimpleName().toString() )
&& executable.getParameters().size() == 1
&& executable.getParameters().get( 0 ).asType().equals( javaLangObjectTypeMirror ) ) {
return false;
}
return true;
}
/**
* @param methods the list of already collected methods of one type hierarchy (order is from sub-types to
* super-types)
* @param executable the method to check
*
* @return <code>true</code>, iff the given executable was not yet overridden by a method in the given list.
*/
private boolean wasNotYetOverridden(List<SourceMethod> methods, ExecutableElement executable) {
for ( SourceMethod alreadyAdded : methods ) {
ExecutableElement executableInSubtype = alreadyAdded.getExecutable();
TypeElement declaringType = (TypeElement) executableInSubtype.getEnclosingElement();
if ( elementUtils.overrides( executableInSubtype, executable, declaringType ) ) {
return false;
}
}
return true;
}
private TypeElement asTypeElement(TypeMirror usedMapper) { private TypeElement asTypeElement(TypeMirror usedMapper) {
return (TypeElement) ( (DeclaredType) usedMapper ).asElement(); return (TypeElement) ( (DeclaredType) usedMapper ).asElement();
} }
private List<Element> allEnclosingElementsIncludeSuper(TypeElement element) {
List<Element> enclosedElements = new ArrayList<Element>( element.getEnclosedElements() );
for ( TypeMirror interfaceType : element.getInterfaces() ) {
enclosedElements.addAll( allEnclosingElementsIncludeSuper( asTypeElement( interfaceType ) ) );
}
if ( hasNonObjectSuperclass( element ) ) {
enclosedElements.addAll( allEnclosingElementsIncludeSuper( asTypeElement( element.getSuperclass() ) ) );
}
return enclosedElements;
}
/**
* @param element the type element to check
*
* @return <code>true</code>, iff the type has a super-class that is not java.lang.Object
*/
private boolean hasNonObjectSuperclass(TypeElement element) {
return element.getSuperclass().getKind() == TypeKind.DECLARED
&& asTypeElement( element.getSuperclass() ).getSuperclass().getKind() == TypeKind.DECLARED;
}
private SourceMethod getMethod(TypeElement usedMapper, private SourceMethod getMethod(TypeElement usedMapper,
ExecutableElement method, ExecutableElement method,
TypeElement mapperToImplement) { TypeElement mapperToImplement) {

View File

@ -19,6 +19,7 @@
package org.mapstruct.ap.util; package org.mapstruct.ap.util;
import java.beans.Introspector; import java.beans.Introspector;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -29,9 +30,12 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
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.Elements;
import javax.lang.model.util.SimpleElementVisitor6; import javax.lang.model.util.SimpleElementVisitor6;
import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.SimpleTypeVisitor6;
import static javax.lang.model.util.ElementFilter.methodsIn;
/** /**
* Provides functionality around {@link ExecutableElement}s. * Provides functionality around {@link ExecutableElement}s.
* *
@ -161,4 +165,107 @@ public class Executables {
return typeElement != null ? typeElement.getQualifiedName().toString() : null; return typeElement != null ? typeElement.getQualifiedName().toString() : null;
} }
/**
* @param mirror the type mirror
* @return the corresponding type element
*/
private static TypeElement asTypeElement(TypeMirror mirror) {
return (TypeElement) ( (DeclaredType) mirror ).asElement();
}
/**
* Finds all executable elements within the given type element, including executable elements defined in super
* classes and implemented interfaces. Methods defined in {@link java.lang.Object} are ignored, as well as
* implementations of {@link java.lang.Object#equals(Object)}.
*
* @param elementUtils element helper
* @param element the element to inspect
* @return the executable elements usable in the type
*/
public static List<ExecutableElement> getAllEnclosingExecutableElements(
Elements elementUtils, TypeElement element) {
List<ExecutableElement> enclosedElements = new ArrayList<ExecutableElement>();
addEnclosingElementsIncludingSuper( elementUtils, enclosedElements, element );
return enclosedElements;
}
private static void addEnclosingElementsIncludingSuper(Elements elementUtils,
List<ExecutableElement> alreadyAdded,
TypeElement element) {
addNotYetOverridden( elementUtils, alreadyAdded, methodsIn( element.getEnclosedElements() ) );
if ( hasNonObjectSuperclass( element ) ) {
addEnclosingElementsIncludingSuper( elementUtils, alreadyAdded, asTypeElement( element.getSuperclass() ) );
}
for ( TypeMirror interfaceType : element.getInterfaces() ) {
addEnclosingElementsIncludingSuper( elementUtils, alreadyAdded, asTypeElement( interfaceType ) );
}
}
/**
* @param alreadyCollected methods that have already been collected and to which the not-yet-overridden methods will
* be added
* @param methodsToAdd methods to add to alreadyAdded, if they are not yet overridden by an element in the list
*/
private static void addNotYetOverridden(Elements elementUtils, List<ExecutableElement> alreadyCollected,
List<ExecutableElement> methodsToAdd) {
List<ExecutableElement> safeToAdd = new ArrayList<ExecutableElement>( methodsToAdd.size() );
for ( ExecutableElement toAdd : methodsToAdd ) {
if ( isNotObjectEquals( toAdd ) && wasNotYetOverridden( elementUtils, alreadyCollected, toAdd ) ) {
safeToAdd.add( toAdd );
}
}
alreadyCollected.addAll( safeToAdd );
}
/**
* @param executable the executable to check
* @return <code>true</code>, iff the executable does not represent {@link java.lang.Object#equals(Object)} or an
* overridden version of it
*/
private static boolean isNotObjectEquals(ExecutableElement executable) {
if ( executable.getSimpleName().contentEquals( "equals" ) && executable.getParameters().size() == 1
&& asTypeElement( executable.getParameters().get( 0 ).asType() ).getQualifiedName().contentEquals(
"java.lang.Object" ) ) {
return false;
}
return true;
}
/**
* @param elementUtils the elementUtils
* @param methods the list of already collected methods of one type hierarchy (order is from sub-types to
* super-types)
* @param executable the method to check
* @return <code>true</code>, iff the given executable was not yet overridden by a method in the given list.
*/
private static boolean wasNotYetOverridden(Elements elementUtils, List<ExecutableElement> alreadyAdded,
ExecutableElement executable) {
for ( ExecutableElement executableInSubtype : alreadyAdded ) {
TypeElement declaringType = (TypeElement) executableInSubtype.getEnclosingElement();
if ( elementUtils.overrides( executableInSubtype, executable, declaringType ) ) {
return false;
}
}
return true;
}
/**
* @param element the type element to check
* @return <code>true</code>, iff the type has a super-class that is not java.lang.Object
*/
private static boolean hasNonObjectSuperclass(TypeElement element) {
return element.getSuperclass().getKind() == TypeKind.DECLARED
&& asTypeElement( element.getSuperclass() ).getSuperclass().getKind() == TypeKind.DECLARED;
}
} }

View File

@ -20,11 +20,10 @@ package org.mapstruct.ap.util;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
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 static javax.lang.model.util.ElementFilter.methodsIn;
/** /**
* Filter methods for working with {@link Element} collections. * Filter methods for working with {@link Element} collections.
* *
@ -35,10 +34,10 @@ public class Filters {
private Filters() { private Filters() {
} }
public static List<ExecutableElement> getterMethodsIn(Iterable<? extends Element> elements) { public static List<ExecutableElement> getterMethodsIn(Iterable<ExecutableElement> elements) {
List<ExecutableElement> getterMethods = new LinkedList<ExecutableElement>(); List<ExecutableElement> getterMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) { for ( ExecutableElement method : elements ) {
if ( Executables.isGetterMethod( method ) ) { if ( Executables.isGetterMethod( method ) ) {
getterMethods.add( method ); getterMethods.add( method );
} }
@ -47,10 +46,10 @@ public class Filters {
return getterMethods; return getterMethods;
} }
public static List<ExecutableElement> setterMethodsIn(Iterable<? extends Element> elements) { public static List<ExecutableElement> setterMethodsIn(Iterable<ExecutableElement> elements) {
List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>(); List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) { for ( ExecutableElement method : elements ) {
if ( Executables.isSetterMethod( method ) ) { if ( Executables.isSetterMethod( method ) ) {
setterMethods.add( method ); setterMethods.add( method );
} }
@ -58,10 +57,10 @@ public class Filters {
return setterMethods; return setterMethods;
} }
public static List<ExecutableElement> adderMethodsIn(Iterable<? extends Element> elements) { public static List<ExecutableElement> adderMethodsIn(Iterable<ExecutableElement> elements) {
List<ExecutableElement> adderMethods = new LinkedList<ExecutableElement>(); List<ExecutableElement> adderMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) { for ( ExecutableElement method : elements ) {
if ( Executables.isAdderMethod( method ) ) { if ( Executables.isAdderMethod( method ) ) {
adderMethods.add( method ); adderMethods.add( method );
} }

View File

@ -35,7 +35,10 @@ import static org.fest.assertions.Assertions.assertThat;
BaseMapperInterface.class, BaseMapperInterface.class,
ReferencedMapper.class, ReferencedMapper.class,
AbstractReferencedMapper.class, AbstractReferencedMapper.class,
ReferencedMapperInterface.class } ) ReferencedMapperInterface.class,
AbstractDto.class,
Identifiable.class,
Measurable.class } )
@RunWith( AnnotationProcessorTestRunner.class ) @RunWith( AnnotationProcessorTestRunner.class )
public class AbstractClassTest { public class AbstractClassTest {
@ -69,5 +72,6 @@ public class AbstractClassTest {
assertThat( target.getBirthday() ).isEqualTo( "Birthday: 26.04.1948" ); assertThat( target.getBirthday() ).isEqualTo( "Birthday: 26.04.1948" );
assertThat( target.getManuallyConverted() ).isEqualTo( 42 ); assertThat( target.getManuallyConverted() ).isEqualTo( 42 );
assertThat( target.isNotAttractingEqualsMethod() ).isTrue(); assertThat( target.isNotAttractingEqualsMethod() ).isTrue();
assertThat( target.getId() ).isEqualTo( 42L );
} }
} }

View File

@ -0,0 +1,37 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.abstractclass;
/**
* @author Andreas Gudian
*
*/
public class AbstractDto implements Identifiable {
private Long id = 1L;
@Override
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@ -0,0 +1,27 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.abstractclass;
/**
* @author Andreas Gudian
*
*/
public interface Identifiable {
Long getId();
}

View File

@ -0,0 +1,27 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.test.abstractclass;
/**
* @author Andreas Gudian
*
*/
public interface Measurable {
int getSize();
}

View File

@ -22,7 +22,7 @@ import java.util.Calendar;
import javax.xml.ws.Holder; import javax.xml.ws.Holder;
public class Source { public class Source extends AbstractDto {
private final int size; private final int size;
private final Calendar birthday; private final Calendar birthday;
@ -33,6 +33,8 @@ public class Source {
size = 181; size = 181;
birthday = Calendar.getInstance(); birthday = Calendar.getInstance();
birthday.set( 1948, 3, 26 ); birthday.set( 1948, 3, 26 );
super.setId( 42L );
} }
public int getSize() { public int getSize() {

View File

@ -18,7 +18,7 @@
*/ */
package org.mapstruct.ap.test.abstractclass; package org.mapstruct.ap.test.abstractclass;
public class Target { public class Target extends AbstractDto {
private Long size; private Long size;
private String birthday; private String birthday;