#636 - refinement of solution java8 default and static interface methods

This commit is contained in:
sjaakd 2015-09-12 20:40:56 +02:00
parent a15a67ff47
commit 45db85a7be
14 changed files with 52 additions and 115 deletions

View File

@ -18,17 +18,13 @@
*/ */
package org.mapstruct.ap.test.bugs._636; package org.mapstruct.ap.test.bugs._636;
import java.math.BigInteger;
public class Source { public class Source {
private final long idFoo; private final long idFoo;
private final String idBar; private final String idBar;
private final BigInteger number;
public Source(long idFoo, String idBar, BigInteger number) { public Source(long idFoo, String idBar) {
this.idFoo = idFoo; this.idFoo = idFoo;
this.idBar = idBar; this.idBar = idBar;
this.number = number;
} }
public long getIdFoo() { public long getIdFoo() {
@ -38,8 +34,4 @@ public class Source {
public String getIdBar() { public String getIdBar() {
return idBar; return idBar;
} }
public BigInteger getNumber() {
return number;
}
} }

View File

@ -18,17 +18,17 @@
*/ */
package org.mapstruct.ap.test.bugs._636; package org.mapstruct.ap.test.bugs._636;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper @Mapper
public class MyMapper { public interface SourceTargetBaseMapper {
public BigDecimal mapBigIntToDecimal(BigInteger source) {
return source == null ? null : new BigDecimal( source );
default Foo fooFromId(long id) {
return new Foo(id);
}
static Bar barFromId(String id) {
return new Bar(id);
} }
} }

View File

@ -23,22 +23,14 @@ import org.mapstruct.Mapping;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
@Mapper(uses = MyMapper.class) @Mapper
public interface SourceTargetMapper { public interface SourceTargetMapper extends SourceTargetBaseMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mappings({ @Mappings({
@Mapping(source = "idFoo", target = "foo"), @Mapping(source = "idFoo", target = "foo"),
@Mapping(source = "idBar", target = "bar"), @Mapping(source = "idBar", target = "bar")
@Mapping(source = "number", target = "number")
}) })
Target mapSourceToTarget(Source source); Target mapSourceToTarget(Source source);
default Foo fooFromId(long id) {
return new Foo(id);
}
static Bar barFromId(String id) {
return new Bar(id);
}
} }

View File

@ -18,12 +18,10 @@
*/ */
package org.mapstruct.ap.test.bugs._636; package org.mapstruct.ap.test.bugs._636;
import java.math.BigDecimal;
public class Target { public class Target {
private Foo foo; private Foo foo;
private Bar bar; private Bar bar;
private BigDecimal number;
public Foo getFoo() { public Foo getFoo() {
return foo; return foo;
@ -41,11 +39,4 @@ public class Target {
this.bar = bar; this.bar = bar;
} }
public BigDecimal getNumber() {
return number;
}
public void setNumber(BigDecimal number) {
this.number = number;
}
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.mapstruct.ap.test.bugs._636; package org.mapstruct.ap.test.bugs._636;
import java.math.BigInteger;
import org.junit.Test; import org.junit.Test;
import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Assertions.assertThat;
@ -31,9 +29,8 @@ public class Issue636Test {
final long idFoo = 123; final long idFoo = 123;
final String idBar = "Bar456"; final String idBar = "Bar456";
final BigInteger number = BigInteger.valueOf( 789L );
final Source source = new Source( idFoo, idBar, number ); final Source source = new Source( idFoo, idBar );
final Target target = SourceTargetMapper.INSTANCE.mapSourceToTarget( source ); final Target target = SourceTargetMapper.INSTANCE.mapSourceToTarget( source );
@ -42,7 +39,5 @@ public class Issue636Test {
assertThat( target.getFoo().getId() ).isEqualTo( idFoo ); assertThat( target.getFoo().getId() ).isEqualTo( idFoo );
assertThat( target.getBar() ).isNotNull(); assertThat( target.getBar() ).isNotNull();
assertThat( target.getBar().getId() ).isEqualTo( idBar ); assertThat( target.getBar().getId() ).isEqualTo( idBar );
assertThat( target.getNumber() ).isNotNull();
assertThat( target.getNumber().toBigInteger() ).isEqualTo( number );
} }
} }

View File

@ -47,7 +47,6 @@ public abstract class MappingMethod extends ModelElement {
private final Accessibility accessibility; private final Accessibility accessibility;
private final List<Type> thrownTypes; private final List<Type> thrownTypes;
private final boolean isStatic; private final boolean isStatic;
private final Type staticMethodFromInterfaceType;
private final String resultName; private final String resultName;
private final List<LifecycleCallbackMethodReference> beforeMappingReferencesWithMappingTarget; private final List<LifecycleCallbackMethodReference> beforeMappingReferencesWithMappingTarget;
private final List<LifecycleCallbackMethodReference> beforeMappingReferencesWithoutMappingTarget; private final List<LifecycleCallbackMethodReference> beforeMappingReferencesWithoutMappingTarget;
@ -70,7 +69,6 @@ public abstract class MappingMethod extends ModelElement {
this.accessibility = method.getAccessibility(); this.accessibility = method.getAccessibility();
this.thrownTypes = method.getThrownTypes(); this.thrownTypes = method.getThrownTypes();
this.isStatic = method.isStatic(); this.isStatic = method.isStatic();
this.staticMethodFromInterfaceType = method.getStaticMethodFromInterfaceType();
this.resultName = initResultName( existingVariableNames ); this.resultName = initResultName( existingVariableNames );
this.beforeMappingReferencesWithMappingTarget = filterMappingTarget( beforeMappingReferences, true ); this.beforeMappingReferencesWithMappingTarget = filterMappingTarget( beforeMappingReferences, true );
this.beforeMappingReferencesWithoutMappingTarget = filterMappingTarget( beforeMappingReferences, false ); this.beforeMappingReferencesWithoutMappingTarget = filterMappingTarget( beforeMappingReferences, false );
@ -146,10 +144,6 @@ public abstract class MappingMethod extends ModelElement {
return isStatic; return isStatic;
} }
public Type getStaticMethodFromInterfaceType() {
return staticMethodFromInterfaceType;
}
@Override @Override
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
Set<Type> types = new HashSet<Type>(); Set<Type> types = new HashSet<Type>();
@ -160,9 +154,6 @@ public abstract class MappingMethod extends ModelElement {
types.add( getReturnType() ); types.add( getReturnType() );
types.addAll( thrownTypes ); types.addAll( thrownTypes );
if ( staticMethodFromInterfaceType != null ) {
types.add( staticMethodFromInterfaceType );
}
return types; return types;
} }

View File

@ -61,6 +61,9 @@ public class MethodReference extends MappingMethod implements Assignment {
*/ */
private Assignment assignment; private Assignment assignment;
private final Type definingType;
/** /**
* Creates a new reference to the given method. * Creates a new reference to the given method.
* *
@ -80,6 +83,7 @@ public class MethodReference extends MappingMethod implements Assignment {
this.importTypes = Collections.<Type>unmodifiableSet( imported ); this.importTypes = Collections.<Type>unmodifiableSet( imported );
this.thrownTypes = method.getThrownTypes(); this.thrownTypes = method.getThrownTypes();
this.isUpdateMethod = method.getMappingTargetParameter() != null; this.isUpdateMethod = method.getMappingTargetParameter() != null;
this.definingType = method.getDefiningType();
} }
public MethodReference(BuiltInMethod method, ConversionContext contextParam) { public MethodReference(BuiltInMethod method, ConversionContext contextParam) {
@ -88,6 +92,7 @@ public class MethodReference extends MappingMethod implements Assignment {
this.contextParam = method.getContextParameter( contextParam ); this.contextParam = method.getContextParameter( contextParam );
this.importTypes = Collections.emptySet(); this.importTypes = Collections.emptySet();
this.thrownTypes = Collections.emptyList(); this.thrownTypes = Collections.emptyList();
this.definingType = null;
this.isUpdateMethod = method.getMappingTargetParameter() != null; this.isUpdateMethod = method.getMappingTargetParameter() != null;
} }
@ -129,12 +134,19 @@ public class MethodReference extends MappingMethod implements Assignment {
return null; return null;
} }
public Type getDefiningType() {
return definingType;
}
@Override @Override
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
Set<Type> imported = new HashSet<Type>( importTypes ); Set<Type> imported = new HashSet<Type>( importTypes );
if ( assignment != null ) { if ( assignment != null ) {
imported.addAll( assignment.getImportTypes() ); imported.addAll( assignment.getImportTypes() );
} }
if ( isStatic() ) {
imported.add( definingType );
}
return imported; return imported;
} }

View File

@ -198,7 +198,7 @@ public class ForgedMethod implements Method {
} }
@Override @Override
public Type getStaticMethodFromInterfaceType() { public Type getDefiningType() {
return null; return null;
} }

View File

@ -152,11 +152,10 @@ public interface Method {
boolean isDefault(); boolean isDefault();
/** /**
* Returns method's enclosing type if method is Java 8 static method
* *
* @return type of static method from Java 8 interface * @return the Type (class or interface) that defines this method.
*/ */
Type getStaticMethodFromInterfaceType(); Type getDefiningType();
/** /**
* *

View File

@ -66,8 +66,7 @@ public class SourceMethod implements Method {
private final MapperConfiguration config; private final MapperConfiguration config;
private final MappingOptions mappingOptions; private final MappingOptions mappingOptions;
private final List<SourceMethod> prototypeMethods; private final List<SourceMethod> prototypeMethods;
private final boolean defaultMethod; private final Type mapperToImplement;
private final Type staticMethodFromInterfaceType;
private List<Parameter> sourceParameters; private List<Parameter> sourceParameters;
@ -78,6 +77,7 @@ public class SourceMethod implements Method {
public static class Builder { public static class Builder {
private Type declaringMapper = null; private Type declaringMapper = null;
private Type definingType = null;
private ExecutableElement executable; private ExecutableElement executable;
private List<Parameter> parameters; private List<Parameter> parameters;
private Type returnType = null; private Type returnType = null;
@ -91,8 +91,6 @@ public class SourceMethod implements Method {
private FormattingMessager messager = null; private FormattingMessager messager = null;
private MapperConfiguration mapperConfig = null; private MapperConfiguration mapperConfig = null;
private List<SourceMethod> prototypeMethods = Collections.emptyList(); private List<SourceMethod> prototypeMethods = Collections.emptyList();
private boolean defaultMethod;
private Type staticMethodFromInterfaceType;
public Builder() { public Builder() {
} }
@ -167,13 +165,8 @@ public class SourceMethod implements Method {
return this; return this;
} }
public Builder setDefaultMethod(boolean defaultMethod) { public Builder setDefininingType(Type definingType) {
this.defaultMethod = defaultMethod; this.definingType = definingType;
return this;
}
public Builder setStaticMethodFromInterfaceType(Type staticMethodFromInterfaceType) {
this.staticMethodFromInterfaceType = staticMethodFromInterfaceType;
return this; return this;
} }
@ -182,6 +175,7 @@ public class SourceMethod implements Method {
MappingOptions mappingOptions MappingOptions mappingOptions
= new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping ); = new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping );
SourceMethod sourceMethod = new SourceMethod( SourceMethod sourceMethod = new SourceMethod(
declaringMapper, declaringMapper,
executable, executable,
@ -193,8 +187,7 @@ public class SourceMethod implements Method {
typeFactory, typeFactory,
mapperConfig, mapperConfig,
prototypeMethods, prototypeMethods,
defaultMethod, definingType
staticMethodFromInterfaceType
); );
if ( mappings != null ) { if ( mappings != null ) {
@ -212,7 +205,7 @@ public class SourceMethod implements Method {
private SourceMethod(Type declaringMapper, ExecutableElement executable, List<Parameter> parameters, private SourceMethod(Type declaringMapper, ExecutableElement executable, List<Parameter> parameters,
Type returnType, List<Type> exceptionTypes, MappingOptions mappingOptions, Types typeUtils, Type returnType, List<Type> exceptionTypes, MappingOptions mappingOptions, Types typeUtils,
TypeFactory typeFactory, MapperConfiguration config, List<SourceMethod> prototypeMethods, TypeFactory typeFactory, MapperConfiguration config, List<SourceMethod> prototypeMethods,
boolean defaultMethod, Type staticMethodFromInterfaceType) { Type mapperToImplement) {
this.declaringMapper = declaringMapper; this.declaringMapper = declaringMapper;
this.executable = executable; this.executable = executable;
this.parameters = parameters; this.parameters = parameters;
@ -229,8 +222,7 @@ public class SourceMethod implements Method {
this.typeFactory = typeFactory; this.typeFactory = typeFactory;
this.config = config; this.config = config;
this.prototypeMethods = prototypeMethods; this.prototypeMethods = prototypeMethods;
this.defaultMethod = defaultMethod; this.mapperToImplement = mapperToImplement;
this.staticMethodFromInterfaceType = staticMethodFromInterfaceType;
} }
private Parameter determineMappingTargetParameter(Iterable<Parameter> parameters) { private Parameter determineMappingTargetParameter(Iterable<Parameter> parameters) {
@ -532,12 +524,12 @@ public class SourceMethod implements Method {
@Override @Override
public boolean isDefault() { public boolean isDefault() {
return defaultMethod; return Executables.isDefaultMethod( executable );
} }
@Override @Override
public Type getStaticMethodFromInterfaceType() { public Type getDefiningType() {
return staticMethodFromInterfaceType; return mapperToImplement;
} }
@Override @Override

View File

@ -245,7 +245,7 @@ public abstract class BuiltInMethod implements Method {
} }
@Override @Override
public Type getStaticMethodFromInterfaceType() { public Type getDefiningType() {
return null; return null;
} }

View File

@ -23,7 +23,6 @@ import java.util.Collections;
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.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;
@ -87,7 +86,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
} }
List<SourceMethod> prototypeMethods = List<SourceMethod> prototypeMethods =
retrievePrototypeMethods( mapperConfig.getMapperConfigMirror(), mapperConfig, mapperTypeElement ); retrievePrototypeMethods( mapperConfig.getMapperConfigMirror(), mapperConfig );
return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperConfig, prototypeMethods ); return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperConfig, prototypeMethods );
} }
@ -96,8 +95,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return 1; return 1;
} }
private List<SourceMethod> retrievePrototypeMethods(TypeMirror typeMirror, MapperConfiguration mapperConfig, private List<SourceMethod> retrievePrototypeMethods(TypeMirror typeMirror, MapperConfiguration mapperConfig ) {
TypeElement mapperTypeElement) {
if ( typeMirror == null || typeMirror.getKind() == TypeKind.VOID ) { if ( typeMirror == null || typeMirror.getKind() == TypeKind.VOID ) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -120,8 +118,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
parameters, parameters,
containsTargetTypeParameter, containsTargetTypeParameter,
mapperConfig, mapperConfig,
prototypeMethods, prototypeMethods
mapperTypeElement
); );
if ( method != null ) { if ( method != null ) {
@ -196,8 +193,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
parameters, parameters,
containsTargetTypeParameter, containsTargetTypeParameter,
mapperConfig, mapperConfig,
prototypeMethods, prototypeMethods );
mapperToImplement);
} }
//otherwise add reference to existing mapper method //otherwise add reference to existing mapper method
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters, returnType ) else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters, returnType )
@ -213,8 +209,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
List<Parameter> parameters, List<Parameter> parameters,
boolean containsTargetTypeParameter, boolean containsTargetTypeParameter,
MapperConfiguration mapperConfig, MapperConfiguration mapperConfig,
List<SourceMethod> prototypeMethods, List<SourceMethod> prototypeMethods ) {
TypeElement mapperToImplement) {
Type returnType = typeFactory.getReturnType( methodType ); Type returnType = typeFactory.getReturnType( methodType );
List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType ); List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType );
List<Parameter> sourceParameters = extractSourceParameters( parameters ); List<Parameter> sourceParameters = extractSourceParameters( parameters );
@ -256,7 +251,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
.setTypeFactory( typeFactory ) .setTypeFactory( typeFactory )
.setMapperConfiguration( mapperConfig ) .setMapperConfiguration( mapperConfig )
.setPrototypeMethods( prototypeMethods ) .setPrototypeMethods( prototypeMethods )
.setDefaultMethod( Executables.isInterfaceDefaultMethod( method, mapperToImplement ) )
.build(); .build();
} }
@ -267,35 +261,26 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType ); List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType );
Type usedMapperAsType = typeFactory.getType( usedMapper ); Type usedMapperAsType = typeFactory.getType( usedMapper );
Type mapperToImplementAsType = typeFactory.getType( mapperToImplement ); Type mapperToImplementAsType = typeFactory.getType( mapperToImplement );
Type staticMethodFromInterfaceType = findStaticMethodFromInterfaceType( method );
if ( !mapperToImplementAsType.canAccess( usedMapperAsType, method ) ) { if ( !mapperToImplementAsType.canAccess( usedMapperAsType, method ) ) {
return null; return null;
} }
Type definingType = typeFactory.getType( method.getEnclosingElement().asType() );
return new SourceMethod.Builder() return new SourceMethod.Builder()
.setDeclaringMapper( usedMapper.equals( mapperToImplement ) ? null : usedMapperAsType ) .setDeclaringMapper( usedMapper.equals( mapperToImplement ) ? null : usedMapperAsType )
.setDefininingType( definingType )
.setExecutable( method ) .setExecutable( method )
.setParameters( parameters ) .setParameters( parameters )
.setReturnType( returnType ) .setReturnType( returnType )
.setExceptionTypes( exceptionTypes ) .setExceptionTypes( exceptionTypes )
.setTypeUtils( typeUtils ) .setTypeUtils( typeUtils )
.setTypeFactory( typeFactory ) .setTypeFactory( typeFactory )
.setDefaultMethod( Executables.isInterfaceDefaultMethod( method, mapperToImplement ) )
.setStaticMethodFromInterfaceType( staticMethodFromInterfaceType )
.build(); .build();
} }
private Type findStaticMethodFromInterfaceType(ExecutableElement method) {
Element enclosingElement = method.getEnclosingElement();
boolean staticMethodFromInterface = ( enclosingElement.getKind().isInterface() &&
Executables.isStaticFromInterfaceMethod(
method, ( (TypeElement) enclosingElement )
)
);
return staticMethodFromInterface ? typeFactory.getType( enclosingElement.asType() ) : null;
}
private boolean isValidLifecycleCallbackMethod(ExecutableElement method, Type returnType) { private boolean isValidLifecycleCallbackMethod(ExecutableElement method, Type returnType) {
return isVoid( returnType ) && Executables.isLifecycleCallbackMethod( method ); return isVoid( returnType ) && Executables.isLifecycleCallbackMethod( method );
} }

View File

@ -21,9 +21,7 @@ package org.mapstruct.ap.internal.util;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.lang.model.element.ElementKind;
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;
@ -196,17 +194,6 @@ public class Executables {
alreadyCollected.addAll( 0, safeToAdd ); alreadyCollected.addAll( 0, safeToAdd );
} }
public static boolean isInterfaceDefaultMethod(ExecutableElement element, TypeElement parentType) {
return parentType.getKind().isInterface() &&
element.getKind() == ElementKind.METHOD &&
isDefaultMethod( element );
}
public static boolean isStaticFromInterfaceMethod(ExecutableElement element, TypeElement parentType) {
return parentType.getKind().isInterface() &&
element.getKind() == ElementKind.METHOD &&
element.getModifiers().containsAll( Arrays.asList( Modifier.PUBLIC, Modifier.STATIC ) );
}
/** /**
* @param executable the executable to check * @param executable the executable to check

View File

@ -21,7 +21,8 @@
<@compress single_line=true> <@compress single_line=true>
<#-- method is either internal to the mapper class, or external (via uses) declaringMapper!=null --> <#-- method is either internal to the mapper class, or external (via uses) declaringMapper!=null -->
<#if declaringMapper??><#if static><@includeModel object=declaringMapper.type/><#else>${mapperVariableName}</#if>.<@params/> <#if declaringMapper??><#if static><@includeModel object=declaringMapper.type/><#else>${mapperVariableName}</#if>.<@params/>
<#elseif staticMethodFromInterfaceType??><@includeModel object=staticMethodFromInterfaceType/>.<@params/> <#-- method is referenced java8 static method in the mapper to implement (interface) -->
<#elseif static><@includeModel object=definingType/>.<@params/>
<#else> <#else>
<@params/> <@params/>
</#if> </#if>