mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#135 applied list of MethodSelectors idea.
This commit is contained in:
parent
4d22ff7abd
commit
b84a5df84e
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright 2012-2014 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.model.source.selector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
|
||||
/**
|
||||
* Selects on inheritance distance, e.g. the amount of inheritance steps from the parameter type.
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class InheritanceSelector implements MethodSelector {
|
||||
|
||||
/**
|
||||
* {@inheritDoc} {@link MethodSelector}
|
||||
*/
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(
|
||||
SourceMethod mappingMethod,
|
||||
Iterable<T> methods,
|
||||
Type parameterType,
|
||||
Type returnType,
|
||||
String targetPropertyName
|
||||
) {
|
||||
|
||||
List<T> candidatesWithBestMatchingSourceType = new ArrayList<T>();
|
||||
int bestMatchingSourceTypeDistance = Integer.MAX_VALUE;
|
||||
|
||||
// find the methods with the minimum distance regarding getParameter getParameter type
|
||||
for (T method : methods ) {
|
||||
Parameter singleSourceParam = method.getSourceParameters().iterator().next();
|
||||
|
||||
int sourceTypeDistance = parameterType.distanceTo( singleSourceParam.getType() );
|
||||
bestMatchingSourceTypeDistance =
|
||||
addToCandidateListIfMinimal(
|
||||
candidatesWithBestMatchingSourceType,
|
||||
bestMatchingSourceTypeDistance,
|
||||
method,
|
||||
sourceTypeDistance
|
||||
);
|
||||
}
|
||||
return candidatesWithBestMatchingSourceType;
|
||||
}
|
||||
|
||||
private <T extends Method> int addToCandidateListIfMinimal(List<T> candidatesWithBestMathingType,
|
||||
int bestMatchingTypeDistance, T method,
|
||||
int currentTypeDistance) {
|
||||
if ( currentTypeDistance == bestMatchingTypeDistance ) {
|
||||
candidatesWithBestMathingType.add( method );
|
||||
}
|
||||
else if ( currentTypeDistance < bestMatchingTypeDistance ) {
|
||||
bestMatchingTypeDistance = currentTypeDistance;
|
||||
|
||||
candidatesWithBestMathingType.clear();
|
||||
candidatesWithBestMathingType.add( method );
|
||||
}
|
||||
return bestMatchingTypeDistance;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright 2012-2014 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.model.source.selector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
|
||||
/**
|
||||
* This class provides the initial set of methods {@link MethodMatcher}
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class InitialSelector implements MethodSelector {
|
||||
|
||||
/**
|
||||
* {@inheritDoc} {@link MethodSelector}
|
||||
*/
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(
|
||||
SourceMethod mappingMethod,
|
||||
Iterable<T> methods,
|
||||
Type parameterType,
|
||||
Type returnType,
|
||||
String targetPropertyName
|
||||
) {
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
for ( T method : methods ) {
|
||||
if ( method.getSourceParameters().size() != 1 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( method.matches( parameterType, returnType ) ) {
|
||||
result.add( method );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright 2012-2014 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.model.source.selector;
|
||||
|
||||
import java.util.List;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public interface MethodSelector {
|
||||
|
||||
/**
|
||||
* Selects a method
|
||||
*
|
||||
* @param <T> either SourceMethod or BuiltInMethod
|
||||
* @param mappingMethod mapping method, defined in Mapper for which this selection is carried out
|
||||
* @param methods set from available methods
|
||||
* @param parameterType parameter type that should be matched
|
||||
* @param returnType return type that should be matched
|
||||
* @param targetPropertyName some information can be derived from the target property
|
||||
* @return list of methods that passes the matching process
|
||||
*/
|
||||
<T extends Method> List<T> getMatchingMethods(
|
||||
SourceMethod mappingMethod,
|
||||
Iterable<T> methods,
|
||||
Type parameterType,
|
||||
Type returnType,
|
||||
String targetPropertyName
|
||||
);
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright 2012-2014 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.model.source.selector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.lang.model.util.Types;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class MethodSelectors implements MethodSelector {
|
||||
|
||||
private final List<MethodSelector> selectors = new ArrayList<MethodSelector>();
|
||||
|
||||
public MethodSelectors( Types typeUtils ) {
|
||||
|
||||
selectors.add( new InitialSelector() );
|
||||
selectors.add( new InheritanceSelector() );
|
||||
selectors.add( new XmlElementDeclSelector( typeUtils ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(
|
||||
SourceMethod mappingMethod,
|
||||
Iterable<T> methods,
|
||||
Type parameterType,
|
||||
Type returnType,
|
||||
String targetPropertyName
|
||||
) {
|
||||
|
||||
List<T> candidates = new ArrayList<T>();
|
||||
for (T method : methods) {
|
||||
candidates.add( method );
|
||||
}
|
||||
|
||||
for ( MethodSelector selector : selectors ) {
|
||||
candidates = selector.getMatchingMethods(
|
||||
mappingMethod,
|
||||
candidates,
|
||||
parameterType,
|
||||
returnType,
|
||||
targetPropertyName
|
||||
);
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright 2012-2014 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.model.source.selector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Types;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.prism.XmlElementDeclPrism;
|
||||
|
||||
/**
|
||||
* This class matches XmlElmentDecl annotation. Matching happens in the following order.
|
||||
* 1) Name and Scope matches
|
||||
* 2) Scope matches
|
||||
* 3) Name matches
|
||||
* 4) No match at all.
|
||||
*
|
||||
* If there are Name and Scope matches, only those will be returned, otherwise the next in line (scope matches), etc.
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class XmlElementDeclSelector implements MethodSelector {
|
||||
|
||||
private final Types typeUtils;
|
||||
|
||||
public XmlElementDeclSelector( Types typeUtils ) {
|
||||
this.typeUtils = typeUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} {@link MethodSelector}
|
||||
*/
|
||||
@Override
|
||||
public <T extends Method> List<T> getMatchingMethods(
|
||||
SourceMethod mappingMethod,
|
||||
Iterable<T> methods,
|
||||
Type parameterType,
|
||||
Type returnType,
|
||||
String targetPropertyName
|
||||
) {
|
||||
|
||||
List<T> noXmlDeclMatch = new ArrayList<T>();
|
||||
List<T> nameMatch = new ArrayList<T>();
|
||||
List<T> scopeMatch = new ArrayList<T>();
|
||||
List<T> nameAndScopeMatch = new ArrayList<T>();
|
||||
|
||||
for ( T candidate : methods ) {
|
||||
if ( candidate instanceof SourceMethod ) {
|
||||
SourceMethod candiateMethod = (SourceMethod) candidate;
|
||||
XmlElementDeclPrism xmlElememtDecl
|
||||
= XmlElementDeclPrism.getInstanceOn( candiateMethod.getExecutable() );
|
||||
if ( xmlElememtDecl != null ) {
|
||||
String name = xmlElememtDecl.name();
|
||||
TypeMirror scope = xmlElememtDecl.scope();
|
||||
TypeMirror target = mappingMethod.getExecutable().getReturnType();
|
||||
if ( ( scope != null ) && ( name != null ) ) {
|
||||
// both scope and name should match when both defined
|
||||
if ( name.equals( targetPropertyName ) && typeUtils.isSameType( scope, target ) ) {
|
||||
nameAndScopeMatch.add( candidate );
|
||||
}
|
||||
}
|
||||
else if ( ( scope == null ) && ( name != null ) ) {
|
||||
// name should match when defined
|
||||
if ( name.equals( targetPropertyName ) ) {
|
||||
nameMatch.add( candidate );
|
||||
}
|
||||
}
|
||||
else if ( ( scope != null ) && ( name == null ) ) {
|
||||
// scope should match when defined
|
||||
if ( typeUtils.isSameType( scope, target ) ) {
|
||||
scopeMatch.add( candidate );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cannot make verdict based on scope or name, so add
|
||||
noXmlDeclMatch.add( candidate );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cannot a verdict on xmldeclannotation, so add
|
||||
noXmlDeclMatch.add( candidate );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cannot a verdict on xmldeclannotation, so add
|
||||
noXmlDeclMatch.add( candidate );
|
||||
}
|
||||
}
|
||||
|
||||
if ( nameAndScopeMatch.size() > 0 ) {
|
||||
return nameAndScopeMatch;
|
||||
}
|
||||
else if ( scopeMatch.size() > 0 ) {
|
||||
return scopeMatch;
|
||||
}
|
||||
else if ( nameMatch.size() > 0 ) {
|
||||
return nameMatch;
|
||||
}
|
||||
return noXmlDeclMatch;
|
||||
}
|
||||
}
|
@ -62,10 +62,10 @@ import org.mapstruct.ap.model.source.Method;
|
||||
import org.mapstruct.ap.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.model.source.builtin.BuiltInMappingMethods;
|
||||
import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
|
||||
import org.mapstruct.ap.model.source.selector.MethodSelectors;
|
||||
import org.mapstruct.ap.option.Options;
|
||||
import org.mapstruct.ap.option.ReportingPolicy;
|
||||
import org.mapstruct.ap.prism.MapperPrism;
|
||||
import org.mapstruct.ap.prism.XmlElementDeclPrism;
|
||||
import org.mapstruct.ap.util.Executables;
|
||||
import org.mapstruct.ap.util.Filters;
|
||||
import org.mapstruct.ap.util.Strings;
|
||||
@ -87,6 +87,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
private Conversions conversions;
|
||||
private BuiltInMappingMethods builtInMethods;
|
||||
|
||||
private MethodSelectors methodSelectors;
|
||||
|
||||
/**
|
||||
* Private methods which are not present in the original mapper interface and are added to map certain property
|
||||
* types.
|
||||
@ -105,6 +107,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
|
||||
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
||||
this.virtualMethods = new HashSet<VirtualMappingMethod>();
|
||||
this.methodSelectors = new MethodSelectors( typeUtils );
|
||||
|
||||
return getMapper( mapperTypeElement, sourceModel );
|
||||
}
|
||||
@ -954,47 +957,16 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
Type parameterType,
|
||||
Type returnType,
|
||||
String targetPropertyName) {
|
||||
List<T> candidatesWithMathingTargetType = new ArrayList<T>();
|
||||
|
||||
for ( T method : methods ) {
|
||||
if ( method.getSourceParameters().size() != 1 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( method.matches( parameterType, returnType ) ) {
|
||||
candidatesWithMathingTargetType.add( method );
|
||||
}
|
||||
}
|
||||
|
||||
List<T> candidatesWithBestMatchingSourceType = new ArrayList<T>();
|
||||
int bestMatchingSourceTypeDistance = Integer.MAX_VALUE;
|
||||
|
||||
// find the methods with the minimum distance regarding getParameter getParameter type
|
||||
for ( T method : candidatesWithMathingTargetType ) {
|
||||
Parameter singleSourceParam = method.getSourceParameters().iterator().next();
|
||||
|
||||
int sourceTypeDistance = parameterType.distanceTo( singleSourceParam.getType() );
|
||||
bestMatchingSourceTypeDistance =
|
||||
addToCandidateListIfMinimal(
|
||||
candidatesWithBestMatchingSourceType,
|
||||
bestMatchingSourceTypeDistance,
|
||||
method,
|
||||
sourceTypeDistance
|
||||
);
|
||||
}
|
||||
List<T> candidates = methodSelectors.getMatchingMethods(
|
||||
mappingMethod,
|
||||
methods,
|
||||
parameterType,
|
||||
returnType,
|
||||
targetPropertyName );
|
||||
|
||||
// print a warning if we find more than one method with minimum getParameter type distance
|
||||
if ( candidatesWithBestMatchingSourceType.size() > 1 ) {
|
||||
|
||||
// OK, we have more candidats. Lets see if we can use additional criteria to determine which is better
|
||||
T result = getBestMatchBasedOnXmlElementDecl(
|
||||
candidatesWithMathingTargetType,
|
||||
mappingMethod,
|
||||
targetPropertyName
|
||||
);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
if ( candidates.size() > 1 ) {
|
||||
|
||||
messager.printMessage(
|
||||
Kind.ERROR,
|
||||
@ -1002,14 +974,14 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
"Ambiguous mapping methods found for mapping " + mappedElement + " from %s to %s: %s.",
|
||||
parameterType,
|
||||
returnType,
|
||||
Strings.join( candidatesWithBestMatchingSourceType, ", " )
|
||||
Strings.join( candidates, ", " )
|
||||
),
|
||||
mappingMethod.getExecutable()
|
||||
);
|
||||
}
|
||||
|
||||
if ( !candidatesWithBestMatchingSourceType.isEmpty() ) {
|
||||
return candidatesWithBestMatchingSourceType.get( 0 );
|
||||
if ( !candidates.isEmpty() ) {
|
||||
return candidates.get( 0 );
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -1208,39 +1180,4 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
|
||||
return alternativeTargetAccessorsMethods;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method uses the {@link javax.xml.bind.annotation.XmlElementDecl } annotation to determine if the scope
|
||||
* of the mapping method matches the mapper method
|
||||
* @param <T> can be SourceMethod or BuildInType. Only SourceMethods qualify
|
||||
* @param candidates list with potential matches
|
||||
* @param mapperMethod mapper method for which this is analyzed
|
||||
* @param targetPropertyName the property name of the target
|
||||
* @return a method (always SourceMethod) when there's a scope that matches
|
||||
*/
|
||||
private <T extends Method> T getBestMatchBasedOnXmlElementDecl(
|
||||
List<T> candidates,
|
||||
SourceMethod mapperMethod,
|
||||
String targetPropertyName) {
|
||||
for (T candidate : candidates ) {
|
||||
if (candidate instanceof SourceMethod) {
|
||||
SourceMethod candiateMethod = (SourceMethod) candidate;
|
||||
XmlElementDeclPrism xmlElememtDecl =
|
||||
XmlElementDeclPrism.getInstanceOn( candiateMethod.getExecutable() );
|
||||
if ( xmlElememtDecl != null ) {
|
||||
String name = xmlElememtDecl.name();
|
||||
TypeMirror scope = xmlElememtDecl.scope();
|
||||
TypeMirror target = mapperMethod.getExecutable().getReturnType();
|
||||
if ( scope != null && typeUtils.isSameType( scope, target ) ) {
|
||||
if ( ( name != null ) && ( name.equals( targetPropertyName ) ) ) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user