#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;
}
/**
* 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
public int hashCode() {
final int prime = 31;

View File

@ -18,13 +18,10 @@
*/
package org.mapstruct.ap.processor;
import static javax.lang.model.util.ElementFilter.methodsIn;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
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.MapperConfig;
import static javax.lang.model.util.ElementFilter.methodsIn;
/**
* 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
@ -131,6 +130,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
/**
* @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) {
@ -142,67 +142,78 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
ExecutableElement method,
TypeElement mapperToImplement) {
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 containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters );
//add method with property mappings if an implementation needs to be generated
if ( ( usedMapper.equals( mapperToImplement ) ) && methodRequiresImplementation ) {
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
SourceMethod.forMethodRequiringImplementation(
method,
parameters,
returnType,
exceptionTypes,
getMappings( method ),
IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ),
MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ) ),
typeUtils
);
}
else {
return null;
}
return getMethodRequiringImplementation( method, parameters, containsTargetTypeParameter );
}
//otherwise add reference to existing mapper method
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters ) ) {
Type usedMapperAsType = typeFactory.getType( usedMapper );
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;
}
return getReferencedMethod( usedMapper, method, mapperToImplement, parameters );
}
else {
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) {
return isValidReferencedOrFactoryMethod( 1, parameters );
}
@ -233,24 +244,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
&& 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) {
for ( Parameter param : parameters ) {
if ( param.isMappingTarget() ) {
@ -328,7 +321,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
messager.printMessage(
Kind.ERROR,
"Can't generate mapping method that has a parameter annotated with @TargetType.",
method );
method
);
return false;
}

View File

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

View File

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

View File

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