mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#636 - refinement of solution java8 default and static interface methods
This commit is contained in:
parent
a15a67ff47
commit
45db85a7be
@ -18,17 +18,13 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.bugs._636;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Source {
|
||||
private final long idFoo;
|
||||
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.idBar = idBar;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public long getIdFoo() {
|
||||
@ -38,8 +34,4 @@ public class Source {
|
||||
public String getIdBar() {
|
||||
return idBar;
|
||||
}
|
||||
|
||||
public BigInteger getNumber() {
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
@ -18,17 +18,17 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.bugs._636;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public class MyMapper {
|
||||
public BigDecimal mapBigIntToDecimal(BigInteger source) {
|
||||
return source == null ? null : new BigDecimal( source );
|
||||
public interface SourceTargetBaseMapper {
|
||||
|
||||
|
||||
default Foo fooFromId(long id) {
|
||||
return new Foo(id);
|
||||
}
|
||||
|
||||
static Bar barFromId(String id) {
|
||||
return new Bar(id);
|
||||
}
|
||||
}
|
@ -23,22 +23,14 @@ import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(uses = MyMapper.class)
|
||||
public interface SourceTargetMapper {
|
||||
@Mapper
|
||||
public interface SourceTargetMapper extends SourceTargetBaseMapper {
|
||||
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "idFoo", target = "foo"),
|
||||
@Mapping(source = "idBar", target = "bar"),
|
||||
@Mapping(source = "number", target = "number")
|
||||
@Mapping(source = "idBar", target = "bar")
|
||||
})
|
||||
Target mapSourceToTarget(Source source);
|
||||
|
||||
default Foo fooFromId(long id) {
|
||||
return new Foo(id);
|
||||
}
|
||||
|
||||
static Bar barFromId(String id) {
|
||||
return new Bar(id);
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,10 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.bugs._636;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class Target {
|
||||
private Foo foo;
|
||||
private Bar bar;
|
||||
private BigDecimal number;
|
||||
|
||||
public Foo getFoo() {
|
||||
return foo;
|
||||
@ -41,11 +39,4 @@ public class Target {
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
public BigDecimal getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(BigDecimal number) {
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.bugs._636;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
@ -31,9 +29,8 @@ public class Issue636Test {
|
||||
|
||||
final long idFoo = 123;
|
||||
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 );
|
||||
|
||||
@ -42,7 +39,5 @@ public class Issue636Test {
|
||||
assertThat( target.getFoo().getId() ).isEqualTo( idFoo );
|
||||
assertThat( target.getBar() ).isNotNull();
|
||||
assertThat( target.getBar().getId() ).isEqualTo( idBar );
|
||||
assertThat( target.getNumber() ).isNotNull();
|
||||
assertThat( target.getNumber().toBigInteger() ).isEqualTo( number );
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ public abstract class MappingMethod extends ModelElement {
|
||||
private final Accessibility accessibility;
|
||||
private final List<Type> thrownTypes;
|
||||
private final boolean isStatic;
|
||||
private final Type staticMethodFromInterfaceType;
|
||||
private final String resultName;
|
||||
private final List<LifecycleCallbackMethodReference> beforeMappingReferencesWithMappingTarget;
|
||||
private final List<LifecycleCallbackMethodReference> beforeMappingReferencesWithoutMappingTarget;
|
||||
@ -70,7 +69,6 @@ public abstract class MappingMethod extends ModelElement {
|
||||
this.accessibility = method.getAccessibility();
|
||||
this.thrownTypes = method.getThrownTypes();
|
||||
this.isStatic = method.isStatic();
|
||||
this.staticMethodFromInterfaceType = method.getStaticMethodFromInterfaceType();
|
||||
this.resultName = initResultName( existingVariableNames );
|
||||
this.beforeMappingReferencesWithMappingTarget = filterMappingTarget( beforeMappingReferences, true );
|
||||
this.beforeMappingReferencesWithoutMappingTarget = filterMappingTarget( beforeMappingReferences, false );
|
||||
@ -146,10 +144,6 @@ public abstract class MappingMethod extends ModelElement {
|
||||
return isStatic;
|
||||
}
|
||||
|
||||
public Type getStaticMethodFromInterfaceType() {
|
||||
return staticMethodFromInterfaceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> getImportTypes() {
|
||||
Set<Type> types = new HashSet<Type>();
|
||||
@ -160,9 +154,6 @@ public abstract class MappingMethod extends ModelElement {
|
||||
|
||||
types.add( getReturnType() );
|
||||
types.addAll( thrownTypes );
|
||||
if ( staticMethodFromInterfaceType != null ) {
|
||||
types.add( staticMethodFromInterfaceType );
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,9 @@ public class MethodReference extends MappingMethod implements Assignment {
|
||||
*/
|
||||
private Assignment assignment;
|
||||
|
||||
|
||||
private final Type definingType;
|
||||
|
||||
/**
|
||||
* 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.thrownTypes = method.getThrownTypes();
|
||||
this.isUpdateMethod = method.getMappingTargetParameter() != null;
|
||||
this.definingType = method.getDefiningType();
|
||||
}
|
||||
|
||||
public MethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
||||
@ -88,6 +92,7 @@ public class MethodReference extends MappingMethod implements Assignment {
|
||||
this.contextParam = method.getContextParameter( contextParam );
|
||||
this.importTypes = Collections.emptySet();
|
||||
this.thrownTypes = Collections.emptyList();
|
||||
this.definingType = null;
|
||||
this.isUpdateMethod = method.getMappingTargetParameter() != null;
|
||||
}
|
||||
|
||||
@ -129,12 +134,19 @@ public class MethodReference extends MappingMethod implements Assignment {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Type getDefiningType() {
|
||||
return definingType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> getImportTypes() {
|
||||
Set<Type> imported = new HashSet<Type>( importTypes );
|
||||
if ( assignment != null ) {
|
||||
imported.addAll( assignment.getImportTypes() );
|
||||
}
|
||||
if ( isStatic() ) {
|
||||
imported.add( definingType );
|
||||
}
|
||||
return imported;
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ public class ForgedMethod implements Method {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getStaticMethodFromInterfaceType() {
|
||||
public Type getDefiningType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -152,11 +152,10 @@ public interface Method {
|
||||
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();
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -66,8 +66,7 @@ public class SourceMethod implements Method {
|
||||
private final MapperConfiguration config;
|
||||
private final MappingOptions mappingOptions;
|
||||
private final List<SourceMethod> prototypeMethods;
|
||||
private final boolean defaultMethod;
|
||||
private final Type staticMethodFromInterfaceType;
|
||||
private final Type mapperToImplement;
|
||||
|
||||
private List<Parameter> sourceParameters;
|
||||
|
||||
@ -78,6 +77,7 @@ public class SourceMethod implements Method {
|
||||
public static class Builder {
|
||||
|
||||
private Type declaringMapper = null;
|
||||
private Type definingType = null;
|
||||
private ExecutableElement executable;
|
||||
private List<Parameter> parameters;
|
||||
private Type returnType = null;
|
||||
@ -91,8 +91,6 @@ public class SourceMethod implements Method {
|
||||
private FormattingMessager messager = null;
|
||||
private MapperConfiguration mapperConfig = null;
|
||||
private List<SourceMethod> prototypeMethods = Collections.emptyList();
|
||||
private boolean defaultMethod;
|
||||
private Type staticMethodFromInterfaceType;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
@ -167,13 +165,8 @@ public class SourceMethod implements Method {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDefaultMethod(boolean defaultMethod) {
|
||||
this.defaultMethod = defaultMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStaticMethodFromInterfaceType(Type staticMethodFromInterfaceType) {
|
||||
this.staticMethodFromInterfaceType = staticMethodFromInterfaceType;
|
||||
public Builder setDefininingType(Type definingType) {
|
||||
this.definingType = definingType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -182,6 +175,7 @@ public class SourceMethod implements Method {
|
||||
MappingOptions mappingOptions
|
||||
= new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping );
|
||||
|
||||
|
||||
SourceMethod sourceMethod = new SourceMethod(
|
||||
declaringMapper,
|
||||
executable,
|
||||
@ -193,8 +187,7 @@ public class SourceMethod implements Method {
|
||||
typeFactory,
|
||||
mapperConfig,
|
||||
prototypeMethods,
|
||||
defaultMethod,
|
||||
staticMethodFromInterfaceType
|
||||
definingType
|
||||
);
|
||||
|
||||
if ( mappings != null ) {
|
||||
@ -212,7 +205,7 @@ public class SourceMethod implements Method {
|
||||
private SourceMethod(Type declaringMapper, ExecutableElement executable, List<Parameter> parameters,
|
||||
Type returnType, List<Type> exceptionTypes, MappingOptions mappingOptions, Types typeUtils,
|
||||
TypeFactory typeFactory, MapperConfiguration config, List<SourceMethod> prototypeMethods,
|
||||
boolean defaultMethod, Type staticMethodFromInterfaceType) {
|
||||
Type mapperToImplement) {
|
||||
this.declaringMapper = declaringMapper;
|
||||
this.executable = executable;
|
||||
this.parameters = parameters;
|
||||
@ -229,8 +222,7 @@ public class SourceMethod implements Method {
|
||||
this.typeFactory = typeFactory;
|
||||
this.config = config;
|
||||
this.prototypeMethods = prototypeMethods;
|
||||
this.defaultMethod = defaultMethod;
|
||||
this.staticMethodFromInterfaceType = staticMethodFromInterfaceType;
|
||||
this.mapperToImplement = mapperToImplement;
|
||||
}
|
||||
|
||||
private Parameter determineMappingTargetParameter(Iterable<Parameter> parameters) {
|
||||
@ -532,12 +524,12 @@ public class SourceMethod implements Method {
|
||||
|
||||
@Override
|
||||
public boolean isDefault() {
|
||||
return defaultMethod;
|
||||
return Executables.isDefaultMethod( executable );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getStaticMethodFromInterfaceType() {
|
||||
return staticMethodFromInterfaceType;
|
||||
public Type getDefiningType() {
|
||||
return mapperToImplement;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -245,7 +245,7 @@ public abstract class BuiltInMethod implements Method {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getStaticMethodFromInterfaceType() {
|
||||
public Type getDefiningType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
@ -87,7 +86,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
}
|
||||
|
||||
List<SourceMethod> prototypeMethods =
|
||||
retrievePrototypeMethods( mapperConfig.getMapperConfigMirror(), mapperConfig, mapperTypeElement );
|
||||
retrievePrototypeMethods( mapperConfig.getMapperConfigMirror(), mapperConfig );
|
||||
return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperConfig, prototypeMethods );
|
||||
}
|
||||
|
||||
@ -96,8 +95,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
return 1;
|
||||
}
|
||||
|
||||
private List<SourceMethod> retrievePrototypeMethods(TypeMirror typeMirror, MapperConfiguration mapperConfig,
|
||||
TypeElement mapperTypeElement) {
|
||||
private List<SourceMethod> retrievePrototypeMethods(TypeMirror typeMirror, MapperConfiguration mapperConfig ) {
|
||||
if ( typeMirror == null || typeMirror.getKind() == TypeKind.VOID ) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@ -120,8 +118,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
parameters,
|
||||
containsTargetTypeParameter,
|
||||
mapperConfig,
|
||||
prototypeMethods,
|
||||
mapperTypeElement
|
||||
prototypeMethods
|
||||
);
|
||||
|
||||
if ( method != null ) {
|
||||
@ -196,8 +193,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
parameters,
|
||||
containsTargetTypeParameter,
|
||||
mapperConfig,
|
||||
prototypeMethods,
|
||||
mapperToImplement);
|
||||
prototypeMethods );
|
||||
}
|
||||
//otherwise add reference to existing mapper method
|
||||
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters, returnType )
|
||||
@ -213,8 +209,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
List<Parameter> parameters,
|
||||
boolean containsTargetTypeParameter,
|
||||
MapperConfiguration mapperConfig,
|
||||
List<SourceMethod> prototypeMethods,
|
||||
TypeElement mapperToImplement) {
|
||||
List<SourceMethod> prototypeMethods ) {
|
||||
Type returnType = typeFactory.getReturnType( methodType );
|
||||
List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType );
|
||||
List<Parameter> sourceParameters = extractSourceParameters( parameters );
|
||||
@ -256,7 +251,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
.setTypeFactory( typeFactory )
|
||||
.setMapperConfiguration( mapperConfig )
|
||||
.setPrototypeMethods( prototypeMethods )
|
||||
.setDefaultMethod( Executables.isInterfaceDefaultMethod( method, mapperToImplement ) )
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -267,35 +261,26 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType );
|
||||
Type usedMapperAsType = typeFactory.getType( usedMapper );
|
||||
Type mapperToImplementAsType = typeFactory.getType( mapperToImplement );
|
||||
Type staticMethodFromInterfaceType = findStaticMethodFromInterfaceType( method );
|
||||
|
||||
if ( !mapperToImplementAsType.canAccess( usedMapperAsType, method ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Type definingType = typeFactory.getType( method.getEnclosingElement().asType() );
|
||||
|
||||
return new SourceMethod.Builder()
|
||||
.setDeclaringMapper( usedMapper.equals( mapperToImplement ) ? null : usedMapperAsType )
|
||||
.setDefininingType( definingType )
|
||||
.setExecutable( method )
|
||||
.setParameters( parameters )
|
||||
.setReturnType( returnType )
|
||||
.setExceptionTypes( exceptionTypes )
|
||||
.setTypeUtils( typeUtils )
|
||||
.setTypeFactory( typeFactory )
|
||||
.setDefaultMethod( Executables.isInterfaceDefaultMethod( method, mapperToImplement ) )
|
||||
.setStaticMethodFromInterfaceType( staticMethodFromInterfaceType )
|
||||
.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) {
|
||||
return isVoid( returnType ) && Executables.isLifecycleCallbackMethod( method );
|
||||
}
|
||||
|
@ -21,9 +21,7 @@ package org.mapstruct.ap.internal.util;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
@ -196,17 +194,6 @@ public class Executables {
|
||||
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
|
||||
|
@ -21,7 +21,8 @@
|
||||
<@compress single_line=true>
|
||||
<#-- 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/>
|
||||
<#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>
|
||||
<@params/>
|
||||
</#if>
|
||||
|
Loading…
x
Reference in New Issue
Block a user