mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
parent
e7f6813d9a
commit
1187e357c1
@ -340,6 +340,11 @@ public class ForgedMethod implements Method {
|
||||
return basedOn.getOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Type> getTypeParameters() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
// the name of the forged method is never fully qualified, so no need to distinguish
|
||||
|
@ -128,6 +128,11 @@ public abstract class HelperMethod implements Method {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Type> getTypeParameters() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* the conversion context is used to format an auxiliary parameter in the method call with context specific
|
||||
* information such as a date format.
|
||||
|
@ -141,6 +141,7 @@ public final class LifecycleMethodResolver {
|
||||
callbackMethods,
|
||||
Collections.emptyList(),
|
||||
targetType,
|
||||
method.getReturnType(),
|
||||
SelectionCriteria.forLifecycleMethods( selectionParameters ) );
|
||||
|
||||
return toLifecycleCallbackMethodRefs(
|
||||
|
@ -78,12 +78,13 @@ public class MappingBuilderContext {
|
||||
* returns a parameter assignment
|
||||
*
|
||||
* @param mappingMethod target mapping method
|
||||
* @param description
|
||||
* @param description the description source
|
||||
* @param targetType return type to match
|
||||
* @param formattingParameters used for formatting dates and numbers
|
||||
* @param criteria parameters criteria in the selection process
|
||||
* @param sourceRHS source information
|
||||
* @param positionHint the mirror for reporting problems
|
||||
* @param forger the supplier of the callback method to forge a method
|
||||
*
|
||||
* @return an assignment to a method parameter, which can either be:
|
||||
* <ol>
|
||||
|
@ -5,9 +5,12 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
@ -23,8 +26,6 @@ import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
@ -129,11 +130,13 @@ public class ObjectFactoryMethodResolver {
|
||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() );
|
||||
|
||||
return selectors.getMatchingMethods(
|
||||
method,
|
||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||
java.util.Collections.emptyList(),
|
||||
alternativeTarget,
|
||||
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
||||
method,
|
||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||
java.util.Collections.emptyList(),
|
||||
alternativeTarget,
|
||||
alternativeTarget,
|
||||
SelectionCriteria.forFactoryMethods( selectionParameters )
|
||||
);
|
||||
}
|
||||
|
||||
public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) {
|
||||
|
@ -12,15 +12,18 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
@ -103,6 +106,8 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
|
||||
private Type boundingBase = null;
|
||||
|
||||
private Type boxedEquivalent = null;
|
||||
|
||||
private Boolean hasAccessibleConstructor;
|
||||
|
||||
private final Filters filters;
|
||||
@ -311,7 +316,12 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return isStream;
|
||||
}
|
||||
|
||||
public boolean isWildCardSuperBound() {
|
||||
/**
|
||||
* A wild card type can have two types of bounds (mutual exclusive): extends and super.
|
||||
*
|
||||
* @return true if the bound has a wild card super bound (e.g. ? super Number)
|
||||
*/
|
||||
public boolean hasSuperBound() {
|
||||
boolean result = false;
|
||||
if ( typeMirror.getKind() == TypeKind.WILDCARD ) {
|
||||
WildcardType wildcardType = (WildcardType) typeMirror;
|
||||
@ -320,7 +330,12 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isWildCardExtendsBound() {
|
||||
/**
|
||||
* A wild card type can have two types of bounds (mutual exclusive): extends and super.
|
||||
*
|
||||
* @return true if the bound has a wild card super bound (e.g. ? extends Number)
|
||||
*/
|
||||
public boolean hasExtendsBound() {
|
||||
boolean result = false;
|
||||
if ( typeMirror.getKind() == TypeKind.WILDCARD ) {
|
||||
WildcardType wildcardType = (WildcardType) typeMirror;
|
||||
@ -329,6 +344,40 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type variable type can have two types of bounds (mutual exclusive): lower and upper.
|
||||
*
|
||||
* Note that its use is only permitted on a definition (not on the place where its used). For instance:
|
||||
* {@code<T super Number> T map( T in)}
|
||||
*
|
||||
* @return true if the bound has a type variable lower bound (e.g. T super Number)
|
||||
*/
|
||||
public boolean hasLowerBound() {
|
||||
boolean result = false;
|
||||
if ( typeMirror.getKind() == TypeKind.TYPEVAR ) {
|
||||
TypeVariable typeVarType = (TypeVariable) typeMirror;
|
||||
result = typeVarType.getLowerBound() != null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type variable type can have two types of bounds (mutual exclusive): lower and upper.
|
||||
*
|
||||
* Note that its use is only permitted on a definition (not on the place where its used). For instance:
|
||||
* {@code><T extends Number> T map( T in)}
|
||||
*
|
||||
* @return true if the bound has a type variable upper bound (e.g. T extends Number)
|
||||
*/
|
||||
public boolean hasUpperBound() {
|
||||
boolean result = false;
|
||||
if ( typeMirror.getKind() == TypeKind.TYPEVAR ) {
|
||||
TypeVariable typeVarType = (TypeVariable) typeMirror;
|
||||
result = typeVarType.getUpperBound() != null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getFullyQualifiedName() {
|
||||
return qualifiedName;
|
||||
}
|
||||
@ -356,7 +405,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
result.addAll( parameter.getImportTypes() );
|
||||
}
|
||||
|
||||
if ( ( isWildCardExtendsBound() || isWildCardSuperBound() ) && getTypeBound() != null ) {
|
||||
if ( ( hasExtendsBound() || hasSuperBound() ) && getTypeBound() != null ) {
|
||||
result.addAll( getTypeBound().getImportTypes() );
|
||||
}
|
||||
|
||||
@ -470,22 +519,19 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this type is assignable to the given other type.
|
||||
* Whether this type is assignable to the given other type, considering the "extends / upper bounds"
|
||||
* as well.
|
||||
*
|
||||
* @param other The other type.
|
||||
*
|
||||
* @return {@code true} if and only if this type is assignable to the given other type.
|
||||
*/
|
||||
// TODO This doesn't yet take super wild card types into account;
|
||||
// e.g. Number wouldn't be assignable to ? super Number atm. (is there any practical use case)
|
||||
public boolean isAssignableTo(Type other) {
|
||||
if ( equals( other ) ) {
|
||||
return true;
|
||||
if ( TypeKind.WILDCARD == typeMirror.getKind() ) {
|
||||
return typeUtils.contains( typeMirror, other.typeMirror );
|
||||
}
|
||||
|
||||
TypeMirror typeMirrorToMatch = isWildCardExtendsBound() ? getTypeBound().typeMirror : typeMirror;
|
||||
|
||||
return typeUtils.isAssignable( typeMirrorToMatch, other.typeMirror );
|
||||
return typeUtils.isAssignable( typeMirror, other.typeMirror );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -506,6 +552,19 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return typeUtils.isAssignable( typeUtils.erasure( typeMirror ), typeUtils.erasure( other.typeMirror ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* removes any bounds from this type.
|
||||
* @return the raw type
|
||||
*/
|
||||
public Type asRawType() {
|
||||
if ( getTypeBound() != null ) {
|
||||
return typeFactory.getType( typeUtils.erasure( typeMirror ) );
|
||||
}
|
||||
else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* getPropertyReadAccessors
|
||||
*
|
||||
@ -1004,7 +1063,14 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
}
|
||||
Type other = (Type) obj;
|
||||
|
||||
return typeUtils.isSameType( typeMirror, other.typeMirror );
|
||||
if ( this.isWildCardBoundByTypeVar() && other.isWildCardBoundByTypeVar() ) {
|
||||
return ( this.hasExtendsBound() == this.hasExtendsBound()
|
||||
|| this.hasSuperBound() == this.hasSuperBound() )
|
||||
&& typeUtils.isSameType( getTypeBound().getTypeMirror(), other.getTypeBound().getTypeMirror() );
|
||||
}
|
||||
else {
|
||||
return typeUtils.isSameType( typeMirror, other.typeMirror );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1085,6 +1151,19 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return hasAccessibleConstructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the direct supertypes of a type. The interface types, if any,
|
||||
* will appear last in the list.
|
||||
*
|
||||
* @return the direct supertypes, or an empty list if none
|
||||
*/
|
||||
public List<Type> getDirectSuperTypes() {
|
||||
return typeUtils.directSupertypes( typeMirror )
|
||||
.stream()
|
||||
.map( typeFactory::getType )
|
||||
.collect( Collectors.toList() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the given superclass and collects all type arguments for the given class
|
||||
*
|
||||
@ -1121,59 +1200,239 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps through the declaredType in order to find a match for this typevar Type. It allignes with
|
||||
* Steps through the declaredType in order to find a match for this typeVar Type. It aligns with
|
||||
* the provided parameterized type where this typeVar type is used.
|
||||
*
|
||||
* @param declaredType the type
|
||||
* @param parameterizedType the parameterized type
|
||||
* For example:
|
||||
* {@code
|
||||
* this: T
|
||||
* declaredType: JAXBElement<String>
|
||||
* parameterizedType: JAXBElement<T>
|
||||
* result: String
|
||||
*
|
||||
* @return the matching declared type.
|
||||
*
|
||||
* this: T, T[] or ? extends T,
|
||||
* declaredType: E.g. Callable<? extends T>
|
||||
* parameterizedType: Callable<BigDecimal>
|
||||
* return: BigDecimal
|
||||
* }
|
||||
*
|
||||
* @param declared the type
|
||||
* @param parameterized the parameterized type
|
||||
*
|
||||
* @return - the same type when this is not a type var in the broadest sense (T, T[], or ? extends T)
|
||||
* - the matching parameter in the parameterized type when this is a type var when found
|
||||
* - null in all other cases
|
||||
*/
|
||||
public Type resolveTypeVarToType(Type declaredType, Type parameterizedType) {
|
||||
if ( isTypeVar() ) {
|
||||
TypeVarMatcher typeVarMatcher = new TypeVarMatcher( typeUtils, this );
|
||||
return typeVarMatcher.visit( parameterizedType.getTypeMirror(), declaredType );
|
||||
public ResolvedPair resolveParameterToType(Type declared, Type parameterized) {
|
||||
if ( isTypeVar() || isArrayTypeVar() || isWildCardBoundByTypeVar() ) {
|
||||
TypeVarMatcher typeVarMatcher = new TypeVarMatcher( typeFactory, typeUtils, this );
|
||||
return typeVarMatcher.visit( parameterized.getTypeMirror(), declared );
|
||||
}
|
||||
return this;
|
||||
return new ResolvedPair( this, this );
|
||||
}
|
||||
|
||||
private static class TypeVarMatcher extends SimpleTypeVisitor8<Type, Type> {
|
||||
public boolean isWildCardBoundByTypeVar() {
|
||||
return ( hasExtendsBound() || hasSuperBound() ) && getTypeBound().isTypeVar();
|
||||
}
|
||||
|
||||
private TypeVariable typeVarToMatch;
|
||||
private TypeUtils types;
|
||||
public boolean isArrayTypeVar() {
|
||||
return isArrayType() && getComponentType().isTypeVar();
|
||||
}
|
||||
|
||||
TypeVarMatcher(TypeUtils types, Type typeVarToMatch ) {
|
||||
super( null );
|
||||
this.typeVarToMatch = (TypeVariable) typeVarToMatch.getTypeMirror();
|
||||
private static class TypeVarMatcher extends SimpleTypeVisitor8<ResolvedPair, Type> {
|
||||
|
||||
private final TypeFactory typeFactory;
|
||||
private final Type typeToMatch;
|
||||
private final TypeUtils types;
|
||||
|
||||
/**
|
||||
* @param typeFactory factory
|
||||
* @param types type utils
|
||||
* @param typeToMatch the typeVar or wildcard with typeVar bound
|
||||
*/
|
||||
TypeVarMatcher(TypeFactory typeFactory, TypeUtils types, Type typeToMatch) {
|
||||
super( new ResolvedPair( typeToMatch, null ) );
|
||||
this.typeFactory = typeFactory;
|
||||
this.typeToMatch = typeToMatch;
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type visitTypeVariable(TypeVariable t, Type parameterized) {
|
||||
if ( types.isSameType( t, typeVarToMatch ) ) {
|
||||
return parameterized;
|
||||
public ResolvedPair visitTypeVariable(TypeVariable parameterized, Type declared) {
|
||||
if ( typeToMatch.isTypeVar() && types.isSameType( parameterized, typeToMatch.getTypeMirror() ) ) {
|
||||
return new ResolvedPair( typeFactory.getType( parameterized ), declared );
|
||||
}
|
||||
return super.visitTypeVariable( t, parameterized );
|
||||
return super.DEFAULT_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If ? extends SomeTime equals the boundary set in typeVarToMatch (NOTE: you can't compare the wildcard itself)
|
||||
* then return a result;
|
||||
*/
|
||||
@Override
|
||||
public ResolvedPair visitWildcard(WildcardType parameterized, Type declared) {
|
||||
if ( typeToMatch.hasExtendsBound() && parameterized.getExtendsBound() != null
|
||||
&& types.isSameType( typeToMatch.getTypeBound().getTypeMirror(), parameterized.getExtendsBound() ) ) {
|
||||
return new ResolvedPair( typeToMatch, declared);
|
||||
}
|
||||
else if ( typeToMatch.hasSuperBound() && parameterized.getSuperBound() != null
|
||||
&& types.isSameType( typeToMatch.getTypeBound().getTypeMirror(), parameterized.getSuperBound() ) ) {
|
||||
return new ResolvedPair( typeToMatch, declared);
|
||||
}
|
||||
if ( parameterized.getExtendsBound() != null ) {
|
||||
ResolvedPair match = visit( parameterized.getExtendsBound(), declared );
|
||||
if ( match.match != null ) {
|
||||
return new ResolvedPair( typeFactory.getType( parameterized ), declared );
|
||||
}
|
||||
}
|
||||
else if (parameterized.getSuperBound() != null ) {
|
||||
ResolvedPair match = visit( parameterized.getSuperBound(), declared );
|
||||
if ( match.match != null ) {
|
||||
return new ResolvedPair( typeFactory.getType( parameterized ), declared );
|
||||
}
|
||||
|
||||
}
|
||||
return super.DEFAULT_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type visitDeclared(DeclaredType t, Type parameterized) {
|
||||
if ( types.isAssignable( types.erasure( t ), types.erasure( parameterized.getTypeMirror() ) ) ) {
|
||||
public ResolvedPair visitArray(ArrayType parameterized, Type declared) {
|
||||
if ( types.isSameType( parameterized.getComponentType(), typeToMatch.getTypeMirror() ) ) {
|
||||
return new ResolvedPair( typeFactory.getType( parameterized ), declared );
|
||||
}
|
||||
if ( declared.isArrayType() ) {
|
||||
return visit( parameterized.getComponentType(), declared.getComponentType() );
|
||||
}
|
||||
return super.DEFAULT_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolvedPair visitDeclared(DeclaredType parameterized, Type declared) {
|
||||
|
||||
List<ResolvedPair> results = new ArrayList<>( );
|
||||
if ( parameterized.getTypeArguments().isEmpty() ) {
|
||||
return super.DEFAULT_VALUE;
|
||||
}
|
||||
else if ( types.isSameType( types.erasure( parameterized ), types.erasure( declared.getTypeMirror() ) ) ) {
|
||||
// We can't assume that the type args are the same
|
||||
// e.g. List<T> is assignable to Object
|
||||
if ( t.getTypeArguments().size() != parameterized.getTypeParameters().size() ) {
|
||||
return super.visitDeclared( t, parameterized );
|
||||
if ( parameterized.getTypeArguments().size() != declared.getTypeParameters().size() ) {
|
||||
return super.visitDeclared( parameterized, declared );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < t.getTypeArguments().size(); i++ ) {
|
||||
Type result = visit( t.getTypeArguments().get( i ), parameterized.getTypeParameters().get( i ) );
|
||||
if ( result != null ) {
|
||||
return result;
|
||||
// only possible to compare parameters when the types are exactly the same
|
||||
for ( int i = 0; i < parameterized.getTypeArguments().size(); i++ ) {
|
||||
TypeMirror parameterizedTypeArg = parameterized.getTypeArguments().get( i );
|
||||
Type declaredTypeArg = declared.getTypeParameters().get( i );
|
||||
ResolvedPair result = visit( parameterizedTypeArg, declaredTypeArg );
|
||||
if ( result != super.DEFAULT_VALUE ) {
|
||||
results.add( result );
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitDeclared( t, parameterized );
|
||||
else {
|
||||
// Also check whether the implemented interfaces are parameterized
|
||||
for ( Type declaredSuperType : declared.getDirectSuperTypes() ) {
|
||||
if ( Object.class.getName().equals( declaredSuperType.getFullyQualifiedName() ) ) {
|
||||
continue;
|
||||
}
|
||||
ResolvedPair result = visitDeclared( parameterized, declaredSuperType );
|
||||
if ( result != super.DEFAULT_VALUE ) {
|
||||
results.add( result );
|
||||
}
|
||||
}
|
||||
|
||||
for ( TypeMirror parameterizedSuper : types.directSupertypes( parameterized ) ) {
|
||||
if ( isJavaLangObject( parameterizedSuper ) ) {
|
||||
continue;
|
||||
}
|
||||
ResolvedPair result = visitDeclared( (DeclaredType) parameterizedSuper, declared );
|
||||
if ( result != super.DEFAULT_VALUE ) {
|
||||
results.add( result );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( results.isEmpty() ) {
|
||||
return super.DEFAULT_VALUE;
|
||||
}
|
||||
else {
|
||||
return results.stream().allMatch( results.get( 0 )::equals ) ? results.get( 0 ) : super.DEFAULT_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isJavaLangObject(TypeMirror type) {
|
||||
if ( type instanceof DeclaredType ) {
|
||||
return ( (TypeElement) ( (DeclaredType) type ).asElement() ).getQualifiedName()
|
||||
.contentEquals( Object.class.getName() );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflects any Resolved Pair, examples are
|
||||
* T, String
|
||||
* ? extends T, BigDecimal
|
||||
* T[], Integer[]
|
||||
*/
|
||||
public static class ResolvedPair {
|
||||
|
||||
public ResolvedPair(Type parameter, Type match) {
|
||||
this.parameter = parameter;
|
||||
this.match = match;
|
||||
}
|
||||
|
||||
/**
|
||||
* parameter, e.g. T, ? extends T or T[]
|
||||
*/
|
||||
private Type parameter;
|
||||
|
||||
/**
|
||||
* match, e.g. String, BigDecimal, Integer[]
|
||||
*/
|
||||
private Type match;
|
||||
|
||||
public Type getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public Type getMatch() {
|
||||
return match;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
ResolvedPair that = (ResolvedPair) o;
|
||||
return Objects.equals( parameter, that.parameter ) && Objects.equals( match, that.match );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( parameter );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the boxed equivalent type if the type is primitive, int will return Integer
|
||||
*
|
||||
* @return boxed equivalent
|
||||
*/
|
||||
public Type getBoxedEquivalent() {
|
||||
if ( boxedEquivalent != null ) {
|
||||
return boxedEquivalent;
|
||||
}
|
||||
else if ( isPrimitive() ) {
|
||||
boxedEquivalent = typeFactory.getType( typeUtils.boxedClass( (PrimitiveType) typeMirror ) );
|
||||
return boxedEquivalent;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -613,7 +613,11 @@ public class TypeFactory {
|
||||
if ( typeVariableType.getUpperBound() != null ) {
|
||||
return typeVariableType.getUpperBound();
|
||||
}
|
||||
// Lowerbounds intentionally left out: Type variables otherwise have a lower bound of NullType.
|
||||
// lower bounds ( T super Number ) cannot be used for argument parameters, but can be used for
|
||||
// method parameters: e.g. <T super Number> T map (T in);
|
||||
if ( typeVariableType.getLowerBound() != null ) {
|
||||
return typeVariableType.getLowerBound();
|
||||
}
|
||||
}
|
||||
|
||||
return typeMirror;
|
||||
|
@ -199,4 +199,11 @@ public interface Method {
|
||||
* @return the short name for error messages when verbose, full name when not
|
||||
*/
|
||||
String describe();
|
||||
|
||||
/**
|
||||
* Returns the formal type parameters of this method in declaration order.
|
||||
*
|
||||
* @return the formal type parameters, or an empty list if there are none
|
||||
*/
|
||||
List<Type> getTypeParameters();
|
||||
}
|
||||
|
@ -5,25 +5,19 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.source;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.TypeParameterElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.type.TypeVariable;
|
||||
import javax.lang.model.type.WildcardType;
|
||||
import javax.lang.model.util.SimpleTypeVisitor6;
|
||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||
|
||||
/**
|
||||
* SourceMethodMatcher $8.4 of the JavaLanguage specification describes a method body as such:
|
||||
@ -66,362 +60,348 @@ public class MethodMatcher {
|
||||
* Whether the given source and target types are matched by this matcher's candidate method.
|
||||
*
|
||||
* @param sourceTypes the source types
|
||||
* @param resultType the target type
|
||||
* @param targetType the target type
|
||||
* @return {@code true} when both, source type and target types match the signature of this matcher's method;
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
boolean matches(List<Type> sourceTypes, Type resultType) {
|
||||
boolean matches(List<Type> sourceTypes, Type targetType) {
|
||||
|
||||
// check & collect generic types.
|
||||
Map<TypeVariable, TypeMirror> genericTypesMap = new HashMap<>();
|
||||
|
||||
if ( candidateMethod.getParameters().size() == sourceTypes.size() ) {
|
||||
int i = 0;
|
||||
for ( Parameter candidateParam : candidateMethod.getParameters() ) {
|
||||
Type sourceType = sourceTypes.get( i++ );
|
||||
if ( sourceType == null
|
||||
|| !matchSourceType( sourceType, candidateParam.getType(), genericTypesMap ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GenericAnalyser analyser =
|
||||
new GenericAnalyser( typeFactory, typeUtils, candidateMethod, sourceTypes, targetType );
|
||||
if ( !analyser.lineUp() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the method matches the proper result type to construct
|
||||
Parameter targetTypeParameter = candidateMethod.getTargetTypeParameter();
|
||||
if ( targetTypeParameter != null ) {
|
||||
Type returnClassType = typeFactory.classTypeOf( resultType );
|
||||
if ( !matchSourceType( returnClassType, targetTypeParameter.getType(), genericTypesMap ) ) {
|
||||
for ( int i = 0; i < sourceTypes.size(); i++ ) {
|
||||
Type candidateSourceParType = analyser.candidateParTypes.get( i );
|
||||
if ( !sourceTypes.get( i ).isAssignableTo( candidateSourceParType )
|
||||
|| isPrimitiveToObject( sourceTypes.get( i ), candidateSourceParType ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO: TargetType checking should not be part of method selection, it should be in checking the annotation
|
||||
// (the relation target / target type, target type being a class)
|
||||
|
||||
if ( !analyser.candidateReturnType.isVoid() ) {
|
||||
if ( !( analyser.candidateReturnType.isAssignableTo( targetType ) ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check result type
|
||||
if ( !matchResultType( resultType, genericTypesMap ) ) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primitive to Object (Boxed Type) should be handled by a type conversion rather than a direct mapping
|
||||
* Direct mapping runs the risk of null pointer exceptions: e.g. in the assignment of Integer to int, Integer
|
||||
* can be null.
|
||||
*
|
||||
* @param type the type to be assigned
|
||||
* @param isAssignableTo the type to which @param type should be assignable to
|
||||
* @return true if isAssignable is a primitive type and type is an object
|
||||
*/
|
||||
private boolean isPrimitiveToObject( Type type, Type isAssignableTo ) {
|
||||
if ( isAssignableTo.isPrimitive() ) {
|
||||
return !type.isPrimitive();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class GenericAnalyser {
|
||||
|
||||
private TypeFactory typeFactory;
|
||||
private TypeUtils typeUtils;
|
||||
private Method candidateMethod;
|
||||
private List<Type> sourceTypes;
|
||||
private Type targetType;
|
||||
|
||||
GenericAnalyser(TypeFactory typeFactory, TypeUtils typeUtils, Method candidateMethod,
|
||||
List<Type> sourceTypes, Type targetType) {
|
||||
this.typeFactory = typeFactory;
|
||||
this.typeUtils = typeUtils;
|
||||
this.candidateMethod = candidateMethod;
|
||||
this.sourceTypes = sourceTypes;
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
// check if all type parameters are indeed mapped
|
||||
if ( candidateMethod.getExecutable().getTypeParameters().size() != genericTypesMap.size() ) {
|
||||
return false;
|
||||
}
|
||||
Type candidateReturnType = null;
|
||||
List<Type> candidateParTypes;
|
||||
|
||||
// check if all entries are in the bounds
|
||||
for ( Map.Entry<TypeVariable, TypeMirror> entry : genericTypesMap.entrySet() ) {
|
||||
if ( !isWithinBounds( entry.getValue(), getTypeParamFromCandidate( entry.getKey() ) ) ) {
|
||||
// checks if the found Type is in bounds of the TypeParameters bounds.
|
||||
private boolean lineUp() {
|
||||
|
||||
if ( candidateMethod.getParameters().size() != sourceTypes.size() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean matchSourceType(Type sourceType,
|
||||
Type candidateSourceType,
|
||||
Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
if ( !candidateMethod.getTypeParameters().isEmpty() ) {
|
||||
|
||||
if ( !isJavaLangObject( candidateSourceType.getTypeMirror() ) ) {
|
||||
TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM, genericTypesMap );
|
||||
if ( !parameterMatcher.visit( candidateSourceType.getTypeMirror(), sourceType.getTypeMirror() ) ) {
|
||||
if ( sourceType.isPrimitive() ) {
|
||||
// the candidate source is primitive, so promote to its boxed type and check again (autobox)
|
||||
TypeMirror boxedType = typeUtils.boxedClass( (PrimitiveType) sourceType.getTypeMirror() ).asType();
|
||||
if ( !parameterMatcher.visit( candidateSourceType.getTypeMirror(), boxedType ) ) {
|
||||
this.candidateParTypes = new ArrayList<>();
|
||||
|
||||
// Per generic method parameter the associated type variable candidates
|
||||
Map<Type, TypeVarCandidate> methodParCandidates = new HashMap<>();
|
||||
|
||||
// Get candidates
|
||||
boolean success = getCandidates( methodParCandidates );
|
||||
if ( !success ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check type bounds
|
||||
boolean withinBounds = candidatesWithinBounds( methodParCandidates );
|
||||
if ( !withinBounds ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Represent result as map.
|
||||
Map<Type, Type> resolvedPairs = new HashMap<>();
|
||||
for ( TypeVarCandidate candidate : methodParCandidates.values() ) {
|
||||
for ( Type.ResolvedPair pair : candidate.pairs) {
|
||||
resolvedPairs.put( pair.getParameter(), pair.getMatch() );
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve parameters and return type by using the found candidates
|
||||
int nrOfMethodPars = candidateMethod.getParameters().size();
|
||||
for ( int i = 0; i < nrOfMethodPars; i++ ) {
|
||||
Type candidateType = resolve( candidateMethod.getParameters().get( i ).getType(), resolvedPairs );
|
||||
if ( candidateType == null ) {
|
||||
return false;
|
||||
}
|
||||
this.candidateParTypes.add( candidateType );
|
||||
|
||||
}
|
||||
if ( !candidateMethod.getReturnType().isVoid() ) {
|
||||
this.candidateReturnType = resolve( candidateMethod.getReturnType(), resolvedPairs );
|
||||
if ( this.candidateReturnType == null ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// NOTE: unboxing is deliberately not considered here. This should be handled via type-conversion
|
||||
// (for NPE safety).
|
||||
this.candidateReturnType = candidateMethod.getReturnType();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.candidateParTypes = candidateMethod.getParameters().stream()
|
||||
.map( Parameter::getType )
|
||||
.collect( Collectors.toList() );
|
||||
this.candidateReturnType = candidateMethod.getReturnType();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code <T, U extends Number> T map( U in ) }
|
||||
*
|
||||
* Resolves all method generic parameter candidates
|
||||
*
|
||||
* @param methodParCandidates Map, keyed by the method generic parameter (T, U extends Number), with their
|
||||
* respective candidates
|
||||
*
|
||||
* @return false no match or conflict has been found *
|
||||
*/
|
||||
boolean getCandidates( Map<Type, TypeVarCandidate> methodParCandidates) {
|
||||
|
||||
int nrOfMethodPars = candidateMethod.getParameters().size();
|
||||
Type returnType = candidateMethod.getReturnType();
|
||||
|
||||
for ( int i = 0; i < nrOfMethodPars; i++ ) {
|
||||
Type sourceType = sourceTypes.get( i );
|
||||
Parameter par = candidateMethod.getParameters().get( i );
|
||||
Type parType = par.getType();
|
||||
boolean success = getCandidates( parType, sourceType, methodParCandidates );
|
||||
if ( !success ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean matchResultType(Type resultType, Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
|
||||
Type candidateResultType = candidateMethod.getResultType();
|
||||
|
||||
if ( !isJavaLangObject( candidateResultType.getTypeMirror() ) && !candidateResultType.isVoid() ) {
|
||||
|
||||
final Assignability visitedAssignability;
|
||||
if ( candidateMethod.getReturnType().isVoid() ) {
|
||||
// for void-methods, the result-type of the candidate needs to be assignable from the given result type
|
||||
visitedAssignability = Assignability.VISITED_ASSIGNABLE_FROM;
|
||||
}
|
||||
else {
|
||||
// for non-void methods, the result-type of the candidate needs to be assignable to the given result
|
||||
// type
|
||||
visitedAssignability = Assignability.VISITED_ASSIGNABLE_TO;
|
||||
}
|
||||
|
||||
TypeMatcher returnTypeMatcher = new TypeMatcher( visitedAssignability, genericTypesMap );
|
||||
if ( !returnTypeMatcher.visit( candidateResultType.getTypeMirror(), resultType.getTypeMirror() ) ) {
|
||||
if ( resultType.isPrimitive() ) {
|
||||
TypeMirror boxedType = typeUtils.boxedClass( (PrimitiveType) resultType.getTypeMirror() ).asType();
|
||||
TypeMatcher boxedReturnTypeMatcher =
|
||||
new TypeMatcher( visitedAssignability, genericTypesMap );
|
||||
|
||||
if ( !boxedReturnTypeMatcher.visit( candidateResultType.getTypeMirror(), boxedType ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( candidateResultType.getTypeMirror().getKind().isPrimitive() ) {
|
||||
TypeMirror boxedCandidateReturnType =
|
||||
typeUtils.boxedClass( (PrimitiveType) candidateResultType.getTypeMirror() ).asType();
|
||||
TypeMatcher boxedReturnTypeMatcher =
|
||||
new TypeMatcher( visitedAssignability, genericTypesMap );
|
||||
|
||||
if ( !boxedReturnTypeMatcher.visit( boxedCandidateReturnType, resultType.getTypeMirror() ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the type
|
||||
* @return {@code true}, if the type represents java.lang.Object
|
||||
*/
|
||||
private boolean isJavaLangObject(TypeMirror type) {
|
||||
return type.getKind() == TypeKind.DECLARED
|
||||
&& ( (TypeElement) ( (DeclaredType) type ).asElement() ).getQualifiedName().contentEquals(
|
||||
Object.class.getName() );
|
||||
}
|
||||
|
||||
private enum Assignability {
|
||||
VISITED_ASSIGNABLE_FROM, VISITED_ASSIGNABLE_TO;
|
||||
|
||||
Assignability invert() {
|
||||
return this == VISITED_ASSIGNABLE_FROM
|
||||
? VISITED_ASSIGNABLE_TO
|
||||
: VISITED_ASSIGNABLE_FROM;
|
||||
}
|
||||
}
|
||||
|
||||
private class TypeMatcher extends SimpleTypeVisitor6<Boolean, TypeMirror> {
|
||||
private final Assignability assignability;
|
||||
private final Map<TypeVariable, TypeMirror> genericTypesMap;
|
||||
private final TypeMatcher inverse;
|
||||
|
||||
TypeMatcher(Assignability assignability, Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
super( Boolean.FALSE ); // default value
|
||||
this.assignability = assignability;
|
||||
this.genericTypesMap = genericTypesMap;
|
||||
this.inverse = new TypeMatcher( this, genericTypesMap );
|
||||
}
|
||||
|
||||
TypeMatcher(TypeMatcher inverse, Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
super( Boolean.FALSE ); // default value
|
||||
this.assignability = inverse.assignability.invert();
|
||||
this.genericTypesMap = genericTypesMap;
|
||||
this.inverse = inverse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPrimitive(PrimitiveType t, TypeMirror p) {
|
||||
return typeUtils.isSameType( t, p );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitArray(ArrayType t, TypeMirror p) {
|
||||
|
||||
if ( p.getKind().equals( TypeKind.ARRAY ) ) {
|
||||
return t.getComponentType().accept( this, ( (ArrayType) p ).getComponentType() );
|
||||
}
|
||||
else {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitDeclared(DeclaredType t, TypeMirror p) {
|
||||
// its a match when: 1) same kind of type, name is equals, nr of type args are the same
|
||||
// (type args are checked later).
|
||||
if ( p.getKind() == TypeKind.DECLARED ) {
|
||||
DeclaredType t1 = (DeclaredType) p;
|
||||
if ( rawAssignabilityMatches( t, t1 ) ) {
|
||||
if ( t.getTypeArguments().size() == t1.getTypeArguments().size() ) {
|
||||
// compare type var side by side
|
||||
for ( int i = 0; i < t.getTypeArguments().size(); i++ ) {
|
||||
if ( !visit( t.getTypeArguments().get( i ), t1.getTypeArguments().get( i ) ) ) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
else {
|
||||
// return true (e.g. matching Enumeration<E> with an enumeration E)
|
||||
// but do not try to line up raw type arguments with types that do have arguments.
|
||||
return assignability == Assignability.VISITED_ASSIGNABLE_TO ?
|
||||
!t1.getTypeArguments().isEmpty() : !t.getTypeArguments().isEmpty();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
else if ( p.getKind() == TypeKind.WILDCARD ) {
|
||||
return inverse.visit( p, t ); // inverse, as we switch the params
|
||||
}
|
||||
else {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean rawAssignabilityMatches(DeclaredType t1, DeclaredType t2) {
|
||||
if ( assignability == Assignability.VISITED_ASSIGNABLE_TO ) {
|
||||
return typeUtils.isAssignable( toRawType( t1 ), toRawType( t2 ) );
|
||||
}
|
||||
else {
|
||||
return typeUtils.isAssignable( toRawType( t2 ), toRawType( t1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
private DeclaredType toRawType(DeclaredType t) {
|
||||
return typeUtils.getDeclaredType( (TypeElement) t.asElement() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitTypeVariable(TypeVariable t, TypeMirror p) {
|
||||
if ( genericTypesMap.containsKey( t ) ) {
|
||||
// when already found, the same mapping should apply
|
||||
// Then we should visit the resolved generic type.
|
||||
// Which can potentially be another generic type
|
||||
// e.g.
|
||||
// <T> T fromOptional(Optional<T> optional)
|
||||
// T resolves to Collection<Integer>
|
||||
// We know what T resolves to, so we should treat it as if the method signature was
|
||||
// Collection<Integer> fromOptional(Optional<Collection<Integer> optional)
|
||||
TypeMirror p1 = genericTypesMap.get( t );
|
||||
// p (Integer) should be a subType of p1 (Number)
|
||||
// i.e. you can assign p (Integer) to p1 (Number)
|
||||
return visit( p, p1 );
|
||||
}
|
||||
else {
|
||||
// check if types are in bound
|
||||
TypeMirror lowerBound = t.getLowerBound();
|
||||
TypeMirror upperBound = t.getUpperBound();
|
||||
if ( ( isNullType( lowerBound ) || typeUtils.isSubtypeErased( lowerBound, p ) )
|
||||
&& ( isNullType( upperBound ) || typeUtils.isSubtypeErased( p, upperBound ) ) ) {
|
||||
genericTypesMap.put( t, p );
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
else {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNullType(TypeMirror type) {
|
||||
return type == null || type.getKind() == TypeKind.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitWildcard(WildcardType t, TypeMirror p) {
|
||||
|
||||
// check extends bound
|
||||
TypeMirror extendsBound = t.getExtendsBound();
|
||||
if ( !isNullType( extendsBound ) ) {
|
||||
switch ( extendsBound.getKind() ) {
|
||||
case DECLARED:
|
||||
// for example method: String method(? extends String)
|
||||
// isSubType checks range [subtype, type], e.g. isSubtype [Object, String]==true
|
||||
return visit( extendsBound, p );
|
||||
|
||||
case TYPEVAR:
|
||||
// for example method: <T extends String & Serializable> T method(? extends T)
|
||||
// this can be done the directly by checking: ? extends String & Serializable
|
||||
// this checks the part? <T extends String & Serializable>
|
||||
return isWithinBounds( p, getTypeParamFromCandidate( extendsBound ) );
|
||||
|
||||
default:
|
||||
// does this situation occur?
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// check super bound
|
||||
TypeMirror superBound = t.getSuperBound();
|
||||
if ( !isNullType( superBound ) ) {
|
||||
switch ( superBound.getKind() ) {
|
||||
case DECLARED:
|
||||
// for example method: String method(? super String)
|
||||
// to check super type, we can simply inverse the argument, but that would initially yield
|
||||
// a result: <type, superType] (so type not included) so we need to check sameType also.
|
||||
return typeUtils.isSubtypeErased( superBound, p ) || typeUtils.isSameType( p, superBound );
|
||||
|
||||
case TYPEVAR:
|
||||
|
||||
TypeParameterElement typeParameter = getTypeParamFromCandidate( superBound );
|
||||
// for example method: <T extends String & Serializable> T method(? super T)
|
||||
if ( !isWithinBounds( p, typeParameter ) ) {
|
||||
// this checks the part? <T extends String & Serializable>
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
// now, it becomes a bit more hairy. We have the relation (? super T). From T we know that
|
||||
// it is a subclass of String & Serializable. However, The Java Language Secification,
|
||||
// Chapter 4.4, states that a bound is either: 'A type variable-', 'A class-' or 'An
|
||||
// interface-' type followed by further interface types. So we must compare with the first
|
||||
// argument in the Expression String & Serializable & ..., so, in this case String.
|
||||
// to check super type, we can simply inverse the argument, but that would initially yield
|
||||
// a result: <type, superType] (so type not included) so we need to check sameType also.
|
||||
TypeMirror superBoundAsDeclared = typeParameter.getBounds().get( 0 );
|
||||
return ( typeUtils.isSubtypeErased( superBoundAsDeclared, p ) || typeUtils.isSameType(
|
||||
p,
|
||||
superBoundAsDeclared ) );
|
||||
default:
|
||||
// does this situation occur?
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks through the list of type parameters of the candidate method for a match
|
||||
*
|
||||
* @param t type parameter to match
|
||||
*
|
||||
* @return matching type parameter
|
||||
*/
|
||||
private TypeParameterElement getTypeParamFromCandidate(TypeMirror t) {
|
||||
for ( TypeParameterElement candidateTypeParam : candidateMethod.getExecutable().getTypeParameters() ) {
|
||||
if ( typeUtils.isSameType( candidateTypeParam.asType(), t ) ) {
|
||||
return candidateTypeParam;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether a type t is in bounds of the typeParameter tpe
|
||||
*
|
||||
* @return true if within bounds
|
||||
*/
|
||||
private boolean isWithinBounds(TypeMirror t, TypeParameterElement tpe) {
|
||||
List<? extends TypeMirror> bounds = tpe != null ? tpe.getBounds() : null;
|
||||
if ( t != null && bounds != null ) {
|
||||
for ( TypeMirror bound : bounds ) {
|
||||
if ( !( bound.getKind() == TypeKind.DECLARED && typeUtils.isSubtypeErased( t, bound ) ) ) {
|
||||
if ( !returnType.isVoid() ) {
|
||||
boolean success = getCandidates( returnType, targetType, methodParCandidates );
|
||||
if ( !success ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
/**
|
||||
* @param aCandidateMethodType parameter type or return type from candidate method
|
||||
* @param matchingType source type / target type to match
|
||||
* @param candidates Map, keyed by the method generic parameter, with the candidates
|
||||
*
|
||||
* @return false no match or conflict has been found
|
||||
*/
|
||||
boolean getCandidates(Type aCandidateMethodType, Type matchingType, Map<Type, TypeVarCandidate> candidates ) {
|
||||
|
||||
if ( !( aCandidateMethodType.isTypeVar()
|
||||
|| aCandidateMethodType.isArrayTypeVar()
|
||||
|| aCandidateMethodType.isWildCardBoundByTypeVar()
|
||||
|| hasGenericTypeParameters( aCandidateMethodType ) ) ) {
|
||||
// the typeFromCandidateMethod is not a generic (parameterized) type
|
||||
return true;
|
||||
}
|
||||
|
||||
for ( Type mthdParType : candidateMethod.getTypeParameters() ) {
|
||||
|
||||
// typeFromCandidateMethod itself is a generic type, e.g. <T> String method( T par );
|
||||
// typeFromCandidateMethod is a generic arrayType e.g. <T> String method( T[] par );
|
||||
// typeFromCandidateMethod is embedded in another type e.g. <T> String method( Callable<T> par );
|
||||
// typeFromCandidateMethod is a wildcard, bounded by a typeVar
|
||||
// e.g. <T> String method( List<? extends T> in )
|
||||
|
||||
Type.ResolvedPair resolved = mthdParType.resolveParameterToType( matchingType, aCandidateMethodType );
|
||||
if ( resolved == null ) {
|
||||
// cannot find a candidate type, but should have since the typeFromCandidateMethod had parameters
|
||||
// to be resolved
|
||||
return !hasGenericTypeParameters( aCandidateMethodType );
|
||||
}
|
||||
|
||||
// resolved something at this point, a candidate can be fetched or created
|
||||
TypeVarCandidate typeVarCandidate;
|
||||
if ( candidates.containsKey( mthdParType ) ) {
|
||||
typeVarCandidate = candidates.get( mthdParType );
|
||||
}
|
||||
else {
|
||||
// add a new type
|
||||
typeVarCandidate = new TypeVarCandidate( );
|
||||
candidates.put( mthdParType, typeVarCandidate );
|
||||
}
|
||||
|
||||
// check what we've resolved
|
||||
if ( resolved.getParameter().isTypeVar() ) {
|
||||
// it might be already set, but we just checked if its an equivalent type
|
||||
if ( typeVarCandidate.match == null ) {
|
||||
typeVarCandidate.match = resolved.getMatch();
|
||||
if ( typeVarCandidate.match == null) {
|
||||
return false;
|
||||
}
|
||||
typeVarCandidate.pairs.add( resolved );
|
||||
}
|
||||
else if ( !areEquivalent( resolved.getMatch(), typeVarCandidate.match ) ) {
|
||||
// type has been resolved twice, but with a different candidate (conflict)
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else if ( resolved.getParameter().isArrayTypeVar()
|
||||
&& resolved.getParameter().getComponentType().isAssignableTo( mthdParType ) ) {
|
||||
// e.g. <T extends Number> T map( List<T[]> in ), the match for T should be assignable
|
||||
// to the parameter T extends Number
|
||||
typeVarCandidate.pairs.add( resolved );
|
||||
}
|
||||
else if ( resolved.getParameter().isWildCardBoundByTypeVar()
|
||||
&& resolved.getParameter().getTypeBound().isAssignableTo( mthdParType ) ) {
|
||||
// e.g. <T extends Number> T map( List<? super T> in ), the match for ? super T should be assignable
|
||||
// to the parameter T extends Number
|
||||
typeVarCandidate.pairs.add( resolved );
|
||||
}
|
||||
else {
|
||||
// none of the above
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all found candidates are within the bounds of the method type var. For instance
|
||||
* @<code><T, U extends Callable<T> U map( T in )</code>. Note that only the relation between the
|
||||
* match for U and Callable are checked. Not the correct parameter.
|
||||
*
|
||||
* @param methodParCandidates
|
||||
*
|
||||
* @return true when all within bounds.
|
||||
*/
|
||||
private boolean candidatesWithinBounds(Map<Type, TypeVarCandidate> methodParCandidates ) {
|
||||
for ( Map.Entry<Type, TypeVarCandidate> entry : methodParCandidates.entrySet() ) {
|
||||
Type bound = entry.getKey().getTypeBound();
|
||||
if ( bound != null ) {
|
||||
for ( Type.ResolvedPair pair : entry.getValue().pairs ) {
|
||||
if ( entry.getKey().hasUpperBound() ) {
|
||||
if ( !pair.getMatch().asRawType().isAssignableTo( bound.asRawType() ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// lower bound
|
||||
if ( !bound.asRawType().isAssignableTo( pair.getMatch().asRawType() ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasGenericTypeParameters(Type typeFromCandidateMethod) {
|
||||
for ( Type typeParam : typeFromCandidateMethod.getTypeParameters() ) {
|
||||
if ( typeParam.isTypeVar() || typeParam.isWildCardBoundByTypeVar() || typeParam.isArrayTypeVar() ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if ( hasGenericTypeParameters( typeParam ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Type resolve( Type typeFromCandidateMethod, Map<Type, Type> pairs ) {
|
||||
if ( typeFromCandidateMethod.isTypeVar() || typeFromCandidateMethod.isArrayTypeVar() ) {
|
||||
return pairs.get( typeFromCandidateMethod );
|
||||
}
|
||||
else if ( hasGenericTypeParameters( typeFromCandidateMethod ) ) {
|
||||
TypeMirror[] typeArgs = new TypeMirror[ typeFromCandidateMethod.getTypeParameters().size() ];
|
||||
for ( int i = 0; i < typeFromCandidateMethod.getTypeParameters().size(); i++ ) {
|
||||
Type typeFromCandidateMethodTypeParameter = typeFromCandidateMethod.getTypeParameters().get( i );
|
||||
if ( hasGenericTypeParameters( typeFromCandidateMethodTypeParameter ) ) {
|
||||
// nested type var, lets resolve some more (recur)
|
||||
Type matchingType = resolve( typeFromCandidateMethodTypeParameter, pairs );
|
||||
if ( matchingType == null ) {
|
||||
// something went wrong
|
||||
return null;
|
||||
}
|
||||
typeArgs[i] = matchingType.getTypeMirror();
|
||||
}
|
||||
else if ( typeFromCandidateMethodTypeParameter.isWildCardBoundByTypeVar()
|
||||
|| typeFromCandidateMethodTypeParameter.isTypeVar()
|
||||
|| typeFromCandidateMethodTypeParameter.isArrayTypeVar()
|
||||
) {
|
||||
Type matchingType = pairs.get( typeFromCandidateMethodTypeParameter );
|
||||
if ( matchingType == null ) {
|
||||
// something went wrong
|
||||
return null;
|
||||
}
|
||||
typeArgs[i] = matchingType.getTypeMirror();
|
||||
}
|
||||
else {
|
||||
// it is not a type var (e.g. Map<String, T> ), String is not a type var
|
||||
typeArgs[i] = typeFromCandidateMethodTypeParameter.getTypeMirror();
|
||||
}
|
||||
}
|
||||
DeclaredType typeArg = typeUtils.getDeclaredType( typeFromCandidateMethod.getTypeElement(), typeArgs );
|
||||
return typeFactory.getType( typeArg );
|
||||
}
|
||||
else {
|
||||
// its not a type var or generic parameterized (e.g. just a plain type)
|
||||
return typeFromCandidateMethod;
|
||||
}
|
||||
}
|
||||
|
||||
boolean areEquivalent( Type a, Type b ) {
|
||||
if ( a == null || b == null ) {
|
||||
return false;
|
||||
}
|
||||
return a.getBoxedEquivalent().equals( b.getBoxedEquivalent() );
|
||||
}
|
||||
}
|
||||
|
||||
private static class TypeVarCandidate {
|
||||
|
||||
private Type match;
|
||||
private List<Type.ResolvedPair> pairs = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||
@ -56,6 +57,7 @@ public class SourceMethod implements Method {
|
||||
private final List<Parameter> sourceParameters;
|
||||
private final List<Parameter> contextParameters;
|
||||
private final ParameterProvidedMethods contextProvidedMethods;
|
||||
private final List<Type> typeParameters;
|
||||
|
||||
private List<String> parameterNames;
|
||||
|
||||
@ -89,6 +91,8 @@ public class SourceMethod implements Method {
|
||||
private List<ValueMappingOptions> valueMappings;
|
||||
private EnumMappingOptions enumMappingOptions;
|
||||
private ParameterProvidedMethods contextProvidedMethods;
|
||||
private List<Type> typeParameters;
|
||||
|
||||
private boolean verboseLogging;
|
||||
|
||||
public Builder setDeclaringMapper(Type declaringMapper) {
|
||||
@ -197,6 +201,12 @@ public class SourceMethod implements Method {
|
||||
valueMappings
|
||||
);
|
||||
|
||||
this.typeParameters = this.executable.getTypeParameters()
|
||||
.stream()
|
||||
.map( Element::asType )
|
||||
.map( typeFactory::getType )
|
||||
.collect( Collectors.toList() );
|
||||
|
||||
return new SourceMethod( this, mappingMethodOptions );
|
||||
}
|
||||
}
|
||||
@ -214,6 +224,7 @@ public class SourceMethod implements Method {
|
||||
this.sourceParameters = Parameter.getSourceParameters( parameters );
|
||||
this.contextParameters = Parameter.getContextParameters( parameters );
|
||||
this.contextProvidedMethods = builder.contextProvidedMethods;
|
||||
this.typeParameters = builder.typeParameters;
|
||||
|
||||
this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
|
||||
this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters );
|
||||
@ -533,6 +544,11 @@ public class SourceMethod implements Method {
|
||||
return hasObjectFactoryAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Type> getTypeParameters() {
|
||||
return this.typeParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
if ( verboseLogging ) {
|
||||
|
@ -67,7 +67,7 @@ public abstract class BuiltInMethod implements Method {
|
||||
|
||||
Type sourceType = first( sourceTypes );
|
||||
|
||||
Type returnType = getReturnType().resolveTypeVarToType( sourceType, getParameter().getType() );
|
||||
Type returnType = getReturnType().resolveParameterToType( sourceType, getParameter().getType() ).getMatch();
|
||||
if ( returnType == null ) {
|
||||
return false;
|
||||
}
|
||||
@ -151,6 +151,11 @@ public abstract class BuiltInMethod implements Method {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Type> getTypeParameters() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* hashCode based on class
|
||||
*
|
||||
|
@ -30,9 +30,11 @@ public class CreateOrUpdateSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired() ) {
|
||||
return methods;
|
||||
|
@ -22,10 +22,10 @@ public class FactoryParameterSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type>sourceTypes,
|
||||
Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType, Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
if ( !criteria.isObjectFactoryRequired() || methods.size() <= 1 ) {
|
||||
return methods;
|
||||
}
|
||||
|
@ -23,10 +23,10 @@ public class InheritanceSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType, Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
if ( sourceTypes.size() != 1 ) {
|
||||
return methods;
|
||||
|
@ -21,9 +21,11 @@ public class MethodFamilySelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type targetType, SelectionCriteria criteria) {
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
List<SelectedMethod<T>> result = new ArrayList<>( methods.size() );
|
||||
for ( SelectedMethod<T> method : methods ) {
|
||||
|
@ -26,12 +26,15 @@ interface MethodSelector {
|
||||
* @param mappingMethod mapping method, defined in Mapper for which this selection is carried out
|
||||
* @param candidates list of available methods
|
||||
* @param sourceTypes parameter type(s) that should be matched
|
||||
* @param targetType result type that should be matched
|
||||
* @param mappingTargetType mappingTargetType that should be matched
|
||||
* @param returnType return type that should be matched
|
||||
* @param criteria criteria used in the selection process
|
||||
* @return list of methods that passes the matching process
|
||||
*/
|
||||
<T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> candidates,
|
||||
List<Type> sourceTypes,
|
||||
Type targetType, SelectionCriteria criteria);
|
||||
List<SelectedMethod<T>> candidates,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria);
|
||||
}
|
||||
|
@ -8,13 +8,13 @@ package org.mapstruct.ap.internal.model.source.selector;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.mapstruct.ap.internal.util.ElementUtils;
|
||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.util.ElementUtils;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||
|
||||
/**
|
||||
* Applies all known {@link MethodSelector}s in order.
|
||||
@ -45,13 +45,17 @@ public class MethodSelectors {
|
||||
* @param mappingMethod mapping method, defined in Mapper for which this selection is carried out
|
||||
* @param methods list of available methods
|
||||
* @param sourceTypes parameter type(s) that should be matched
|
||||
* @param targetType return type that should be matched
|
||||
* @param mappingTargetType the mapping target type that should be matched
|
||||
* @param returnType return type that should be matched
|
||||
* @param criteria criteria used in the selection process
|
||||
* @return list of methods that passes the matching process
|
||||
*/
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod, List<T> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<T> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
List<SelectedMethod<T>> candidates = new ArrayList<>( methods.size() );
|
||||
for ( T method : methods ) {
|
||||
@ -63,7 +67,8 @@ public class MethodSelectors {
|
||||
mappingMethod,
|
||||
candidates,
|
||||
sourceTypes,
|
||||
targetType,
|
||||
mappingTargetType,
|
||||
returnType,
|
||||
criteria );
|
||||
}
|
||||
return candidates;
|
||||
|
@ -50,9 +50,11 @@ public class QualifierSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
int numberOfQualifiersToMatch = 0;
|
||||
|
||||
|
@ -32,9 +32,11 @@ public class TargetTypeSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
TypeMirror qualifyingTypeMirror = criteria.getQualifyingResultType();
|
||||
if ( qualifyingTypeMirror != null && !criteria.isLifecycleCallbackRequired() ) {
|
||||
|
@ -39,9 +39,11 @@ public class TypeSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
if ( methods.isEmpty() ) {
|
||||
return methods;
|
||||
@ -54,12 +56,16 @@ public class TypeSelector implements MethodSelector {
|
||||
// if no source types are given, we have a factory or lifecycle method
|
||||
availableBindings = getAvailableParameterBindingsFromMethod(
|
||||
mappingMethod,
|
||||
targetType,
|
||||
mappingTargetType,
|
||||
criteria.getSourceRHS()
|
||||
);
|
||||
}
|
||||
else {
|
||||
availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType, mappingMethod );
|
||||
availableBindings = getAvailableParameterBindingsFromSourceTypes(
|
||||
sourceTypes,
|
||||
mappingTargetType,
|
||||
mappingMethod
|
||||
);
|
||||
}
|
||||
|
||||
for ( SelectedMethod<T> method : methods ) {
|
||||
@ -68,7 +74,7 @@ public class TypeSelector implements MethodSelector {
|
||||
|
||||
if ( parameterBindingPermutations != null ) {
|
||||
SelectedMethod<T> matchingMethod =
|
||||
getMatchingParameterBinding( targetType, mappingMethod, method, parameterBindingPermutations );
|
||||
getMatchingParameterBinding( returnType, mappingMethod, method, parameterBindingPermutations );
|
||||
|
||||
if ( matchingMethod != null ) {
|
||||
result.add( matchingMethod );
|
||||
@ -143,7 +149,7 @@ public class TypeSelector implements MethodSelector {
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Method> SelectedMethod<T> getMatchingParameterBinding(Type targetType,
|
||||
private <T extends Method> SelectedMethod<T> getMatchingParameterBinding(Type returnType,
|
||||
Method mappingMethod, SelectedMethod<T> selectedMethodInfo,
|
||||
List<List<ParameterBinding>> parameterAssignmentVariants) {
|
||||
|
||||
@ -155,7 +161,7 @@ public class TypeSelector implements MethodSelector {
|
||||
|
||||
// remove all assignment variants that doesn't match the types from the method
|
||||
matchingParameterAssignmentVariants.removeIf( parameterAssignments ->
|
||||
!selectedMethod.matches( extractTypes( parameterAssignments ), targetType )
|
||||
!selectedMethod.matches( extractTypes( parameterAssignments ), returnType )
|
||||
);
|
||||
|
||||
if ( matchingParameterAssignmentVariants.isEmpty() ) {
|
||||
|
@ -46,7 +46,9 @@ public class XmlElementDeclSelector implements MethodSelector {
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
List<Type> sourceTypes,
|
||||
Type mappingTargetType,
|
||||
Type returnType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
List<SelectedMethod<T>> nameMatches = new ArrayList<>();
|
||||
|
@ -513,14 +513,14 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
messager.printMessage( method, Message.RETRIEVAL_TYPE_VAR_RESULT );
|
||||
return false;
|
||||
}
|
||||
if ( typeParameter.isWildCardExtendsBound() ) {
|
||||
if ( typeParameter.hasExtendsBound() ) {
|
||||
messager.printMessage( method, Message.RETRIEVAL_WILDCARD_EXTENDS_BOUND_RESULT );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for ( Type typeParameter : parameterType.getTypeParameters() ) {
|
||||
if ( typeParameter.isWildCardSuperBound() ) {
|
||||
if ( typeParameter.hasSuperBound() ) {
|
||||
messager.printMessage( method, Message.RETRIEVAL_WILDCARD_SUPER_BOUND_SOURCE );
|
||||
return false;
|
||||
}
|
||||
|
@ -460,6 +460,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
methods,
|
||||
singletonList( source ),
|
||||
target,
|
||||
target,
|
||||
selectionCriteria
|
||||
);
|
||||
}
|
||||
@ -758,7 +759,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
attempt.selectionCriteria.setPreferUpdateMapping( false );
|
||||
for ( T2 yCandidate : yMethods ) {
|
||||
Type ySourceType = yCandidate.getMappingSourceType();
|
||||
ySourceType = ySourceType.resolveTypeVarToType( targetType, yCandidate.getResultType() );
|
||||
ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch();
|
||||
Type yTargetType = yCandidate.getResultType();
|
||||
if ( ySourceType == null
|
||||
|| !yTargetType.isRawAssignableTo( targetType )
|
||||
@ -876,7 +877,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
|
||||
for ( T yCandidate : methods ) {
|
||||
Type ySourceType = yCandidate.getMappingSourceType();
|
||||
ySourceType = ySourceType.resolveTypeVarToType( targetType, yCandidate.getResultType() );
|
||||
ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch();
|
||||
Type yTargetType = yCandidate.getResultType();
|
||||
if ( ySourceType == null
|
||||
|| !yTargetType.isRawAssignableTo( targetType )
|
||||
@ -993,7 +994,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
for ( T xCandidate : methods ) {
|
||||
Type xTargetType = xCandidate.getReturnType();
|
||||
Type xSourceType = xCandidate.getMappingSourceType();
|
||||
xTargetType = xTargetType.resolveTypeVarToType( sourceType, xSourceType );
|
||||
xTargetType = xTargetType.resolveParameterToType( sourceType, xSourceType ).getMatch();
|
||||
if ( xTargetType == null
|
||||
|| xCandidate.isUpdateMethod()
|
||||
|| !sourceType.isRawAssignableTo( xSourceType )
|
||||
|
@ -6,10 +6,49 @@
|
||||
package org.mapstruct.ap.internal.util;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.type.TypeVariable;
|
||||
import javax.lang.model.type.WildcardType;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
public class EclipseTypeUtilsDecorator extends AbstractTypeUtilsDecorator {
|
||||
|
||||
private final Types delegate;
|
||||
|
||||
EclipseTypeUtilsDecorator(ProcessingEnvironment processingEnv) {
|
||||
super( processingEnv );
|
||||
this.delegate = processingEnv.getTypeUtils();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(TypeMirror t1, TypeMirror t2) {
|
||||
if ( TypeKind.TYPEVAR == t2.getKind() ) {
|
||||
return containsType( t1, ( (TypeVariable) t2 ).getLowerBound() );
|
||||
}
|
||||
else {
|
||||
return containsType( t1, t2 );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsType(TypeMirror t1, TypeMirror t2) {
|
||||
|
||||
boolean result = false;
|
||||
if ( TypeKind.DECLARED == t2.getKind() ) {
|
||||
if ( TypeKind.WILDCARD == t1.getKind() ) {
|
||||
WildcardType wct = (WildcardType) t1;
|
||||
if ( wct.getExtendsBound() != null ) {
|
||||
result = isAssignable( t2, wct.getExtendsBound() );
|
||||
}
|
||||
else if ( wct.getSuperBound() != null ) {
|
||||
result = isAssignable( wct.getSuperBound(), t2 );
|
||||
}
|
||||
else {
|
||||
result = isAssignable( t2, wct );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,9 +7,9 @@
|
||||
-->
|
||||
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.common.Type" -->
|
||||
<@compress single_line=true>
|
||||
<#if wildCardExtendsBound>
|
||||
<#if hasExtendsBound()>
|
||||
? extends <@includeModel object=typeBound />
|
||||
<#elseif wildCardSuperBound>
|
||||
<#elseif hasSuperBound()>
|
||||
? super <@includeModel object=typeBound />
|
||||
<#else>
|
||||
<#if ext.asVarArgs!false>${createReferenceName()?remove_ending("[]")}...<#else>${createReferenceName()}</#if></#if><#if (!ext.raw?? && typeParameters?size > 0) ><<#list typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>>
|
||||
|
@ -20,8 +20,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
Source2.class,
|
||||
Target.class,
|
||||
SourceEnum.class,
|
||||
SourceTargetMapper.class,
|
||||
TargetSourceMapper.class,
|
||||
BigDecimalWrapper.class,
|
||||
ValueWrapper.class
|
||||
})
|
||||
@ -30,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class Issue1482Test {
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceTargetMapper.class )
|
||||
public void testForward() {
|
||||
|
||||
Source source = new Source();
|
||||
@ -45,6 +44,7 @@ public class Issue1482Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( TargetSourceMapper.class )
|
||||
public void testReverse() {
|
||||
|
||||
Target target = new Target();
|
||||
|
@ -20,9 +20,9 @@ public abstract class TargetSourceMapper {
|
||||
@Mapping(target = "wrapper", source = "bigDecimal")
|
||||
abstract Source2 map(Target target);
|
||||
|
||||
protected <T extends Enum<T>> Enum<T> map(String in, @TargetType Class<T>clz ) {
|
||||
protected <T extends Enum<T>> T map(String in, @TargetType Class<T>clz ) {
|
||||
if ( clz.isAssignableFrom( SourceEnum.class )) {
|
||||
return (Enum<T>) SourceEnum.valueOf( in );
|
||||
return (T) SourceEnum.valueOf( in );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -37,8 +37,6 @@ public abstract class SourceTargetMapper {
|
||||
@InheritConfiguration
|
||||
public abstract Target sourceToTargetTwoArg(Source source, @MappingTarget Target target);
|
||||
|
||||
public abstract Set<String> integerSetToStringSet(Set<Integer> integers);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
public abstract Set<Integer> stringSetToIntegerSet(Set<String> strings);
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.generics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.junit.Test;
|
||||
@ -17,8 +19,6 @@ import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for the invocation of generic methods for mapping bean properties.
|
||||
*
|
||||
@ -39,7 +39,6 @@ public class ConversionTest {
|
||||
|
||||
// setup used types
|
||||
TypeB typeB = new TypeB();
|
||||
TypeC typeC = new TypeC();
|
||||
|
||||
// setup source
|
||||
Source source = new Source();
|
||||
@ -51,10 +50,7 @@ public class ConversionTest {
|
||||
source.setFooNested( new Wrapper<>( new Wrapper<>( new BigDecimal( 5 ) ) ) );
|
||||
source.setFooUpperBoundCorrect( new UpperBoundWrapper<>( typeB ) );
|
||||
source.setFooWildCardExtendsString( new WildCardExtendsWrapper<>( "test3" ) );
|
||||
source.setFooWildCardExtendsTypeCCorrect( new WildCardExtendsWrapper<>( typeC ) );
|
||||
source.setFooWildCardExtendsTypeBCorrect( new WildCardExtendsWrapper<>( typeB ) );
|
||||
source.setFooWildCardSuperString( new WildCardSuperWrapper<>( "test4" ) );
|
||||
source.setFooWildCardExtendsMBTypeCCorrect( new WildCardExtendsMBWrapper<>( typeC ) );
|
||||
source.setFooWildCardSuperTypeBCorrect( new WildCardSuperWrapper<>( typeB ) );
|
||||
|
||||
// define wrapper
|
||||
@ -71,12 +67,8 @@ public class ConversionTest {
|
||||
assertThat( target.getFooNested() ).isEqualTo( new BigDecimal( 5 ) );
|
||||
assertThat( target.getFooUpperBoundCorrect() ).isEqualTo( typeB );
|
||||
assertThat( target.getFooWildCardExtendsString() ).isEqualTo( "test3" );
|
||||
assertThat( target.getFooWildCardExtendsTypeCCorrect() ).isEqualTo( typeC );
|
||||
assertThat( target.getFooWildCardExtendsTypeBCorrect() ).isEqualTo( typeB );
|
||||
assertThat( target.getFooWildCardSuperString() ).isEqualTo( "test4" );
|
||||
assertThat( target.getFooWildCardExtendsMBTypeCCorrect() ).isEqualTo( typeC );
|
||||
assertThat( target.getFooWildCardSuperTypeBCorrect() ).isEqualTo( typeB );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -140,20 +132,6 @@ public class ConversionTest {
|
||||
public void shouldFailOnSuperBounds1() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ErroneousSource5.class, ErroneousTarget5.class, ErroneousSourceTargetMapper5.class })
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousSourceTargetMapper5.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 16,
|
||||
message = "No target bean properties found: can't map property \"WildCardSuperWrapper<TypeC> " +
|
||||
"fooWildCardSuperTypeCFailure\" to \"TypeC fooWildCardSuperTypeCFailure\". " +
|
||||
"Consider to declare/implement a mapping method: \"TypeC map(WildCardSuperWrapper<TypeC> value)\".")
|
||||
})
|
||||
public void shouldFailOnSuperBounds2() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({
|
||||
ErroneousSource6.class,
|
||||
|
@ -5,8 +5,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.generics;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class GenericTypeMapper {
|
||||
|
||||
public <T> T getWrapped(Wrapper<T> source) {
|
||||
@ -53,7 +51,4 @@ public class GenericTypeMapper {
|
||||
return (String) t.getWrapped();
|
||||
}
|
||||
|
||||
public <T extends TypeB & Serializable> T getWildCardExtendsMBType(WildCardExtendsMBWrapper<? extends T> t) {
|
||||
return t.getWrapped();
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,7 @@ public class Source {
|
||||
private Wrapper<Wrapper<BigDecimal>> fooNested;
|
||||
private UpperBoundWrapper<TypeB> fooUpperBoundCorrect;
|
||||
private WildCardExtendsWrapper<String> fooWildCardExtendsString;
|
||||
private WildCardExtendsWrapper<TypeC> fooWildCardExtendsTypeCCorrect;
|
||||
private WildCardExtendsWrapper<TypeB> fooWildCardExtendsTypeBCorrect;
|
||||
private WildCardSuperWrapper<String> fooWildCardSuperString;
|
||||
private WildCardExtendsMBWrapper<TypeC> fooWildCardExtendsMBTypeCCorrect;
|
||||
private WildCardSuperWrapper<TypeB> fooWildCardSuperTypeBCorrect;
|
||||
|
||||
public Wrapper<Integer> getFooInteger() {
|
||||
@ -87,22 +84,6 @@ public class Source {
|
||||
this.fooWildCardExtendsString = fooWildCardExtendsString;
|
||||
}
|
||||
|
||||
public void setFooWildCardExtendsTypeCCorrect(WildCardExtendsWrapper<TypeC> fooWildCardExtendsTypeCCorrect) {
|
||||
this.fooWildCardExtendsTypeCCorrect = fooWildCardExtendsTypeCCorrect;
|
||||
}
|
||||
|
||||
public WildCardExtendsWrapper<TypeC> getFooWildCardExtendsTypeCCorrect() {
|
||||
return fooWildCardExtendsTypeCCorrect;
|
||||
}
|
||||
|
||||
public WildCardExtendsWrapper<TypeB> getFooWildCardExtendsTypeBCorrect() {
|
||||
return fooWildCardExtendsTypeBCorrect;
|
||||
}
|
||||
|
||||
public void setFooWildCardExtendsTypeBCorrect(WildCardExtendsWrapper<TypeB> fooWildCardExtendsTypeBCorrect) {
|
||||
this.fooWildCardExtendsTypeBCorrect = fooWildCardExtendsTypeBCorrect;
|
||||
}
|
||||
|
||||
public WildCardSuperWrapper<String> getFooWildCardSuperString() {
|
||||
return fooWildCardSuperString;
|
||||
}
|
||||
@ -111,14 +92,6 @@ public class Source {
|
||||
this.fooWildCardSuperString = fooWildCardSuperString;
|
||||
}
|
||||
|
||||
public WildCardExtendsMBWrapper<TypeC> getFooWildCardExtendsMBTypeCCorrect() {
|
||||
return fooWildCardExtendsMBTypeCCorrect;
|
||||
}
|
||||
|
||||
public void setFooWildCardExtendsMBTypeCCorrect(WildCardExtendsMBWrapper<TypeC> fooWildCardExtendsMBTypeCCorrect) {
|
||||
this.fooWildCardExtendsMBTypeCCorrect = fooWildCardExtendsMBTypeCCorrect;
|
||||
}
|
||||
|
||||
public WildCardSuperWrapper<TypeB> getFooWildCardSuperTypeBCorrect() {
|
||||
return fooWildCardSuperTypeBCorrect;
|
||||
}
|
||||
|
@ -17,10 +17,7 @@ public class Target {
|
||||
private BigDecimal fooNested;
|
||||
private TypeB fooUpperBoundCorrect;
|
||||
private String fooWildCardExtendsString;
|
||||
private TypeC fooWildCardExtendsTypeCCorrect;
|
||||
private TypeB fooWildCardExtendsTypeBCorrect;
|
||||
private String fooWildCardSuperString;
|
||||
private TypeC fooWildCardExtendsMBTypeCCorrect;
|
||||
private TypeB fooWildCardSuperTypeBCorrect;
|
||||
|
||||
public Integer getFooInteger() {
|
||||
@ -87,22 +84,6 @@ public class Target {
|
||||
this.fooWildCardExtendsString = fooWildCardExtendsString;
|
||||
}
|
||||
|
||||
public TypeC getFooWildCardExtendsTypeCCorrect() {
|
||||
return fooWildCardExtendsTypeCCorrect;
|
||||
}
|
||||
|
||||
public void setFooWildCardExtendsTypeCCorrect(TypeC fooWildCardExtendsTypeCCorrect) {
|
||||
this.fooWildCardExtendsTypeCCorrect = fooWildCardExtendsTypeCCorrect;
|
||||
}
|
||||
|
||||
public TypeB getFooWildCardExtendsTypeBCorrect() {
|
||||
return fooWildCardExtendsTypeBCorrect;
|
||||
}
|
||||
|
||||
public void setFooWildCardExtendsTypeBCorrect(TypeB fooWildCardExtendsTypeBCorrect) {
|
||||
this.fooWildCardExtendsTypeBCorrect = fooWildCardExtendsTypeBCorrect;
|
||||
}
|
||||
|
||||
public String getFooWildCardSuperString() {
|
||||
return fooWildCardSuperString;
|
||||
}
|
||||
@ -111,14 +92,6 @@ public class Target {
|
||||
this.fooWildCardSuperString = fooWildCardSuperString;
|
||||
}
|
||||
|
||||
public TypeC getFooWildCardExtendsMBTypeCCorrect() {
|
||||
return fooWildCardExtendsMBTypeCCorrect;
|
||||
}
|
||||
|
||||
public void setFooWildCardExtendsMBTypeCCorrect(TypeC fooWildCardExtendsMBTypeCCorrect) {
|
||||
this.fooWildCardExtendsMBTypeCCorrect = fooWildCardExtendsMBTypeCCorrect;
|
||||
}
|
||||
|
||||
public TypeB getFooWildCardSuperTypeBCorrect() {
|
||||
return fooWildCardSuperTypeBCorrect;
|
||||
}
|
||||
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.array;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface BothParameterizedMapper {
|
||||
|
||||
BothParameterizedMapper INSTANCE = Mappers.getMapper( BothParameterizedMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
default <T> GenericTargetWrapper<T[]> map(GenericSourceWrapper<T[]> in ) {
|
||||
return new GenericTargetWrapper<>( in.getWrapped() );
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final GenericSourceWrapper<String[]> prop;
|
||||
|
||||
public Source(GenericSourceWrapper<String[]> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public GenericSourceWrapper<String[]> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private GenericTargetWrapper<String[]> prop;
|
||||
|
||||
public GenericTargetWrapper<String[]> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(GenericTargetWrapper<String[]> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericTargetWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericTargetWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericSourceWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericSourceWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.array;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class GenericArrayTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( ReturnTypeIsTypeVarArrayMapper.class )
|
||||
public void testGenericReturnTypeVar() {
|
||||
|
||||
ReturnTypeIsTypeVarArrayMapper.GenericWrapper<String> wrapper =
|
||||
new ReturnTypeIsTypeVarArrayMapper.GenericWrapper<>( "test" );
|
||||
ReturnTypeIsTypeVarArrayMapper.Source source = new ReturnTypeIsTypeVarArrayMapper.Source( wrapper );
|
||||
|
||||
ReturnTypeIsTypeVarArrayMapper.Target target = ReturnTypeIsTypeVarArrayMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).containsExactly( "test" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceTypeIsTypeVarArrayMapper.class )
|
||||
public void testGenericSourceTypeVar() {
|
||||
|
||||
SourceTypeIsTypeVarArrayMapper.Source source =
|
||||
new SourceTypeIsTypeVarArrayMapper.Source( new String[] { "test" } );
|
||||
SourceTypeIsTypeVarArrayMapper.Target target = SourceTypeIsTypeVarArrayMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp().getWrapped() ).isEqualTo( "test" );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( BothParameterizedMapper.class )
|
||||
public void testBothParameterized() {
|
||||
|
||||
BothParameterizedMapper.GenericSourceWrapper<String[]> wrapper =
|
||||
new BothParameterizedMapper.GenericSourceWrapper<>( new String[] { "test" } );
|
||||
BothParameterizedMapper.Source source = new BothParameterizedMapper.Source( wrapper );
|
||||
BothParameterizedMapper.Target target = BothParameterizedMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp().getWrapped() ).containsExactly( "test" );
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.array;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface ReturnTypeIsTypeVarArrayMapper {
|
||||
|
||||
ReturnTypeIsTypeVarArrayMapper INSTANCE = Mappers.getMapper( ReturnTypeIsTypeVarArrayMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
default <T> T[] map(GenericWrapper<T> in) {
|
||||
return Collections.singletonList( in.getWrapped() )
|
||||
.toArray( (T[]) Array.newInstance( in.getWrapped().getClass(), 1 ) );
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private GenericWrapper<String> prop;
|
||||
|
||||
public Source(GenericWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public GenericWrapper<String> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(GenericWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private String[] prop;
|
||||
|
||||
public String[] getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(String[] prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.array;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface SourceTypeIsTypeVarArrayMapper {
|
||||
|
||||
SourceTypeIsTypeVarArrayMapper INSTANCE = Mappers.getMapper( SourceTypeIsTypeVarArrayMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> GenericWrapper<T> map( T[] in ) {
|
||||
if ( in.length > 0 ) {
|
||||
return new GenericWrapper<>( in[0] );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private String[] prop;
|
||||
|
||||
public Source(String[] prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public String[] getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private GenericWrapper<String> prop;
|
||||
|
||||
public GenericWrapper<String> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(GenericWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.bounds;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith( AnnotationProcessorTestRunner.class )
|
||||
public class BoundsTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceTypeIsBoundedTypeVarMapper.class )
|
||||
public void testGenericSourceTypeVar() {
|
||||
|
||||
SourceTypeIsBoundedTypeVarMapper.Source source = new SourceTypeIsBoundedTypeVarMapper.Source( "5", "test" );
|
||||
SourceTypeIsBoundedTypeVarMapper.Target target =
|
||||
SourceTypeIsBoundedTypeVarMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp1() ).isEqualTo( 5L );
|
||||
assertThat( target.getProp2().getProp() ).isEqualTo( "test" );
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.bounds;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.TargetType;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface SourceTypeIsBoundedTypeVarMapper {
|
||||
|
||||
SourceTypeIsBoundedTypeVarMapper INSTANCE = Mappers.getMapper( SourceTypeIsBoundedTypeVarMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
default <T extends NestedBase> T map(String in, @TargetType Class<T> clz) {
|
||||
if ( clz == Nested.class ) {
|
||||
return (T) new Nested( in );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final String prop1;
|
||||
private final String prop2;
|
||||
|
||||
public Source(String prop1, String prop2) {
|
||||
this.prop1 = prop1;
|
||||
this.prop2 = prop2;
|
||||
}
|
||||
|
||||
public String getProp1() {
|
||||
return prop1;
|
||||
}
|
||||
|
||||
public String getProp2() {
|
||||
return prop2;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private Long prop1;
|
||||
private Nested prop2;
|
||||
|
||||
public Long getProp1() {
|
||||
return prop1;
|
||||
}
|
||||
|
||||
public void setProp1(Long prop1) {
|
||||
this.prop1 = prop1;
|
||||
}
|
||||
|
||||
public Nested getProp2() {
|
||||
return prop2;
|
||||
}
|
||||
|
||||
public void setProp2(Nested prop2) {
|
||||
this.prop2 = prop2;
|
||||
}
|
||||
}
|
||||
|
||||
class NestedBase {
|
||||
}
|
||||
|
||||
class Nested extends NestedBase {
|
||||
|
||||
private String prop;
|
||||
|
||||
public Nested(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.multiple;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith( AnnotationProcessorTestRunner.class)
|
||||
public class MultipleTypeVarTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( ReturnTypeHasMultipleTypeVarOneGenericMapper.class )
|
||||
public void testGenericSourceTypeVarOneGeneric() {
|
||||
|
||||
ReturnTypeHasMultipleTypeVarOneGenericMapper.Source src =
|
||||
new ReturnTypeHasMultipleTypeVarOneGenericMapper.Source( 5L );
|
||||
ReturnTypeHasMultipleTypeVarOneGenericMapper.Target target =
|
||||
ReturnTypeHasMultipleTypeVarOneGenericMapper.INSTANCE.toTarget( src );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( ReturnTypeHasMultipleTypeVarBothGenericMapper.class )
|
||||
public void testGenericReturnTypeVarBothGeneric() {
|
||||
|
||||
ReturnTypeHasMultipleTypeVarBothGenericMapper.Pair pair
|
||||
= new ReturnTypeHasMultipleTypeVarBothGenericMapper.Pair( "test", 5L );
|
||||
ReturnTypeHasMultipleTypeVarBothGenericMapper.Source src =
|
||||
new ReturnTypeHasMultipleTypeVarBothGenericMapper.Source( pair );
|
||||
ReturnTypeHasMultipleTypeVarBothGenericMapper.Target target =
|
||||
ReturnTypeHasMultipleTypeVarBothGenericMapper.INSTANCE.toTarget( src );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceTypeHasMultipleTypeVarBothGenericMapper.class )
|
||||
public void testGenericSourceTypeVarBothGeneric() {
|
||||
|
||||
Map<String, Long> map = Collections.singletonMap( "test", 5L );
|
||||
SourceTypeHasMultipleTypeVarBothGenericMapper.Source src =
|
||||
new SourceTypeHasMultipleTypeVarBothGenericMapper.Source( map );
|
||||
SourceTypeHasMultipleTypeVarBothGenericMapper.Target target =
|
||||
SourceTypeHasMultipleTypeVarBothGenericMapper.INSTANCE.toTarget( src );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp().getFirst() ).isEqualTo( "test" );
|
||||
assertThat( target.getProp().getSecond() ).isEqualTo( 5L );
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.multiple;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface ReturnTypeHasMultipleTypeVarBothGenericMapper {
|
||||
|
||||
ReturnTypeHasMultipleTypeVarBothGenericMapper INSTANCE =
|
||||
Mappers.getMapper( ReturnTypeHasMultipleTypeVarBothGenericMapper.class );
|
||||
|
||||
Target toTarget(Source source);
|
||||
|
||||
default <T, U> HashMap<T, U> toMap( Pair<T, U> entry) {
|
||||
HashMap<T, U> result = new HashMap<>( );
|
||||
result.put( entry.first, entry.second );
|
||||
return result;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private Pair<String, Long> prop;
|
||||
|
||||
public Source(Pair<String, Long> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Pair<String, Long> getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private Map<String, Long> prop;
|
||||
|
||||
public Map<String, Long> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Target setProp(Map<String, Long> prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class Pair<T, U> {
|
||||
private final T first;
|
||||
private final U second;
|
||||
|
||||
public Pair(T first, U second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public T getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public U getSecond() {
|
||||
return second;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.multiple;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface ReturnTypeHasMultipleTypeVarOneGenericMapper {
|
||||
|
||||
ReturnTypeHasMultipleTypeVarOneGenericMapper INSTANCE =
|
||||
Mappers.getMapper( ReturnTypeHasMultipleTypeVarOneGenericMapper.class );
|
||||
|
||||
Target toTarget(Source source);
|
||||
|
||||
default <T> HashMap<String, T> toMap( T entry) {
|
||||
HashMap<String, T> result = new HashMap<>( );
|
||||
result.put( "test", entry );
|
||||
return result;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private Long prop;
|
||||
|
||||
public Source(Long prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Long getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private Map<String, Long> prop;
|
||||
|
||||
public Map<String, Long> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Target setProp(Map<String, Long> prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.multiple;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface SourceTypeHasMultipleTypeVarBothGenericMapper {
|
||||
|
||||
SourceTypeHasMultipleTypeVarBothGenericMapper INSTANCE =
|
||||
Mappers.getMapper( SourceTypeHasMultipleTypeVarBothGenericMapper.class );
|
||||
|
||||
Target toTarget(Source source);
|
||||
|
||||
default <T, U> HashMap<T, U> toMap( Pair<T, U> entry) {
|
||||
HashMap<T, U> result = new HashMap<>( );
|
||||
result.put( entry.first, entry.second );
|
||||
return result;
|
||||
}
|
||||
|
||||
default <T, U> Pair<T, U> toPair( Map<T, U> map) {
|
||||
if ( !map.isEmpty() ) {
|
||||
Map.Entry<T, U> firstEntry = map.entrySet().iterator().next();
|
||||
return new Pair<>( firstEntry.getKey(), firstEntry.getValue() );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final Map<String, Long> prop;
|
||||
|
||||
public Source(Map<String, Long> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Map<String, Long> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private Pair<String, Long> prop;
|
||||
|
||||
public Target(Pair<String, Long> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Pair<String, Long> getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Pair<T, U> {
|
||||
|
||||
private final T first;
|
||||
private final U second;
|
||||
|
||||
public Pair(T first, U second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public T getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public U getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.nestedgenerics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith( AnnotationProcessorTestRunner.class)
|
||||
public class NestedGenericsTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( ReturnTypeHasNestedTypeVarMapper.class )
|
||||
public void testGenericReturnTypeVar() {
|
||||
|
||||
ReturnTypeHasNestedTypeVarMapper.Source source = new ReturnTypeHasNestedTypeVarMapper.Source("test" );
|
||||
ReturnTypeHasNestedTypeVarMapper.Target target = ReturnTypeHasNestedTypeVarMapper.INSTANCE.toTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).hasSize( 1 );
|
||||
assertThat( target.getProp().get( 0 ) ).contains( "test" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceTypeHasNestedTypeVarMapper.class )
|
||||
public void testGenericSourceTypeVar() {
|
||||
|
||||
SourceTypeHasNestedTypeVarMapper.Source src =
|
||||
new SourceTypeHasNestedTypeVarMapper.Source( Collections.singletonList( Collections.singleton( "test" ) ) );
|
||||
SourceTypeHasNestedTypeVarMapper.Target target = SourceTypeHasNestedTypeVarMapper.INSTANCE.toTarget( src );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isEqualTo( "test" );
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.nestedgenerics;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface ReturnTypeHasNestedTypeVarMapper {
|
||||
|
||||
ReturnTypeHasNestedTypeVarMapper INSTANCE = Mappers.getMapper( ReturnTypeHasNestedTypeVarMapper.class );
|
||||
|
||||
Target toTarget(Source source);
|
||||
|
||||
default <T> List<Set<T>> wrapAsSetInList(T entry) {
|
||||
return Collections.singletonList( Collections.singleton( entry ) );
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final String prop;
|
||||
|
||||
public Source(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private List<Set<String>> prop;
|
||||
|
||||
public List<Set<String>> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Target setProp(List<Set<String>> prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.nestedgenerics;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface SourceTypeHasNestedTypeVarMapper {
|
||||
|
||||
SourceTypeHasNestedTypeVarMapper INSTANCE = Mappers.getMapper( SourceTypeHasNestedTypeVarMapper.class );
|
||||
|
||||
Target toTarget(Source source);
|
||||
|
||||
default <T> T unwrapToOneElement(List<Set<T>> listOfSet) {
|
||||
if ( !listOfSet.isEmpty() && listOfSet.get( 0 ).iterator().hasNext() ) {
|
||||
return listOfSet.get( 0 ).iterator().next();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final List<Set<String>> prop;
|
||||
|
||||
public Source(List<Set<String>> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public List<Set<String>> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private String prop;
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.plain;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface BothParameterizedMapper {
|
||||
|
||||
BothParameterizedMapper INSTANCE = Mappers.getMapper( BothParameterizedMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
default <T> GenericTargetWrapper<T> map(GenericSourceWrapper<T> in ) {
|
||||
return new GenericTargetWrapper<>( in.getWrapped() );
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final GenericSourceWrapper<String> prop;
|
||||
|
||||
public Source(GenericSourceWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public GenericSourceWrapper<String> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private GenericTargetWrapper<String> prop;
|
||||
|
||||
public GenericTargetWrapper<String> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(GenericTargetWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericTargetWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericTargetWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericSourceWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericSourceWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.plain;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class PlainTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( ReturnTypeIsTypeVarMapper.class )
|
||||
public void testGenericReturnTypeVar() {
|
||||
|
||||
ReturnTypeIsTypeVarMapper.Source source =
|
||||
new ReturnTypeIsTypeVarMapper.Source( new ReturnTypeIsTypeVarMapper.GenericWrapper<>( "test" ) );
|
||||
ReturnTypeIsTypeVarMapper.Target target = ReturnTypeIsTypeVarMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isEqualTo( "test" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceTypeIsTypeVarMapper.class )
|
||||
public void testGenericSourceTypeVar() {
|
||||
|
||||
SourceTypeIsTypeVarMapper.Source source = new SourceTypeIsTypeVarMapper.Source( "test" );
|
||||
SourceTypeIsTypeVarMapper.Target target = SourceTypeIsTypeVarMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp().getWrapped() ).isEqualTo( "test" );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( BothParameterizedMapper.class )
|
||||
public void testBothParameterized() {
|
||||
|
||||
BothParameterizedMapper.Source source =
|
||||
new BothParameterizedMapper.Source( new BothParameterizedMapper.GenericSourceWrapper<>( "test" ) );
|
||||
BothParameterizedMapper.Target target = BothParameterizedMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp().getWrapped() ).isEqualTo( "test" );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( ReturnTypeIsRawTypeMapper.class )
|
||||
public void testRaw() {
|
||||
|
||||
ReturnTypeIsRawTypeMapper.Source source = new ReturnTypeIsRawTypeMapper.Source( Collections.singleton( 5 ) );
|
||||
|
||||
ReturnTypeIsRawTypeMapper.Target target = ReturnTypeIsRawTypeMapper.INSTANCE.sourceToTarget( source );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp().iterator().next() ).isEqualTo( "5" );
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.plain;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface ReturnTypeIsRawTypeMapper {
|
||||
|
||||
ReturnTypeIsRawTypeMapper INSTANCE = Mappers.getMapper( ReturnTypeIsRawTypeMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
Set<String> selectMe(Set<Integer> integers);
|
||||
|
||||
Set<Integer> doNotSelectMe(Set<String> strings);
|
||||
|
||||
class Source {
|
||||
|
||||
private final Set<Integer> prop;
|
||||
|
||||
public Source(Set<Integer> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Set<Integer> getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private Set prop;
|
||||
|
||||
public Set getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Target setProp(Set prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.plain;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface ReturnTypeIsTypeVarMapper {
|
||||
|
||||
ReturnTypeIsTypeVarMapper INSTANCE = Mappers.getMapper( ReturnTypeIsTypeVarMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> T map(GenericWrapper<T> in) {
|
||||
return in.getWrapped();
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final GenericWrapper<String> prop;
|
||||
|
||||
public Source(GenericWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public GenericWrapper<String> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private String prop;
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.plain;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface SourceTypeIsTypeVarMapper {
|
||||
|
||||
SourceTypeIsTypeVarMapper INSTANCE = Mappers.getMapper( SourceTypeIsTypeVarMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> GenericWrapper<T> map( T in ) {
|
||||
return new GenericWrapper<>( in );
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final String prop;
|
||||
|
||||
public Source(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private GenericWrapper<String> prop;
|
||||
|
||||
public GenericWrapper<String> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(GenericWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericWrapper<T> {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.targettype;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.TargetType;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface NestedTargetTypeMapper {
|
||||
|
||||
NestedTargetTypeMapper INSTANCE = Mappers.getMapper( NestedTargetTypeMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T extends BaseType> T map(String string, @TargetType Class<T> clazz) {
|
||||
if ( clazz == GenericWrapper.class ) {
|
||||
return (T) new GenericWrapper<>( string );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private String prop;
|
||||
|
||||
public Source(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private GenericWrapper<String> prop;
|
||||
|
||||
public GenericWrapper<String> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(GenericWrapper<String> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class BaseType {
|
||||
}
|
||||
|
||||
class GenericWrapper<T> extends BaseType {
|
||||
private final T wrapped;
|
||||
|
||||
public GenericWrapper(T someType) {
|
||||
this.wrapped = someType;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.targettype;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.TargetType;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface PlainTargetTypeMapper {
|
||||
|
||||
PlainTargetTypeMapper INSTANCE = Mappers.getMapper( PlainTargetTypeMapper.class );
|
||||
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> T map(String string, @TargetType Class<T> clazz) {
|
||||
if ( clazz == BigDecimal.class ) {
|
||||
return (T) new BigDecimal( string );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private String prop;
|
||||
|
||||
public Source(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private BigDecimal prop;
|
||||
|
||||
public BigDecimal getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(BigDecimal prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
class BaseType {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.targettype;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class TargetTypeTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( PlainTargetTypeMapper.class )
|
||||
public void testPlain() {
|
||||
|
||||
PlainTargetTypeMapper.Target target =
|
||||
PlainTargetTypeMapper.INSTANCE.sourceToTarget( new PlainTargetTypeMapper.Source( "15" ) );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp().toPlainString() ).isEqualTo( "15" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( NestedTargetTypeMapper.class )
|
||||
public void testNestedTypeVar() {
|
||||
NestedTargetTypeMapper.Target target =
|
||||
NestedTargetTypeMapper.INSTANCE.sourceToTarget( new NestedTargetTypeMapper.Source( "test" ) );
|
||||
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp().getWrapped() ).isEqualTo( "test" );
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.wildcards;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface IntersectionMapper {
|
||||
|
||||
IntersectionMapper INSTANCE = Mappers.getMapper( IntersectionMapper.class );
|
||||
|
||||
Target map( Source source);
|
||||
|
||||
default <T extends TypeB & Serializable> T unwrap(Wrapper<? extends T> t) {
|
||||
return t.getWrapped();
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final Wrapper<TypeC> prop;
|
||||
|
||||
public Source(Wrapper<TypeC> prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Wrapper<TypeC> getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Wrapper<T> {
|
||||
|
||||
private final T wrapped;
|
||||
|
||||
public Wrapper(T wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private TypeC prop;
|
||||
|
||||
public TypeC getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void setProp(TypeC prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TypeC must intersect both TypeB & Serializable
|
||||
*/
|
||||
class TypeC extends TypeB implements Serializable {
|
||||
}
|
||||
|
||||
class TypeB extends TypeA {
|
||||
}
|
||||
|
||||
class TypeA {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.wildcards;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface SourceWildCardExtendsMapper {
|
||||
|
||||
SourceWildCardExtendsMapper INSTANCE = Mappers.getMapper( SourceWildCardExtendsMapper.class );
|
||||
|
||||
Target map( Source source);
|
||||
|
||||
default <T extends TypeB> T unwrap(Wrapper<? extends T> t) {
|
||||
return t.getWrapped();
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final Wrapper<TypeC> propB;
|
||||
private final Wrapper<TypeC> propC;
|
||||
|
||||
public Source(Wrapper<TypeC> propB, Wrapper<TypeC> propC) {
|
||||
this.propB = propB;
|
||||
this.propC = propC;
|
||||
}
|
||||
|
||||
public Wrapper<TypeC> getPropB() {
|
||||
return propB;
|
||||
}
|
||||
|
||||
public Wrapper<TypeC> getPropC() {
|
||||
return propC;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Wrapper<T> {
|
||||
|
||||
private final T wrapped;
|
||||
|
||||
public Wrapper(T wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private TypeB propB;
|
||||
private TypeC propC;
|
||||
|
||||
public TypeB getPropB() {
|
||||
return propB;
|
||||
}
|
||||
|
||||
public void setPropB(TypeB propB) {
|
||||
this.propB = propB;
|
||||
}
|
||||
|
||||
public TypeC getPropC() {
|
||||
return propC;
|
||||
}
|
||||
|
||||
public void setPropC(TypeC propC) {
|
||||
this.propC = propC;
|
||||
}
|
||||
}
|
||||
|
||||
class TypeC extends TypeB {
|
||||
}
|
||||
|
||||
class TypeB extends TypeA {
|
||||
}
|
||||
|
||||
class TypeA {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.methodgenerics.wildcards;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
import org.mapstruct.ap.testutil.runner.Compiler;
|
||||
import org.mapstruct.ap.testutil.runner.DisabledOnCompiler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class WildCardTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceWildCardExtendsMapper.class )
|
||||
public void testExtendsRelation() {
|
||||
|
||||
// prepare source
|
||||
SourceWildCardExtendsMapper.TypeB typeB = new SourceWildCardExtendsMapper.TypeB();
|
||||
SourceWildCardExtendsMapper.Wrapper wrapperB = new SourceWildCardExtendsMapper.Wrapper( typeB );
|
||||
SourceWildCardExtendsMapper.TypeC typeC = new SourceWildCardExtendsMapper.TypeC();
|
||||
SourceWildCardExtendsMapper.Wrapper wrapperC = new SourceWildCardExtendsMapper.Wrapper( typeC );
|
||||
SourceWildCardExtendsMapper.Source source = new SourceWildCardExtendsMapper.Source( wrapperB, wrapperC );
|
||||
|
||||
// action
|
||||
SourceWildCardExtendsMapper.Target target = SourceWildCardExtendsMapper.INSTANCE.map( source );
|
||||
|
||||
// verify target
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getPropB() ).isEqualTo( typeB );
|
||||
assertThat( target.getPropC() ).isEqualTo( typeC );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( IntersectionMapper.class )
|
||||
// Eclipse does not handle intersection types correctly (TODO: worthwhile to investigate?)
|
||||
@DisabledOnCompiler( Compiler.ECLIPSE )
|
||||
public void testIntersectionRelation() {
|
||||
|
||||
// prepare source
|
||||
IntersectionMapper.TypeC typeC = new IntersectionMapper.TypeC();
|
||||
IntersectionMapper.Wrapper wrapper = new IntersectionMapper.Wrapper( typeC );
|
||||
IntersectionMapper.Source source = new IntersectionMapper.Source( wrapper );
|
||||
|
||||
// action
|
||||
IntersectionMapper.Target target = IntersectionMapper.INSTANCE.map( source );
|
||||
|
||||
// verify target
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isEqualTo( typeC );
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.typegenerics;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface SourceWildCardExtendsMapper {
|
||||
|
||||
SourceWildCardExtendsMapper INSTANCE = Mappers.getMapper( SourceWildCardExtendsMapper.class );
|
||||
|
||||
Target map( Source source);
|
||||
|
||||
default String unwrap(Wrapper<? extends Number> t) {
|
||||
return t.getWrapped().toString();
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final Wrapper<BigInteger> prop;
|
||||
|
||||
public Source(Wrapper<BigInteger> prop) {
|
||||
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Wrapper<BigInteger> getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Wrapper<T> {
|
||||
|
||||
private final T wrapped;
|
||||
|
||||
public Wrapper(T wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private String prop;
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Target setProp(String prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.typegenerics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class WildCardTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceWildCardExtendsMapper.class )
|
||||
public void testWildCard() {
|
||||
|
||||
// prepare source
|
||||
SourceWildCardExtendsMapper.Wrapper<BigInteger> wrapper =
|
||||
new SourceWildCardExtendsMapper.Wrapper<>( new BigInteger( "5" ) );
|
||||
SourceWildCardExtendsMapper.Source source = new SourceWildCardExtendsMapper.Source( wrapper );
|
||||
|
||||
// action
|
||||
SourceWildCardExtendsMapper.Target target = SourceWildCardExtendsMapper.INSTANCE.map( source );
|
||||
|
||||
// verify target
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isEqualTo( "5" );
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.wildcards;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface ReturnTypeWildCardExtendsMapper {
|
||||
|
||||
ReturnTypeWildCardExtendsMapper INSTANCE = Mappers.getMapper( ReturnTypeWildCardExtendsMapper.class );
|
||||
|
||||
Target map(Source source);
|
||||
|
||||
default Wrapper<BigInteger> wrap(String in) {
|
||||
return new Wrapper<>( new BigInteger( in ) );
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private String prop;
|
||||
|
||||
public Source(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Source setProp(String prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private Wrapper<? super BigInteger> prop;
|
||||
|
||||
public Target setProp(Wrapper<? super BigInteger> prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Wrapper<? super BigInteger> getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Wrapper<T> {
|
||||
|
||||
private final T wrapped;
|
||||
|
||||
public Wrapper(T wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.wildcards;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface SourceWildCardExtendsMapper {
|
||||
|
||||
SourceWildCardExtendsMapper INSTANCE = Mappers.getMapper( SourceWildCardExtendsMapper.class );
|
||||
|
||||
Target map( Source source);
|
||||
|
||||
default String unwrap(Wrapper<? extends Number> t) {
|
||||
return t.getWrapped().toString();
|
||||
}
|
||||
|
||||
class Source {
|
||||
|
||||
private final Wrapper<BigInteger> prop;
|
||||
|
||||
public Source(Wrapper<BigInteger> prop) {
|
||||
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Wrapper<BigInteger> getProp() {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
class Wrapper<T> {
|
||||
|
||||
private final T wrapped;
|
||||
|
||||
public Wrapper(T wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Target {
|
||||
|
||||
private String prop;
|
||||
|
||||
public String getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Target setProp(String prop) {
|
||||
this.prop = prop;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.wildcards;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class WildCardTest {
|
||||
|
||||
@Test
|
||||
@WithClasses( SourceWildCardExtendsMapper.class )
|
||||
public void testWildCardAsSourceType() {
|
||||
|
||||
// prepare source
|
||||
SourceWildCardExtendsMapper.Wrapper<BigInteger> wrapper =
|
||||
new SourceWildCardExtendsMapper.Wrapper<>( new BigInteger( "5" ) );
|
||||
SourceWildCardExtendsMapper.Source source = new SourceWildCardExtendsMapper.Source( wrapper );
|
||||
|
||||
// action
|
||||
SourceWildCardExtendsMapper.Target target = SourceWildCardExtendsMapper.INSTANCE.map( source );
|
||||
|
||||
// verify target
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isEqualTo( "5" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( ReturnTypeWildCardExtendsMapper.class )
|
||||
public void testWildCardAsReturnType() {
|
||||
|
||||
// prepare source
|
||||
ReturnTypeWildCardExtendsMapper.Source source = new ReturnTypeWildCardExtendsMapper.Source( "5" );
|
||||
|
||||
// action
|
||||
ReturnTypeWildCardExtendsMapper.Target target = ReturnTypeWildCardExtendsMapper.INSTANCE.map( source );
|
||||
|
||||
// verify target
|
||||
assertThat( target ).isNotNull();
|
||||
assertThat( target.getProp() ).isNotNull();
|
||||
assertThat( target.getProp().getWrapped() ).isEqualTo( BigInteger.valueOf( 5 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user