#206 Moving canAccess() method to Type; Splitting up MethodRetrievalProcessor#getMethod()

This commit is contained in:
Gunnar Morling 2014-05-10 19:03:10 +02:00
parent a77a693d9d
commit fc2bd954c6
5 changed files with 85 additions and 74 deletions

View File

@ -363,6 +363,24 @@ public class Type extends ModelElement implements Comparable<Type> {
return 1 + minDistanceOfSuperToTargetType; return 1 + minDistanceOfSuperToTargetType;
} }
/**
* Whether this type can access the given method declared on the given type.
*/
public boolean canAccess(Type type, ExecutableElement method) {
if ( method.getModifiers().contains( Modifier.PRIVATE ) ) {
return false;
}
else if ( method.getModifiers().contains( Modifier.PROTECTED ) ) {
return isAssignableTo( type ) || getPackageName().equals( type.getPackageName() );
}
else if ( !method.getModifiers().contains( Modifier.PUBLIC ) ) {
// default
return getPackageName().equals( type.getPackageName() );
}
// public
return true;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;

View File

@ -18,13 +18,10 @@
*/ */
package org.mapstruct.ap.processor; package org.mapstruct.ap.processor;
import static javax.lang.model.util.ElementFilter.methodsIn;
import java.util.ArrayList; 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.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
@ -50,6 +47,8 @@ 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;
/** /**
* A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s * A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s
* representing all the mapping methods of the given bean mapper type as well as * representing all the mapping methods of the given bean mapper type as well as
@ -131,6 +130,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
/** /**
* @param element the type element to check * @param element the type element to check
*
* @return <code>true</code>, iff the type has a super-class that is not java.lang.Object * @return <code>true</code>, iff the type has a super-class that is not java.lang.Object
*/ */
private boolean hasNonObjectSuperclass(TypeElement element) { private boolean hasNonObjectSuperclass(TypeElement element) {
@ -142,67 +142,78 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
ExecutableElement method, ExecutableElement method,
TypeElement mapperToImplement) { TypeElement mapperToImplement) {
List<Parameter> parameters = typeFactory.getParameters( method ); List<Parameter> parameters = typeFactory.getParameters( method );
Type returnType = typeFactory.getReturnType( method );
List<Type> exceptionTypes = typeFactory.getThrownTypes( method );
//add method with property mappings if an implementation needs to be generated
boolean methodRequiresImplementation = method.getModifiers().contains( Modifier.ABSTRACT ); boolean methodRequiresImplementation = method.getModifiers().contains( Modifier.ABSTRACT );
boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters ); boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters );
//add method with property mappings if an implementation needs to be generated
if ( ( usedMapper.equals( mapperToImplement ) ) && methodRequiresImplementation ) { if ( ( usedMapper.equals( mapperToImplement ) ) && methodRequiresImplementation ) {
List<Parameter> sourceParameters = extractSourceParameters( parameters ); return getMethodRequiringImplementation( method, parameters, containsTargetTypeParameter );
Parameter targetParameter = extractTargetParameter( parameters );
Type resultType = selectResultType( returnType, targetParameter );
boolean isValid =
checkParameterAndReturnType(
method,
sourceParameters,
targetParameter,
resultType,
returnType,
containsTargetTypeParameter );
if ( isValid ) {
return
SourceMethod.forMethodRequiringImplementation(
method,
parameters,
returnType,
exceptionTypes,
getMappings( method ),
IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ),
MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ) ),
typeUtils
);
}
else {
return null;
}
} }
//otherwise add reference to existing mapper method //otherwise add reference to existing mapper method
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters ) ) { else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters ) ) {
Type usedMapperAsType = typeFactory.getType( usedMapper ); return getReferencedMethod( usedMapper, method, mapperToImplement, parameters );
Type mapperToImplementAsType = typeFactory.getType( mapperToImplement );
if ( isAccessible( mapperToImplementAsType, usedMapperAsType, method ) ) {
return SourceMethod.forReferencedMethod(
usedMapper.equals( mapperToImplement ) ? null : usedMapperAsType,
method,
parameters,
returnType,
exceptionTypes,
typeUtils
);
}
else {
return null;
}
} }
else { else {
return null; return null;
} }
} }
private SourceMethod getMethodRequiringImplementation(ExecutableElement method, List<Parameter> parameters,
boolean containsTargetTypeParameter) {
Type returnType = typeFactory.getReturnType( method );
List<Type> exceptionTypes = typeFactory.getThrownTypes( method );
List<Parameter> sourceParameters = extractSourceParameters( parameters );
Parameter targetParameter = extractTargetParameter( parameters );
Type resultType = selectResultType( returnType, targetParameter );
boolean isValid = checkParameterAndReturnType(
method,
sourceParameters,
targetParameter,
resultType,
returnType,
containsTargetTypeParameter
);
if ( !isValid ) {
return null;
}
return SourceMethod.forMethodRequiringImplementation(
method,
parameters,
returnType,
exceptionTypes,
getMappings( method ),
IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ),
MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ) ),
typeUtils
);
}
private SourceMethod getReferencedMethod(TypeElement usedMapper, ExecutableElement method,
TypeElement mapperToImplement, List<Parameter> parameters) {
Type returnType = typeFactory.getReturnType( method );
List<Type> exceptionTypes = typeFactory.getThrownTypes( method );
Type usedMapperAsType = typeFactory.getType( usedMapper );
Type mapperToImplementAsType = typeFactory.getType( mapperToImplement );
if ( !mapperToImplementAsType.canAccess( usedMapperAsType, method ) ) {
return null;
}
return SourceMethod.forReferencedMethod(
usedMapper.equals( mapperToImplement ) ? null : usedMapperAsType,
method,
parameters,
returnType,
exceptionTypes,
typeUtils
);
}
private boolean isValidReferencedMethod(List<Parameter> parameters) { private boolean isValidReferencedMethod(List<Parameter> parameters) {
return isValidReferencedOrFactoryMethod( 1, parameters ); return isValidReferencedOrFactoryMethod( 1, parameters );
} }
@ -233,24 +244,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
&& parameters.size() == validSourceParameters + targetParameters + targetTypeParameters; && parameters.size() == validSourceParameters + targetParameters + targetTypeParameters;
} }
private boolean isAccessible( Type mapperToImplement, Type usedMapper, ExecutableElement method ) {
if ( method.getModifiers().contains( Modifier.PRIVATE ) ) {
return false;
}
else if ( method.getModifiers().contains( Modifier.PROTECTED ) ) {
return mapperToImplement.isAssignableTo( usedMapper ) ||
mapperToImplement.getPackageName().equals( usedMapper.getPackageName() );
}
else if ( !method.getModifiers().contains( Modifier.PUBLIC ) ) {
// default
return mapperToImplement.getPackageName().equals( usedMapper.getPackageName() );
}
// public
return true;
}
private Parameter extractTargetParameter(List<Parameter> parameters) { private Parameter extractTargetParameter(List<Parameter> parameters) {
for ( Parameter param : parameters ) { for ( Parameter param : parameters ) {
if ( param.isMappingTarget() ) { if ( param.isMappingTarget() ) {
@ -328,7 +321,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
messager.printMessage( messager.printMessage(
Kind.ERROR, Kind.ERROR,
"Can't generate mapping method that has a parameter annotated with @TargetType.", "Can't generate mapping method that has a parameter annotated with @TargetType.",
method ); method
);
return false; return false;
} }

View File

@ -84,7 +84,6 @@ public class ReferencedAccessibilityTest {
@WithClasses( { AbstractSourceTargetMapperProtected.class, SourceTargetmapperProtectedBase.class } ) @WithClasses( { AbstractSourceTargetMapperProtected.class, SourceTargetmapperProtectedBase.class } )
public void shouldBeAbleToAccessProtectedMethodInBase() throws Exception { } public void shouldBeAbleToAccessProtectedMethodInBase() throws Exception { }
@Test @Test
@IssueKey( "206" ) @IssueKey( "206" )
@WithClasses( { AbstractSourceTargetMapperPrivate.class, SourceTargetmapperPrivateBase.class } ) @WithClasses( { AbstractSourceTargetMapperPrivate.class, SourceTargetmapperPrivateBase.class } )

View File

@ -19,12 +19,12 @@
package org.mapstruct.ap.test.accessibility.referenced; package org.mapstruct.ap.test.accessibility.referenced;
/** /**
*
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class ReferencedMapperPrivate { public class ReferencedMapperPrivate {
private ReferencedTarget sourceToTarget( ReferencedSource source ) { @SuppressWarnings("unused")
private ReferencedTarget sourceToTarget(ReferencedSource source) {
ReferencedTarget target = new ReferencedTarget(); ReferencedTarget target = new ReferencedTarget();
target.setFoo( source.getFoo() ); target.setFoo( source.getFoo() );
return target; return target;

View File

@ -19,12 +19,12 @@
package org.mapstruct.ap.test.accessibility.referenced; package org.mapstruct.ap.test.accessibility.referenced;
/** /**
*
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class SourceTargetmapperPrivateBase { public class SourceTargetmapperPrivateBase {
private ReferencedTarget sourceToTarget( ReferencedSource source ) { @SuppressWarnings("unused")
private ReferencedTarget sourceToTarget(ReferencedSource source) {
ReferencedTarget target = new ReferencedTarget(); ReferencedTarget target = new ReferencedTarget();
target.setFoo( source.getFoo() ); target.setFoo( source.getFoo() );
return target; return target;