mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#975 Refactor method-matching to unify selection and rendering of mapping method, factories and lifecycle methods
This commit is contained in:
parent
46363028bd
commit
79f87e8833
@ -31,6 +31,7 @@ import org.mapstruct.ap.internal.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.internal.model.assignment.LocalVarWrapper;
|
||||
import org.mapstruct.ap.internal.model.assignment.SetterWrapper;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
|
||||
@ -191,7 +192,11 @@ public class IterableMappingMethod extends MappingMethod {
|
||||
forgedMethodHistory
|
||||
);
|
||||
|
||||
Assignment assignment = new MethodReference( forgedMethod, null, targetType );
|
||||
Assignment assignment = new MethodReference(
|
||||
forgedMethod,
|
||||
null,
|
||||
ParameterBinding.fromParameters( forgedMethod.getParameters() ) );
|
||||
|
||||
assignment.setAssignment( sourceRHS );
|
||||
|
||||
return assignment;
|
||||
|
@ -19,17 +19,16 @@
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.QualifierSelector;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||
|
||||
/**
|
||||
@ -84,45 +83,36 @@ public final class LifecycleCallbackFactory {
|
||||
Method method, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
|
||||
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
||||
|
||||
Map<SourceMethod, List<Parameter>> parameterAssignmentsForSourceMethod
|
||||
= new HashMap<SourceMethod, List<Parameter>>();
|
||||
MethodSelectors selectors =
|
||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory() );
|
||||
|
||||
List<SourceMethod> candidates =
|
||||
filterCandidatesByType( method, callbackMethods, parameterAssignmentsForSourceMethod, ctx );
|
||||
|
||||
candidates = filterCandidatesByQualifiers( method, selectionParameters, candidates, ctx );
|
||||
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
|
||||
method,
|
||||
callbackMethods,
|
||||
Collections.<Type> emptyList(),
|
||||
method.getResultType(),
|
||||
SelectionCriteria.forLifecycleMethods( selectionParameters ) );
|
||||
|
||||
return toLifecycleCallbackMethodRefs(
|
||||
method,
|
||||
candidates,
|
||||
parameterAssignmentsForSourceMethod,
|
||||
matchingMethods,
|
||||
ctx,
|
||||
existingVariableNames );
|
||||
}
|
||||
|
||||
private static List<SourceMethod> filterCandidatesByQualifiers(Method method,
|
||||
SelectionParameters selectionParameters,
|
||||
List<SourceMethod> candidates,
|
||||
MappingBuilderContext ctx) {
|
||||
QualifierSelector selector = new QualifierSelector( ctx.getTypeUtils(), ctx.getElementUtils() );
|
||||
|
||||
return selector.getMatchingMethods( method, candidates, null, null, new SelectionCriteria(
|
||||
selectionParameters,
|
||||
null,
|
||||
false,
|
||||
false) );
|
||||
}
|
||||
|
||||
private static List<LifecycleCallbackMethodReference> toLifecycleCallbackMethodRefs(Method method,
|
||||
List<SourceMethod> candidates, Map<SourceMethod, List<Parameter>> parameterAssignmentsForSourceMethod,
|
||||
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
||||
List<SelectedMethod<SourceMethod>> candidates,
|
||||
MappingBuilderContext ctx,
|
||||
Set<String> existingVariableNames) {
|
||||
|
||||
List<LifecycleCallbackMethodReference> result = new ArrayList<LifecycleCallbackMethodReference>();
|
||||
for ( SourceMethod candidate : candidates ) {
|
||||
markMapperReferenceAsUsed( ctx.getMapperReferences(), candidate );
|
||||
for ( SelectedMethod<SourceMethod> candidate : candidates ) {
|
||||
MapperReference mapperReference = findMapperReference( ctx.getMapperReferences(), candidate.getMethod() );
|
||||
result.add(
|
||||
new LifecycleCallbackMethodReference(
|
||||
candidate,
|
||||
parameterAssignmentsForSourceMethod.get( candidate ),
|
||||
candidate.getMethod(),
|
||||
mapperReference,
|
||||
candidate.getParameterBindings(),
|
||||
method.getReturnType(),
|
||||
method.getResultType(),
|
||||
existingVariableNames ) );
|
||||
@ -130,77 +120,15 @@ public final class LifecycleCallbackFactory {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<SourceMethod> filterCandidatesByType(Method method,
|
||||
List<SourceMethod> callbackMethods, Map<SourceMethod, List<Parameter>> parameterAssignmentsForSourceMethod,
|
||||
MappingBuilderContext ctx) {
|
||||
|
||||
List<SourceMethod> candidates = new ArrayList<SourceMethod>();
|
||||
|
||||
List<Parameter> availableParams = getAvailableParameters( method, ctx );
|
||||
for ( SourceMethod callback : callbackMethods ) {
|
||||
List<Parameter> parameterAssignments =
|
||||
ParameterAssignmentUtil.getParameterAssignments( availableParams, callback.getParameters() );
|
||||
|
||||
if ( isValidCandidate( callback, method, parameterAssignments ) ) {
|
||||
parameterAssignmentsForSourceMethod.put( callback, parameterAssignments );
|
||||
candidates.add( callback );
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private static boolean isValidCandidate(SourceMethod candidate, Method method,
|
||||
List<Parameter> parameterAssignments) {
|
||||
if ( parameterAssignments == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( !candidate.matches( extractSourceTypes( parameterAssignments ), method.getResultType() ) ) {
|
||||
return false;
|
||||
}
|
||||
return ( candidate.getReturnType().isVoid() || candidate.getReturnType().isTypeVar()
|
||||
|| candidate.getReturnType().isAssignableTo( method.getResultType() ) );
|
||||
}
|
||||
|
||||
private static List<Parameter> getAvailableParameters(Method method, MappingBuilderContext ctx) {
|
||||
List<Parameter> availableParams = new ArrayList<Parameter>( method.getParameters() );
|
||||
if ( method.getMappingTargetParameter() == null ) {
|
||||
availableParams.add( new Parameter( null, method.getResultType(), true, false, false) );
|
||||
}
|
||||
|
||||
Parameter targetTypeParameter = new Parameter(
|
||||
null,
|
||||
ctx.getTypeFactory().classTypeOf( method.getResultType() ),
|
||||
false,
|
||||
true,
|
||||
false );
|
||||
|
||||
availableParams.add( targetTypeParameter );
|
||||
return availableParams;
|
||||
}
|
||||
|
||||
private static void markMapperReferenceAsUsed(List<MapperReference> references, Method method) {
|
||||
for ( MapperReference ref : references ) {
|
||||
private static MapperReference findMapperReference(List<MapperReference> mapperReferences, SourceMethod method) {
|
||||
for ( MapperReference ref : mapperReferences ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
if ( !ref.isUsed() && !method.isStatic() ) {
|
||||
ref.setUsed( true );
|
||||
}
|
||||
ref.setUsed( ref.isUsed() || !method.isStatic() );
|
||||
ref.setTypeRequiresImport( true );
|
||||
|
||||
return;
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Type> extractSourceTypes(List<Parameter> parameters) {
|
||||
List<Type> result = new ArrayList<Type>( parameters.size() );
|
||||
|
||||
for ( Parameter param : parameters ) {
|
||||
if ( !param.isMappingTarget() && !param.isTargetType() ) {
|
||||
result.add( param.getType() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<SourceMethod> filterBeforeMappingMethods(List<SourceMethod> methods) {
|
||||
|
@ -18,11 +18,10 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import java.beans.Introspector;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
@ -34,35 +33,22 @@ import org.mapstruct.ap.internal.util.Strings;
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class LifecycleCallbackMethodReference extends MappingMethod {
|
||||
public class LifecycleCallbackMethodReference extends MethodReference {
|
||||
|
||||
private final Type declaringType;
|
||||
private final List<Parameter> parameterAssignments;
|
||||
private final Type methodReturnType;
|
||||
private final Type methodResultType;
|
||||
private final String instanceVariableName;
|
||||
private final String targetVariableName;
|
||||
|
||||
public LifecycleCallbackMethodReference(SourceMethod method, List<Parameter> parameterAssignments,
|
||||
public LifecycleCallbackMethodReference(SourceMethod method, MapperReference mapperReference,
|
||||
List<ParameterBinding> parameterBindings,
|
||||
Type methodReturnType, Type methodResultType,
|
||||
Set<String> existingVariableNames) {
|
||||
super( method );
|
||||
super( method, mapperReference, parameterBindings );
|
||||
this.declaringType = method.getDeclaringMapper();
|
||||
this.parameterAssignments = parameterAssignments;
|
||||
this.methodReturnType = methodReturnType;
|
||||
this.methodResultType = methodResultType;
|
||||
|
||||
if ( isStatic() ) {
|
||||
this.instanceVariableName = declaringType.getName();
|
||||
}
|
||||
else if ( declaringType != null ) {
|
||||
this.instanceVariableName =
|
||||
Strings.getSaveVariableName( Introspector.decapitalize( declaringType.getName() ) );
|
||||
}
|
||||
else {
|
||||
this.instanceVariableName = null;
|
||||
}
|
||||
|
||||
if ( hasReturnType() ) {
|
||||
this.targetVariableName = Strings.getSaveVariableName( "target", existingVariableNames );
|
||||
existingVariableNames.add( this.targetVariableName );
|
||||
@ -76,10 +62,6 @@ public class LifecycleCallbackMethodReference extends MappingMethod {
|
||||
return declaringType;
|
||||
}
|
||||
|
||||
public String getInstanceVariableName() {
|
||||
return instanceVariableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the return type of the mapping method in which this callback method is called
|
||||
*
|
||||
@ -109,12 +91,8 @@ public class LifecycleCallbackMethodReference extends MappingMethod {
|
||||
return declaringType != null ? Collections.asSet( declaringType ) : java.util.Collections.<Type> emptySet();
|
||||
}
|
||||
|
||||
public List<Parameter> getParameterAssignments() {
|
||||
return parameterAssignments;
|
||||
}
|
||||
|
||||
public boolean hasMappingTargetParameter() {
|
||||
for ( Parameter param : parameterAssignments ) {
|
||||
for ( ParameterBinding param : getParameterBindings() ) {
|
||||
if ( param.isMappingTarget() ) {
|
||||
return true;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import java.util.Set;
|
||||
import org.mapstruct.ap.internal.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.internal.model.assignment.LocalVarWrapper;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
|
||||
@ -211,7 +212,11 @@ public class MapMappingMethod extends MappingMethod {
|
||||
history
|
||||
);
|
||||
|
||||
Assignment assignment = new MethodReference( forgedMethod, null, targetType );
|
||||
Assignment assignment = new MethodReference(
|
||||
forgedMethod,
|
||||
null,
|
||||
ParameterBinding.fromParameters( forgedMethod.getParameters() ) );
|
||||
|
||||
assignment.setAssignment( sourceRHS );
|
||||
|
||||
forgedMethods.add( forgedMethod );
|
||||
|
@ -27,6 +27,7 @@ import java.util.Set;
|
||||
import org.mapstruct.ap.internal.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.internal.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod;
|
||||
@ -62,18 +63,19 @@ public class MethodReference extends MappingMethod implements Assignment {
|
||||
private Assignment assignment;
|
||||
|
||||
private final Type definingType;
|
||||
private final List<ParameterBinding> parameterBindings;
|
||||
|
||||
/**
|
||||
* Creates a new reference to the given method.
|
||||
*
|
||||
* @param method the target method of the reference
|
||||
* @param declaringMapper the method declaring the mapper; {@code null} if the current mapper itself
|
||||
* @param targetType in case the referenced method has a parameter for passing the target type, the given
|
||||
* target type, otherwise {@code null}
|
||||
* @param parameterBindings the parameter bindings of this method reference
|
||||
*/
|
||||
public MethodReference(Method method, MapperReference declaringMapper, Type targetType) {
|
||||
public MethodReference(Method method, MapperReference declaringMapper, List<ParameterBinding> parameterBindings) {
|
||||
super( method );
|
||||
this.declaringMapper = declaringMapper;
|
||||
this.parameterBindings = parameterBindings;
|
||||
this.contextParam = null;
|
||||
Set<Type> imported = new HashSet<Type>();
|
||||
|
||||
@ -81,8 +83,8 @@ public class MethodReference extends MappingMethod implements Assignment {
|
||||
imported.addAll( type.getImportTypes() );
|
||||
}
|
||||
|
||||
if ( targetType != null ) {
|
||||
imported.addAll( targetType.getImportTypes() );
|
||||
for ( ParameterBinding binding : parameterBindings ) {
|
||||
imported.addAll( binding.getImportTypes() );
|
||||
}
|
||||
|
||||
this.importTypes = Collections.<Type>unmodifiableSet( imported );
|
||||
@ -99,6 +101,7 @@ public class MethodReference extends MappingMethod implements Assignment {
|
||||
this.thrownTypes = Collections.emptyList();
|
||||
this.definingType = null;
|
||||
this.isUpdateMethod = method.getMappingTargetParameter() != null;
|
||||
this.parameterBindings = ParameterBinding.fromParameters( method.getParameters() );
|
||||
}
|
||||
|
||||
public MapperReference getDeclaringMapper() {
|
||||
@ -214,4 +217,8 @@ public class MethodReference extends MappingMethod implements Assignment {
|
||||
public boolean isCallingUpdateMethod() {
|
||||
return isUpdateMethod;
|
||||
}
|
||||
|
||||
public List<ParameterBinding> getParameterBindings() {
|
||||
return parameterBindings;
|
||||
}
|
||||
}
|
||||
|
@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.type.TypeKind;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
|
||||
public class ParameterAssignmentUtil {
|
||||
|
||||
private ParameterAssignmentUtil() {
|
||||
}
|
||||
|
||||
public static List<Parameter> getParameterAssignments(List<Parameter> availableParams,
|
||||
List<Parameter> methodParameters) {
|
||||
List<Parameter> result = new ArrayList<Parameter>( methodParameters.size() );
|
||||
|
||||
for ( Parameter methodParam : methodParameters ) {
|
||||
List<Parameter> assignableParams = findCandidateParameters( availableParams, methodParam );
|
||||
|
||||
if ( assignableParams.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( assignableParams.size() == 1 ) {
|
||||
result.add( assignableParams.get( 0 ) );
|
||||
}
|
||||
else if ( assignableParams.size() > 1 ) {
|
||||
Parameter paramWithMatchingName = findParameterWithName( assignableParams, methodParam.getName() );
|
||||
|
||||
if ( paramWithMatchingName != null ) {
|
||||
result.add( paramWithMatchingName );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Parameter findParameterWithName(List<Parameter> parameters, String name) {
|
||||
for ( Parameter param : parameters ) {
|
||||
if ( name.equals( param.getName() ) ) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param candidateParameters available for assignment.
|
||||
* @param parameter that need assignment from one of the candidate parameters.
|
||||
* @return list of matching candidate parameters that can be assigned.
|
||||
*/
|
||||
private static List<Parameter> findCandidateParameters(List<Parameter> candidateParameters, Parameter parameter) {
|
||||
List<Parameter> result = new ArrayList<Parameter>( candidateParameters.size() );
|
||||
|
||||
for ( Parameter candidate : candidateParameters ) {
|
||||
if ( ( isTypeVarOrWildcard( parameter ) || candidate.getType().isAssignableTo( parameter.getType() ) )
|
||||
&& parameter.isMappingTarget() == candidate.isMappingTarget() && !parameter.isTargetType()
|
||||
&& !candidate.isTargetType() ) {
|
||||
result.add( candidate );
|
||||
}
|
||||
else if ( parameter.isTargetType() && candidate.isTargetType() ) {
|
||||
result.add( candidate );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isTypeVarOrWildcard(Parameter parameter) {
|
||||
TypeKind kind = parameter.getType().getTypeMirror().getKind();
|
||||
return kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD;
|
||||
}
|
||||
}
|
@ -43,13 +43,14 @@ import org.mapstruct.ap.internal.model.assignment.SetterWrapperForCollectionsAnd
|
||||
import org.mapstruct.ap.internal.model.assignment.UpdateWrapper;
|
||||
import org.mapstruct.ap.internal.model.common.ModelElement;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
|
||||
import org.mapstruct.ap.internal.model.source.FormattingParameters;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SourceReference;
|
||||
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
|
||||
import org.mapstruct.ap.internal.util.Executables;
|
||||
@ -566,7 +567,11 @@ public class PropertyMapping extends ModelElement {
|
||||
methodRef = new ForgedMethod( existingName, methodRef );
|
||||
}
|
||||
|
||||
assignment = new MethodReference( methodRef, null, targetType );
|
||||
assignment = new MethodReference(
|
||||
methodRef,
|
||||
null,
|
||||
ParameterBinding.fromParameters( methodRef.getParameters() ) );
|
||||
|
||||
assignment.setAssignment( source );
|
||||
|
||||
forgedMethods.addAll( iterableMappingMethod.getForgedMethods() );
|
||||
@ -608,7 +613,10 @@ public class PropertyMapping extends ModelElement {
|
||||
String existingName = ctx.getExistingMappingMethod( mapMappingMethod ).getName();
|
||||
methodRef = new ForgedMethod( existingName, methodRef );
|
||||
}
|
||||
assignment = new MethodReference( methodRef, null, targetType );
|
||||
assignment = new MethodReference(
|
||||
methodRef,
|
||||
null,
|
||||
ParameterBinding.fromParameters( methodRef.getParameters() ) );
|
||||
assignment.setAssignment( source );
|
||||
|
||||
forgedMethods.addAll( mapMappingMethod.getForgedMethods() );
|
||||
@ -635,7 +643,11 @@ public class PropertyMapping extends ModelElement {
|
||||
getForgedMethodHistory( sourceRHS )
|
||||
);
|
||||
|
||||
Assignment assignment = new MethodReference( forgedMethod, null, targetType );
|
||||
Assignment assignment = new MethodReference(
|
||||
forgedMethod,
|
||||
null,
|
||||
ParameterBinding.fromParameters( forgedMethod.getParameters() ) );
|
||||
|
||||
assignment.setAssignment( sourceRHS );
|
||||
|
||||
this.forgedMethods.add( forgedMethod );
|
||||
|
@ -34,20 +34,18 @@ public class Parameter extends ModelElement {
|
||||
private final Type type;
|
||||
private final boolean mappingTarget;
|
||||
private final boolean targetType;
|
||||
private final boolean mappingSource;
|
||||
|
||||
public Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingSource) {
|
||||
public Parameter(String name, Type type, boolean mappingTarget, boolean targetType) {
|
||||
// issue #909: FreeMarker doesn't like "values" as a parameter name
|
||||
this.name = "values".equals( name ) ? "values_" : name;
|
||||
this.originalName = name;
|
||||
this.type = type;
|
||||
this.mappingTarget = mappingTarget;
|
||||
this.targetType = targetType;
|
||||
this.mappingSource = mappingSource;
|
||||
}
|
||||
|
||||
public Parameter(String name, Type type) {
|
||||
this( name, type, false, false, false );
|
||||
this( name, type, false, false );
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -62,10 +60,6 @@ public class Parameter extends ModelElement {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isMappingSource() {
|
||||
return mappingSource;
|
||||
}
|
||||
|
||||
public boolean isMappingTarget() {
|
||||
return mappingTarget;
|
||||
}
|
||||
@ -106,5 +100,4 @@ public class Parameter extends ModelElement {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents how one parameter of a method to be called is populated.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class ParameterBinding {
|
||||
|
||||
private final Type type;
|
||||
private final String variableName;
|
||||
private final boolean targetType;
|
||||
private final boolean mappingTarget;
|
||||
|
||||
private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType) {
|
||||
this.type = parameterType;
|
||||
this.variableName = variableName;
|
||||
this.targetType = targetType;
|
||||
this.mappingTarget = mappingTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the variable (or parameter) that is being used as argument for the parameter being bound.
|
||||
*/
|
||||
public String getVariableName() {
|
||||
return variableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true}, if the parameter being bound is a {@code @TargetType} parameter.
|
||||
*/
|
||||
public boolean isTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true}, if the parameter being bound is a {@code @MappingTarget} parameter.
|
||||
*/
|
||||
public boolean isMappingTarget() {
|
||||
return mappingTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type of the parameter that is bound
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Set<Type> getImportTypes() {
|
||||
if ( targetType ) {
|
||||
return type.getImportTypes();
|
||||
}
|
||||
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parameter parameter
|
||||
* @return a parameter binding reflecting the given parameter as being used as argument for a method call
|
||||
*/
|
||||
public static ParameterBinding fromParameter(Parameter parameter) {
|
||||
return new ParameterBinding(
|
||||
parameter.getType(),
|
||||
parameter.getName(),
|
||||
parameter.isMappingTarget(),
|
||||
parameter.isTargetType() );
|
||||
}
|
||||
|
||||
public static List<ParameterBinding> fromParameters(List<Parameter> parameters) {
|
||||
List<ParameterBinding> result = new ArrayList<ParameterBinding>( parameters.size() );
|
||||
for ( Parameter param : parameters ) {
|
||||
result.add( fromParameter( param ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classTypeOf the type representing {@code Class<X>} for the target type {@code X}
|
||||
* @return a parameter binding representing a target type parameter
|
||||
*/
|
||||
public static ParameterBinding forTargetTypeBinding(Type classTypeOf) {
|
||||
return new ParameterBinding( classTypeOf, null, false, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resultType type of the mapping target
|
||||
* @return a parameter binding representing a mapping target parameter
|
||||
*/
|
||||
public static ParameterBinding forMappingTargetBinding(Type resultType) {
|
||||
return new ParameterBinding( resultType, null, true, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sourceType type of the parameter
|
||||
* @return a parameter binding representing a mapping source type
|
||||
*/
|
||||
public static ParameterBinding forSourceTypeBinding(Type sourceType) {
|
||||
return new ParameterBinding( sourceType, null, false, false );
|
||||
}
|
||||
}
|
@ -754,11 +754,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( ( name == null ) ? 0 : name.hashCode() );
|
||||
result = prime * result + ( ( packageName == null ) ? 0 : packageName.hashCode() );
|
||||
return result;
|
||||
return typeMirror.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -787,7 +783,6 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return typeMirror.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return an identification that can be used as part in a forged method name.
|
||||
|
@ -326,8 +326,7 @@ public class TypeFactory {
|
||||
parameter.getSimpleName().toString(),
|
||||
getType( parameterType ),
|
||||
MappingTargetPrism.getInstanceOn( parameter ) != null,
|
||||
TargetTypePrism.getInstanceOn( parameter ) != null,
|
||||
false) );
|
||||
TargetTypePrism.getInstanceOn( parameter ) != null ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -116,7 +116,7 @@ public class ForgedMethod implements Method {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !first( sourceTypes ).equals( parameters.get( 0 ).getType() ) ) {
|
||||
if ( !first( sourceTypes ).equals( first( parameters ).getType() ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.source;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.hasNonNullElements;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -90,27 +88,18 @@ public class MethodMatcher {
|
||||
// check & collect generic types.
|
||||
Map<TypeVariable, TypeMirror> genericTypesMap = new HashMap<TypeVariable, TypeMirror>();
|
||||
|
||||
if ( hasNonNullElements( sourceTypes ) ) {
|
||||
// if sourceTypes contains non-null elements then only methods with all source parameters matching qualify
|
||||
if ( candidateMethod.getSourceParameters().size() == sourceTypes.size() ) {
|
||||
int i = 0;
|
||||
for ( Parameter candidateSourceParam : candidateMethod.getSourceParameters() ) {
|
||||
Type sourceType = sourceTypes.get( i++ );
|
||||
if ( sourceType == null
|
||||
|| !matchSourceType( sourceType, candidateSourceParam.getType(), genericTypesMap ) ) {
|
||||
return false;
|
||||
}
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if the sourceTypes empty/contains only nulls then only factory and lifecycle methods qualify
|
||||
if ( !candidateMethod.isObjectFactory() && !candidateMethod.isLifecycleCallbackMethod() ) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the method matches the proper result type to construct
|
||||
|
@ -42,14 +42,19 @@ import org.mapstruct.ap.internal.model.source.Method;
|
||||
public class CreateOrUpdateSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
|
||||
Type sourceType, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
List<T> createCandidates = new ArrayList<T>();
|
||||
List<T> updateCandidates = new ArrayList<T>();
|
||||
for ( T method : methods ) {
|
||||
boolean isCreateCandidate = method.getMappingTargetParameter() == null;
|
||||
if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired() ) {
|
||||
return methods;
|
||||
}
|
||||
|
||||
List<SelectedMethod<T>> createCandidates = new ArrayList<SelectedMethod<T>>();
|
||||
List<SelectedMethod<T>> updateCandidates = new ArrayList<SelectedMethod<T>>();
|
||||
for ( SelectedMethod<T> method : methods ) {
|
||||
boolean isCreateCandidate = method.getMethod().getMappingTargetParameter() == null;
|
||||
if ( isCreateCandidate ) {
|
||||
createCandidates.add( method );
|
||||
}
|
||||
|
@ -35,26 +35,26 @@ import org.mapstruct.ap.internal.model.source.Method;
|
||||
public class InheritanceSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(
|
||||
Method mappingMethod,
|
||||
List<T> methods,
|
||||
Type sourceType,
|
||||
Type targetType,
|
||||
SelectionCriteria criteria
|
||||
) {
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
if ( sourceType == null ) {
|
||||
if ( sourceTypes.size() != 1 ) {
|
||||
return methods;
|
||||
}
|
||||
|
||||
List<T> candidatesWithBestMatchingSourceType = new ArrayList<T>();
|
||||
Type singleSourceType = first( sourceTypes );
|
||||
|
||||
List<SelectedMethod<T>> candidatesWithBestMatchingSourceType = new ArrayList<SelectedMethod<T>>();
|
||||
int bestMatchingSourceTypeDistance = Integer.MAX_VALUE;
|
||||
|
||||
// find the methods with the minimum distance regarding getParameter getParameter type
|
||||
for ( T method : methods ) {
|
||||
Parameter singleSourceParam = first( method.getSourceParameters() );
|
||||
for ( SelectedMethod<T> method : methods ) {
|
||||
Parameter singleSourceParam = first( method.getMethod().getSourceParameters() );
|
||||
|
||||
int sourceTypeDistance = sourceType.distanceTo( singleSourceParam.getType() );
|
||||
int sourceTypeDistance = singleSourceType.distanceTo( singleSourceParam.getType() );
|
||||
bestMatchingSourceTypeDistance =
|
||||
addToCandidateListIfMinimal(
|
||||
candidatesWithBestMatchingSourceType,
|
||||
@ -66,8 +66,8 @@ public class InheritanceSelector implements MethodSelector {
|
||||
return candidatesWithBestMatchingSourceType;
|
||||
}
|
||||
|
||||
private <T extends Method> int addToCandidateListIfMinimal(List<T> candidatesWithBestMathingType,
|
||||
int bestMatchingTypeDistance, T method,
|
||||
private <T extends Method> int addToCandidateListIfMinimal(List<SelectedMethod<T>> candidatesWithBestMathingType,
|
||||
int bestMatchingTypeDistance, SelectedMethod<T> method,
|
||||
int currentTypeDistance) {
|
||||
if ( currentTypeDistance == bestMatchingTypeDistance ) {
|
||||
candidatesWithBestMathingType.add( method );
|
||||
|
@ -23,23 +23,26 @@ import java.util.List;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.MethodMatcher;
|
||||
|
||||
/**
|
||||
* Selects those methods from the given input set which match whether a factory was requested ({@link MethodMatcher} and
|
||||
* {@link org.mapstruct.ObjectFactory}).
|
||||
* Selects those methods from the given input set which match for the requested family of methods: factory methods,
|
||||
* lifecycle callback methods, or any other mapping methods.
|
||||
*
|
||||
* @author Remo Meier
|
||||
*/
|
||||
public class ObjectFactorySelector implements MethodSelector {
|
||||
public class MethodFamilySelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type sourceType,
|
||||
Type targetType, SelectionCriteria criteria) {
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes,
|
||||
Type targetType, SelectionCriteria criteria) {
|
||||
|
||||
List<SelectedMethod<T>> result = new ArrayList<SelectedMethod<T>>( methods.size() );
|
||||
for ( SelectedMethod<T> method : methods ) {
|
||||
if ( method.getMethod().isObjectFactory() == criteria.isObjectFactoryRequired()
|
||||
&& method.getMethod().isLifecycleCallbackMethod() == criteria.isLifecycleCallbackRequired() ) {
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
for ( T method : methods ) {
|
||||
if ( method.isObjectFactory() == criteria.isObjectFactoryRequired() ) {
|
||||
result.add( method );
|
||||
}
|
||||
}
|
@ -30,20 +30,21 @@ import org.mapstruct.ap.internal.model.source.Method;
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public interface MethodSelector {
|
||||
interface MethodSelector {
|
||||
|
||||
/**
|
||||
* Selects those methods which match the given types and other criteria
|
||||
*
|
||||
* @param <T> either SourceMethod or BuiltInMethod
|
||||
* @param mappingMethod mapping method, defined in Mapper for which this selection is carried out
|
||||
* @param methods list of available methods
|
||||
* @param sourceType parameter type that should be matched
|
||||
* @param targetType return type that should be matched
|
||||
* @param candidates list of available methods
|
||||
* @param sourceTypes parameter type(s) that should be matched
|
||||
* @param targetType result 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<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type sourceType,
|
||||
Type targetType, SelectionCriteria criteria);
|
||||
<T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> candidates,
|
||||
List<Type> sourceTypes,
|
||||
Type targetType, SelectionCriteria criteria);
|
||||
}
|
||||
|
@ -34,38 +34,48 @@ import org.mapstruct.ap.internal.model.source.Method;
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class MethodSelectors implements MethodSelector {
|
||||
public class MethodSelectors {
|
||||
|
||||
private final List<MethodSelector> selectors;
|
||||
|
||||
public MethodSelectors(Types typeUtils, Elements elementUtils, TypeFactory typeFactory) {
|
||||
selectors =
|
||||
Arrays.<MethodSelector>asList(
|
||||
new ObjectFactorySelector(),
|
||||
new TypeSelector(),
|
||||
new QualifierSelector( typeUtils, elementUtils ),
|
||||
new TargetTypeSelector( typeUtils, elementUtils ),
|
||||
new XmlElementDeclSelector( typeUtils, elementUtils ),
|
||||
new InheritanceSelector(),
|
||||
new CreateOrUpdateSelector()
|
||||
);
|
||||
selectors = Arrays.asList(
|
||||
new MethodFamilySelector(),
|
||||
new TypeSelector( typeFactory ),
|
||||
new QualifierSelector( typeUtils, elementUtils ),
|
||||
new TargetTypeSelector( typeUtils, elementUtils ),
|
||||
new XmlElementDeclSelector( typeUtils, elementUtils ),
|
||||
new InheritanceSelector(),
|
||||
new CreateOrUpdateSelector() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
|
||||
Type sourceType, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
/**
|
||||
* Selects those methods which match the given types and other criteria
|
||||
*
|
||||
* @param <T> either SourceMethod or BuiltInMethod
|
||||
* @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 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) {
|
||||
|
||||
List<T> candidates = new ArrayList<T>( methods );
|
||||
List<SelectedMethod<T>> candidates = new ArrayList<SelectedMethod<T>>( methods.size() );
|
||||
for ( T method : methods ) {
|
||||
candidates.add( new SelectedMethod<T>( method ) );
|
||||
}
|
||||
|
||||
for ( MethodSelector selector : selectors ) {
|
||||
candidates = selector.getMatchingMethods(
|
||||
mappingMethod,
|
||||
candidates,
|
||||
sourceType,
|
||||
sourceTypes,
|
||||
targetType,
|
||||
criteria
|
||||
);
|
||||
criteria );
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
@ -63,9 +63,10 @@ public class QualifierSelector implements MethodSelector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
|
||||
Type sourceType, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
int numberOfQualifiersToMatch = 0;
|
||||
|
||||
@ -89,11 +90,11 @@ public class QualifierSelector implements MethodSelector {
|
||||
// Check there are qualfiers for this mapping: Mapping#qualifier or Mapping#qualfiedByName
|
||||
if ( qualifierTypes.isEmpty() ) {
|
||||
// When no qualifiers, disqualify all methods marked with a qualifier by removing them from the candidates
|
||||
List<T> nonQualiferAnnotatedMethods = new ArrayList<T>();
|
||||
for ( T candidate : methods ) {
|
||||
List<SelectedMethod<T>> nonQualiferAnnotatedMethods = new ArrayList<SelectedMethod<T>>( methods.size() );
|
||||
for ( SelectedMethod<T> candidate : methods ) {
|
||||
|
||||
if ( candidate instanceof SourceMethod ) {
|
||||
Set<AnnotationMirror> qualifierAnnotations = getQualifierAnnotationMirrors( candidate );
|
||||
if ( candidate.getMethod() instanceof SourceMethod ) {
|
||||
Set<AnnotationMirror> qualifierAnnotations = getQualifierAnnotationMirrors( candidate.getMethod() );
|
||||
if ( qualifierAnnotations.isEmpty() ) {
|
||||
nonQualiferAnnotatedMethods.add( candidate );
|
||||
}
|
||||
@ -107,15 +108,16 @@ public class QualifierSelector implements MethodSelector {
|
||||
}
|
||||
else {
|
||||
// Check all methods marked with qualfier (or methods in Mappers marked wiht a qualfier) for matches.
|
||||
List<T> matches = new ArrayList<T>();
|
||||
for ( T candidate : methods ) {
|
||||
List<SelectedMethod<T>> matches = new ArrayList<SelectedMethod<T>>( methods.size() );
|
||||
for ( SelectedMethod<T> candidate : methods ) {
|
||||
|
||||
if ( !( candidate instanceof SourceMethod ) ) {
|
||||
if ( !( candidate.getMethod() instanceof SourceMethod ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// retrieve annotations
|
||||
Set<AnnotationMirror> qualifierAnnotationMirrors = getQualifierAnnotationMirrors( candidate );
|
||||
Set<AnnotationMirror> qualifierAnnotationMirrors =
|
||||
getQualifierAnnotationMirrors( candidate.getMethod() );
|
||||
|
||||
// now count if all qualifiers are matched
|
||||
int matchingQualifierCounter = 0;
|
||||
|
@ -16,28 +16,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
package org.mapstruct.ap.internal.model.source.selector;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
|
||||
/**
|
||||
* Represents a reference to a factory method.
|
||||
* A selected method with additional metadata that might be required for further usage of the selected method.
|
||||
*
|
||||
* @author Remo Meier
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class ObjectFactoryMethodReference extends MethodReference {
|
||||
public class SelectedMethod<T extends Method> {
|
||||
private T method;
|
||||
private List<ParameterBinding> parameterBindings;
|
||||
|
||||
private final List<Parameter> parameterAssignments;
|
||||
|
||||
public ObjectFactoryMethodReference(Method method, MapperReference ref, List<Parameter> parameterAssignments) {
|
||||
super( method, ref, null );
|
||||
this.parameterAssignments = parameterAssignments;
|
||||
public SelectedMethod(T method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public List<Parameter> getParameterAssignments() {
|
||||
return parameterAssignments;
|
||||
public T getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public List<ParameterBinding> getParameterBindings() {
|
||||
return parameterBindings;
|
||||
}
|
||||
|
||||
public void setParameterBindings(List<ParameterBinding> parameterBindings) {
|
||||
this.parameterBindings = parameterBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return method.toString();
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
|
||||
/**
|
||||
@ -37,9 +38,11 @@ public class SelectionCriteria {
|
||||
private final TypeMirror qualifyingResultType;
|
||||
private boolean preferUpdateMapping;
|
||||
private final boolean objectFactoryRequired;
|
||||
private final boolean lifecycleCallbackRequired;
|
||||
|
||||
public SelectionCriteria(SelectionParameters selectionParameters, String targetPropertyName,
|
||||
boolean preferUpdateMapping, boolean objectFactoryRequired) {
|
||||
boolean preferUpdateMapping, boolean objectFactoryRequired,
|
||||
boolean lifecycleCallbackRequired) {
|
||||
if ( selectionParameters != null ) {
|
||||
qualifiers.addAll( selectionParameters.getQualifiers() );
|
||||
qualifiedByNames.addAll( selectionParameters.getQualifyingNames() );
|
||||
@ -51,6 +54,7 @@ public class SelectionCriteria {
|
||||
this.targetPropertyName = targetPropertyName;
|
||||
this.preferUpdateMapping = preferUpdateMapping;
|
||||
this.objectFactoryRequired = objectFactoryRequired;
|
||||
this.lifecycleCallbackRequired = lifecycleCallbackRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,6 +64,13 @@ public class SelectionCriteria {
|
||||
return objectFactoryRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if lifecycle callback methods should be selected, false otherwise.
|
||||
*/
|
||||
public boolean isLifecycleCallbackRequired() {
|
||||
return lifecycleCallbackRequired;
|
||||
}
|
||||
|
||||
public List<TypeMirror> getQualifiers() {
|
||||
return qualifiers;
|
||||
}
|
||||
@ -84,4 +95,17 @@ public class SelectionCriteria {
|
||||
this.preferUpdateMapping = preferUpdateMapping;
|
||||
}
|
||||
|
||||
public static SelectionCriteria forMappingMethods(SelectionParameters selectionParameters,
|
||||
String targetPropertyName, boolean preferUpdateMapping) {
|
||||
|
||||
return new SelectionCriteria( selectionParameters, targetPropertyName, preferUpdateMapping, false, false );
|
||||
}
|
||||
|
||||
public static SelectionCriteria forFactoryMethods(SelectionParameters selectionParameters) {
|
||||
return new SelectionCriteria( selectionParameters, null, false, true, false );
|
||||
}
|
||||
|
||||
public static SelectionCriteria forLifecycleMethods(SelectionParameters selectionParameters) {
|
||||
return new SelectionCriteria( selectionParameters, null, false, false, true );
|
||||
}
|
||||
}
|
||||
|
@ -45,16 +45,19 @@ public class TargetTypeSelector implements MethodSelector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
|
||||
Type sourceType, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
TypeMirror qualifyingTypeMirror = criteria.getQualifyingResultType();
|
||||
if ( qualifyingTypeMirror != null ) {
|
||||
if ( qualifyingTypeMirror != null && !criteria.isLifecycleCallbackRequired() ) {
|
||||
|
||||
List<T> candidatesWithQualifyingTargetType = new ArrayList<T>();
|
||||
for ( T method : methods ) {
|
||||
TypeMirror resultTypeMirror = method.getResultType().getTypeElement().asType();
|
||||
List<SelectedMethod<T>> candidatesWithQualifyingTargetType =
|
||||
new ArrayList<SelectedMethod<T>>( methods.size() );
|
||||
|
||||
for ( SelectedMethod<T> method : methods ) {
|
||||
TypeMirror resultTypeMirror = method.getMethod().getResultType().getTypeElement().asType();
|
||||
if ( typeUtils.isSameType( qualifyingTypeMirror, resultTypeMirror ) ) {
|
||||
candidatesWithQualifyingTargetType.add( method );
|
||||
}
|
||||
|
@ -18,11 +18,15 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.source.selector;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
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.model.source.MethodMatcher;
|
||||
|
||||
@ -34,17 +38,168 @@ import org.mapstruct.ap.internal.model.source.MethodMatcher;
|
||||
*/
|
||||
public class TypeSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
|
||||
Type sourceType, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
private TypeFactory typeFactory;
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
for ( T method : methods ) {
|
||||
if ( !method.isLifecycleCallbackMethod() && method.matches( Arrays.asList( sourceType ), targetType ) ) {
|
||||
result.add( method );
|
||||
public TypeSelector(TypeFactory typeFactory) {
|
||||
this.typeFactory = typeFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
if ( methods.isEmpty() ) {
|
||||
return methods;
|
||||
}
|
||||
|
||||
List<SelectedMethod<T>> result = new ArrayList<SelectedMethod<T>>();
|
||||
|
||||
List<ParameterBinding> availableBindings;
|
||||
if ( sourceTypes.isEmpty() ) {
|
||||
// if no source types are given, we have a factory or lifecycle method
|
||||
availableBindings = getAvailableParameterBindingsFromMethod( mappingMethod );
|
||||
}
|
||||
else {
|
||||
availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType );
|
||||
}
|
||||
|
||||
for ( SelectedMethod<T> method : methods ) {
|
||||
List<List<ParameterBinding>> parameterBindingPermutations =
|
||||
getCandidateParameterBindingPermutations( availableBindings, method.getMethod().getParameters() );
|
||||
|
||||
if ( parameterBindingPermutations != null ) {
|
||||
SelectedMethod<T> matchingMethod =
|
||||
getFirstMatchingParameterBinding( targetType, method, parameterBindingPermutations );
|
||||
|
||||
if ( matchingMethod != null ) {
|
||||
result.add( matchingMethod );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ParameterBinding> getAvailableParameterBindingsFromMethod(Method method) {
|
||||
List<ParameterBinding> availableParams = new ArrayList<ParameterBinding>( method.getParameters().size() + 2 );
|
||||
|
||||
availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) );
|
||||
addMappingTargetAndTargetTypeBindings( availableParams, method.getResultType() );
|
||||
|
||||
return availableParams;
|
||||
}
|
||||
|
||||
private List<ParameterBinding> getAvailableParameterBindingsFromSourceTypes(List<Type> sourceTypes,
|
||||
Type targetType) {
|
||||
|
||||
List<ParameterBinding> availableParams = new ArrayList<ParameterBinding>( sourceTypes.size() + 2 );
|
||||
|
||||
addMappingTargetAndTargetTypeBindings( availableParams, targetType );
|
||||
|
||||
for ( Type sourceType : sourceTypes ) {
|
||||
availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) );
|
||||
}
|
||||
|
||||
return availableParams;
|
||||
}
|
||||
|
||||
private void addMappingTargetAndTargetTypeBindings(List<ParameterBinding> availableParams, Type targetType) {
|
||||
availableParams.add( ParameterBinding.forMappingTargetBinding( targetType ) );
|
||||
availableParams.add( ParameterBinding.forTargetTypeBinding( typeFactory.classTypeOf( targetType ) ) );
|
||||
}
|
||||
|
||||
private <T extends Method> SelectedMethod<T> getFirstMatchingParameterBinding(Type targetType,
|
||||
SelectedMethod<T> method, List<List<ParameterBinding>> parameterAssignmentVariants) {
|
||||
|
||||
for ( List<ParameterBinding> parameterAssignments : parameterAssignmentVariants ) {
|
||||
if ( method.getMethod().matches( extractTypes( parameterAssignments ), targetType ) ) {
|
||||
method.setParameterBindings( parameterAssignments );
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param availableParams parameter bindings available in the scope of the method call
|
||||
* @param methodParameters parameters of the method that is inspected
|
||||
* @return all parameter binding permutations for which proper type checks need to be conducted.
|
||||
*/
|
||||
private static List<List<ParameterBinding>> getCandidateParameterBindingPermutations(
|
||||
List<ParameterBinding> availableParams,
|
||||
List<Parameter> methodParameters) {
|
||||
|
||||
if ( methodParameters.size() > availableParams.size() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<List<ParameterBinding>> bindingPermutations = new ArrayList<List<ParameterBinding>>( 1 );
|
||||
bindingPermutations.add( new ArrayList<ParameterBinding>( methodParameters.size() ) );
|
||||
|
||||
for ( Parameter methodParam : methodParameters ) {
|
||||
List<ParameterBinding> candidateBindings =
|
||||
findCandidateBindingsForParameter( availableParams, methodParam );
|
||||
|
||||
if ( candidateBindings.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( candidateBindings.size() == 1 ) {
|
||||
// short-cut to avoid list-copies for the usual case where only one binding fits
|
||||
for ( List<ParameterBinding> variant : bindingPermutations ) {
|
||||
// add binding to each existing variant
|
||||
variant.add( first( candidateBindings ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
List<List<ParameterBinding>> newVariants =
|
||||
new ArrayList<List<ParameterBinding>>( bindingPermutations.size() * candidateBindings.size() );
|
||||
for ( List<ParameterBinding> variant : bindingPermutations ) {
|
||||
// create a copy of each variant for each binding
|
||||
for ( ParameterBinding binding : candidateBindings ) {
|
||||
List<ParameterBinding> extendedVariant =
|
||||
new ArrayList<ParameterBinding>( methodParameters.size() );
|
||||
extendedVariant.addAll( variant );
|
||||
extendedVariant.add( binding );
|
||||
|
||||
newVariants.add( extendedVariant );
|
||||
}
|
||||
}
|
||||
|
||||
bindingPermutations = newVariants;
|
||||
}
|
||||
}
|
||||
|
||||
return bindingPermutations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param candidateParameters available for assignment.
|
||||
* @param parameter that need assignment from one of the candidate parameter bindings.
|
||||
* @return list of candidate parameter bindings that might be assignable.
|
||||
*/
|
||||
private static List<ParameterBinding> findCandidateBindingsForParameter(List<ParameterBinding> candidateParameters,
|
||||
Parameter parameter) {
|
||||
List<ParameterBinding> result = new ArrayList<ParameterBinding>( candidateParameters.size() );
|
||||
|
||||
for ( ParameterBinding candidate : candidateParameters ) {
|
||||
if ( parameter.isTargetType() == candidate.isTargetType()
|
||||
&& parameter.isMappingTarget() == candidate.isMappingTarget() ) {
|
||||
result.add( candidate );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Type> extractTypes(List<ParameterBinding> parameters) {
|
||||
List<Type> result = new ArrayList<Type>( parameters.size() );
|
||||
|
||||
for ( ParameterBinding param : parameters ) {
|
||||
result.add( param.getType() );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -61,9 +61,10 @@ public class XmlElementDeclSelector implements MethodSelector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
|
||||
Type sourceType, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type> sourceTypes, Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
|
||||
// only true source methods are qualifying
|
||||
if ( !(mappingMethod instanceof SourceMethod) ) {
|
||||
@ -72,18 +73,18 @@ public class XmlElementDeclSelector implements MethodSelector {
|
||||
|
||||
SourceMethod sourceMappingMethod = (SourceMethod) mappingMethod;
|
||||
|
||||
List<T> nameMatches = new ArrayList<T>();
|
||||
List<T> scopeMatches = new ArrayList<T>();
|
||||
List<T> nameAndScopeMatches = new ArrayList<T>();
|
||||
List<SelectedMethod<T>> nameMatches = new ArrayList<SelectedMethod<T>>();
|
||||
List<SelectedMethod<T>> scopeMatches = new ArrayList<SelectedMethod<T>>();
|
||||
List<SelectedMethod<T>> nameAndScopeMatches = new ArrayList<SelectedMethod<T>>();
|
||||
XmlElementRefInfo xmlElementRefInfo =
|
||||
findXmlElementRef( sourceMappingMethod.getResultType(), criteria.getTargetPropertyName() );
|
||||
|
||||
for ( T candidate : methods ) {
|
||||
if ( !( candidate instanceof SourceMethod ) ) {
|
||||
for ( SelectedMethod<T> candidate : methods ) {
|
||||
if ( !( candidate.getMethod() instanceof SourceMethod ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SourceMethod candidateMethod = (SourceMethod) candidate;
|
||||
SourceMethod candidateMethod = (SourceMethod) candidate.getMethod();
|
||||
XmlElementDeclPrism xmlElememtDecl = XmlElementDeclPrism.getInstanceOn( candidateMethod.getExecutable() );
|
||||
|
||||
if ( xmlElememtDecl == null ) {
|
||||
|
@ -18,6 +18,9 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.processor.creation;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -39,23 +42,20 @@ import org.mapstruct.ap.internal.model.HelperMethod;
|
||||
import org.mapstruct.ap.internal.model.MapperReference;
|
||||
import org.mapstruct.ap.internal.model.MappingBuilderContext.MappingResolver;
|
||||
import org.mapstruct.ap.internal.model.MethodReference;
|
||||
import org.mapstruct.ap.internal.model.ObjectFactoryMethodReference;
|
||||
import org.mapstruct.ap.internal.model.ParameterAssignmentUtil;
|
||||
import org.mapstruct.ap.internal.model.SourceRHS;
|
||||
import org.mapstruct.ap.internal.model.VirtualMappingMethod;
|
||||
import org.mapstruct.ap.internal.model.assignment.Assignment;
|
||||
import org.mapstruct.ap.internal.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.internal.model.common.DefaultConversionContext;
|
||||
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.model.source.FormattingParameters;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMappingMethods;
|
||||
import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||
import org.mapstruct.ap.internal.util.Collections;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
@ -109,7 +109,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
boolean preferUpdateMapping) {
|
||||
|
||||
SelectionCriteria criteria =
|
||||
new SelectionCriteria( selectionParameters, targetPropertyName, preferUpdateMapping, false );
|
||||
SelectionCriteria.forMappingMethods( selectionParameters, targetPropertyName, preferUpdateMapping );
|
||||
|
||||
String dateFormat = null;
|
||||
String numberFormat = null;
|
||||
@ -139,58 +139,46 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
public MethodReference getFactoryMethod(final Method mappingMethod, Type targetType,
|
||||
SelectionParameters selectionParameters) {
|
||||
|
||||
SelectionCriteria criteria = new SelectionCriteria( selectionParameters, null, false, true );
|
||||
List<SelectedMethod<Method>> matchingFactoryMethods =
|
||||
methodSelectors.getMatchingMethods(
|
||||
mappingMethod,
|
||||
sourceModel,
|
||||
java.util.Collections.<Type> emptyList(),
|
||||
targetType,
|
||||
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
||||
|
||||
ResolvingAttempt attempt = new ResolvingAttempt( sourceModel, mappingMethod, null, null, null, criteria );
|
||||
|
||||
List<Method> matchingSourceMethods = attempt.getMatches( sourceModel, null, targetType );
|
||||
|
||||
List<MethodReference> factoryRefsWithAssigments = new ArrayList<MethodReference>();
|
||||
List<Method> factoryRefSources = new ArrayList<Method>();
|
||||
|
||||
for ( Method matchingSourceMethod : matchingSourceMethods ) {
|
||||
|
||||
if ( matchingSourceMethod != null ) {
|
||||
MapperReference ref = attempt.findMapperReference( matchingSourceMethod );
|
||||
|
||||
if ( matchingSourceMethod.getSourceParameters().isEmpty() ) {
|
||||
// factory taking no argument
|
||||
factoryRefsWithAssigments.add( new MethodReference( matchingSourceMethod, ref, null ) );
|
||||
factoryRefSources.add( matchingSourceMethod );
|
||||
}
|
||||
else {
|
||||
// check whether factory have has a valid assignment, if so, choose as candidate
|
||||
List<Parameter> availableParameters = new ArrayList<Parameter>();
|
||||
availableParameters.addAll( mappingMethod.getSourceParameters() );
|
||||
availableParameters.add(
|
||||
new Parameter( null,
|
||||
typeFactory.classTypeOf( targetType ),
|
||||
false, true, false ) );
|
||||
List<Parameter> factoryParamAssinment =
|
||||
ParameterAssignmentUtil.getParameterAssignments( availableParameters,
|
||||
matchingSourceMethod.getParameters() );
|
||||
if ( factoryParamAssinment != null ) {
|
||||
factoryRefSources.add( matchingSourceMethod );
|
||||
factoryRefsWithAssigments.add(
|
||||
new ObjectFactoryMethodReference( matchingSourceMethod, ref, factoryParamAssinment ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matchingFactoryMethods.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( factoryRefsWithAssigments.size() > 1 ) {
|
||||
if ( matchingFactoryMethods.size() > 1 ) {
|
||||
messager.printMessage(
|
||||
mappingMethod.getExecutable(),
|
||||
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
||||
targetType,
|
||||
Strings.join( factoryRefSources, ", " ) );
|
||||
}
|
||||
else if ( factoryRefsWithAssigments.size() == 1 ) {
|
||||
// factory methods with assignment are favored over the ones without any
|
||||
return factoryRefsWithAssigments.get( 0 );
|
||||
Strings.join( matchingFactoryMethods, ", " ) );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// no factory found
|
||||
SelectedMethod<Method> matchingFactoryMethod = first( matchingFactoryMethods );
|
||||
|
||||
MapperReference ref = findMapperReference( matchingFactoryMethod.getMethod() );
|
||||
|
||||
return new MethodReference(
|
||||
matchingFactoryMethod.getMethod(),
|
||||
ref,
|
||||
matchingFactoryMethod.getParameterBindings() );
|
||||
}
|
||||
|
||||
private MapperReference findMapperReference(Method method) {
|
||||
for ( MapperReference ref : mapperReferences ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
ref.setUsed( ref.isUsed() || !method.isStatic() );
|
||||
ref.setTypeRequiresImport( true );
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -315,7 +303,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
private Assignment resolveViaMethod(Type sourceType, Type targetType, boolean considerBuiltInMethods) {
|
||||
|
||||
// first try to find a matching source method
|
||||
Method matchingSourceMethod = getBestMatch( methods, sourceType, targetType );
|
||||
SelectedMethod<Method> matchingSourceMethod = getBestMatch( methods, sourceType, targetType );
|
||||
|
||||
if ( matchingSourceMethod != null ) {
|
||||
return getMappingMethodReference( matchingSourceMethod, targetType );
|
||||
@ -329,15 +317,15 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
}
|
||||
|
||||
private Assignment resolveViaBuiltInMethod(Type sourceType, Type targetType) {
|
||||
BuiltInMethod matchingBuiltInMethod =
|
||||
SelectedMethod<BuiltInMethod> matchingBuiltInMethod =
|
||||
getBestMatch( builtInMethods.getBuiltInMethods(), sourceType, targetType );
|
||||
|
||||
if ( matchingBuiltInMethod != null ) {
|
||||
virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod ) );
|
||||
virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod.getMethod() ) );
|
||||
ConversionContext ctx = new DefaultConversionContext( typeFactory, messager,
|
||||
sourceType,
|
||||
targetType, dateFormat, numberFormat);
|
||||
Assignment methodReference = new MethodReference( matchingBuiltInMethod, ctx );
|
||||
Assignment methodReference = new MethodReference( matchingBuiltInMethod.getMethod(), ctx );
|
||||
methodReference.setAssignment( sourceRHS );
|
||||
return methodReference;
|
||||
}
|
||||
@ -491,22 +479,12 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
&& !methodCandidate.isLifecycleCallbackMethod();
|
||||
}
|
||||
|
||||
private <T extends Method> List<T> getMatches(List<T> methods, Type sourceType, Type returnType) {
|
||||
return methodSelectors.getMatchingMethods(
|
||||
private <T extends Method> SelectedMethod<T> getBestMatch(List<T> methods, Type sourceType, Type returnType) {
|
||||
|
||||
List<SelectedMethod<T>> candidates = methodSelectors.getMatchingMethods(
|
||||
mappingMethod,
|
||||
methods,
|
||||
sourceType,
|
||||
returnType,
|
||||
selectionCriteria
|
||||
);
|
||||
}
|
||||
|
||||
private <T extends Method> T getBestMatch(List<T> methods, Type sourceType, Type returnType) {
|
||||
|
||||
List<T> candidates = methodSelectors.getMatchingMethods(
|
||||
mappingMethod,
|
||||
methods,
|
||||
sourceType,
|
||||
singletonList( sourceType ),
|
||||
returnType,
|
||||
selectionCriteria
|
||||
);
|
||||
@ -533,33 +511,23 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
}
|
||||
|
||||
if ( !candidates.isEmpty() ) {
|
||||
return candidates.get( 0 );
|
||||
return first( candidates );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Assignment getMappingMethodReference(Method method,
|
||||
private Assignment getMappingMethodReference(SelectedMethod<Method> method,
|
||||
Type targetType) {
|
||||
MapperReference mapperReference = findMapperReference( method );
|
||||
MapperReference mapperReference = findMapperReference( method.getMethod() );
|
||||
|
||||
return new MethodReference( method,
|
||||
return new MethodReference(
|
||||
method.getMethod(),
|
||||
mapperReference,
|
||||
SourceMethod.containsTargetTypeParameter( method.getParameters() ) ? targetType : null
|
||||
method.getParameterBindings()
|
||||
);
|
||||
}
|
||||
|
||||
private MapperReference findMapperReference(Method method) {
|
||||
for ( MapperReference ref : mapperReferences ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
ref.setUsed( !method.isStatic() );
|
||||
ref.setTypeRequiresImport( true );
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given source and target type are both a collection type or both a map type and the source value
|
||||
* can be propagated via a copy constructor.
|
||||
|
@ -22,10 +22,7 @@
|
||||
<#if hasReturnType()>
|
||||
<@includeModel object=methodResultType /> ${targetVariableName} =
|
||||
</#if>
|
||||
<#if declaringType??>${instanceVariableName}.</#if>${name}(
|
||||
<#list parameterAssignments as param>
|
||||
<#if param.targetType><@includeModel object=ext.targetType raw=true/>.class<#elseif param.mappingTarget>${ext.targetBeanName}<#else>${param.name}</#if><#if param_has_next>,<#else> </#if>
|
||||
</#list>);
|
||||
<#include 'MethodReference.ftl'>;
|
||||
</@compress>
|
||||
<#if hasReturnType()><#nt>
|
||||
if ( ${targetVariableName} != null ) {
|
||||
|
@ -28,18 +28,20 @@
|
||||
</#if>
|
||||
<#macro params>
|
||||
<@compress>
|
||||
${name}<#if (parameters?size > 0)>( <@arguments/> )<#else>()</#if>
|
||||
${name}<#if (parameterBindings?size > 0)>( <@arguments/> )<#else>()</#if>
|
||||
</@compress>
|
||||
</#macro>
|
||||
<#macro arguments>
|
||||
<#list parameters as param>
|
||||
<#list parameterBindings as param>
|
||||
<#if param.targetType>
|
||||
<#-- a class is passed on for casting, see @TargetType -->
|
||||
<@includeModel object=ext.targetType raw=true/>.class<#t>
|
||||
<#elseif param.mappingTarget>
|
||||
${ext.targetBeanName}.${ext.targetReadAccessorName}
|
||||
${ext.targetBeanName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}</#if><#t>
|
||||
<#elseif assignment??>
|
||||
<@_assignment/><#t>
|
||||
<#else>
|
||||
<@_assignment/>
|
||||
${param.variableName}<#t>
|
||||
</#if>
|
||||
<#if param_has_next>, </#if><#t>
|
||||
</#list>
|
||||
|
@ -1,46 +0,0 @@
|
||||
<#--
|
||||
|
||||
Copyright 2012-2016 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
and/or other contributors as indicated by the @authors tag. See the
|
||||
copyright.txt file in the distribution for a full listing of all
|
||||
contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<@compress single_line=true>
|
||||
<#-- method is either internal to the mapper class, or external (via uses) declaringMapper!=null -->
|
||||
<#if declaringMapper??><#if static><@includeModel object=declaringMapper.type/><#else>${mapperVariableName}</#if>.<@params/>
|
||||
<#-- method is referenced java8 static method in the mapper to implement (interface) -->
|
||||
<#elseif static><@includeModel object=definingType/>.<@params/>
|
||||
<#else>
|
||||
<@params/>
|
||||
</#if>
|
||||
<#macro params>
|
||||
<@compress>
|
||||
${name}<#if (parameterAssignments?size > 0)>( <@arguments/> )<#else>()</#if>
|
||||
</@compress>
|
||||
</#macro>
|
||||
<#macro arguments>
|
||||
<#list parameterAssignments as param>
|
||||
<#if param.targetType>
|
||||
<#-- a class is passed on for casting, see @TargetType -->
|
||||
<@includeModel object=ext.targetType raw=true/>.class<#t>
|
||||
<#elseif param.mappingTarget>
|
||||
${ext.targetBeanName}.${ext.targetReadAccessorName}()
|
||||
<#else>${param.name}</#if><#if param_has_next>, </#if><#t>
|
||||
</#list>
|
||||
<#-- context parameter, e.g. for builtin methods concerning date conversion -->
|
||||
<#if contextParam??>, ${contextParam}</#if><#t>
|
||||
</#macro>
|
||||
</@compress>
|
@ -73,7 +73,8 @@ public class AnnotationProcessorTestRunner extends ParentRunner<Runner> {
|
||||
|
||||
return Arrays.<Runner> asList(
|
||||
new InnerAnnotationProcessorRunner( klass, Compiler.JDK ),
|
||||
new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE ) );
|
||||
new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user