From b84a5df84ec370ae2955deb08d084159b23c7b75 Mon Sep 17 00:00:00 2001 From: sjaakd Date: Mon, 24 Feb 2014 21:58:31 +0100 Subject: [PATCH] #135 applied list of MethodSelectors idea. --- .../source/selector/InheritanceSelector.java | 80 ++++++++++++ .../source/selector/InitialSelector.java | 57 +++++++++ .../model/source/selector/MethodSelector.java | 50 ++++++++ .../source/selector/MethodSelectors.java | 69 ++++++++++ .../selector/XmlElementDeclSelector.java | 120 ++++++++++++++++++ .../ap/processor/MapperCreationProcessor.java | 91 ++----------- 6 files changed, 390 insertions(+), 77 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java create mode 100644 processor/src/main/java/org/mapstruct/ap/model/source/selector/InitialSelector.java create mode 100644 processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java create mode 100644 processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java create mode 100644 processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java new file mode 100644 index 000000000..e82ab9d81 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java @@ -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 List getMatchingMethods( + SourceMethod mappingMethod, + Iterable methods, + Type parameterType, + Type returnType, + String targetPropertyName + ) { + + List candidatesWithBestMatchingSourceType = new ArrayList(); + 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 int addToCandidateListIfMinimal(List 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; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/InitialSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InitialSelector.java new file mode 100644 index 000000000..ab50785a5 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InitialSelector.java @@ -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 List getMatchingMethods( + SourceMethod mappingMethod, + Iterable methods, + Type parameterType, + Type returnType, + String targetPropertyName + ) { + + List result = new ArrayList(); + for ( T method : methods ) { + if ( method.getSourceParameters().size() != 1 ) { + continue; + } + + if ( method.matches( parameterType, returnType ) ) { + result.add( method ); + } + } + return result; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java new file mode 100644 index 000000000..5fac5f521 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java @@ -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 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 + */ + List getMatchingMethods( + SourceMethod mappingMethod, + Iterable methods, + Type parameterType, + Type returnType, + String targetPropertyName + ); +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java new file mode 100644 index 000000000..fabe6cfbc --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java @@ -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 selectors = new ArrayList(); + + public MethodSelectors( Types typeUtils ) { + + selectors.add( new InitialSelector() ); + selectors.add( new InheritanceSelector() ); + selectors.add( new XmlElementDeclSelector( typeUtils ) ); + } + + @Override + public List getMatchingMethods( + SourceMethod mappingMethod, + Iterable methods, + Type parameterType, + Type returnType, + String targetPropertyName + ) { + + List candidates = new ArrayList(); + for (T method : methods) { + candidates.add( method ); + } + + for ( MethodSelector selector : selectors ) { + candidates = selector.getMatchingMethods( + mappingMethod, + candidates, + parameterType, + returnType, + targetPropertyName + ); + } + return candidates; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java new file mode 100644 index 000000000..bf94ce1b3 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java @@ -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 List getMatchingMethods( + SourceMethod mappingMethod, + Iterable methods, + Type parameterType, + Type returnType, + String targetPropertyName + ) { + + List noXmlDeclMatch = new ArrayList(); + List nameMatch = new ArrayList(); + List scopeMatch = new ArrayList(); + List nameAndScopeMatch = new ArrayList(); + + 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; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java index f69526f95..21948f4ee 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -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(); + this.methodSelectors = new MethodSelectors( typeUtils ); return getMapper( mapperTypeElement, sourceModel ); } @@ -954,47 +957,16 @@ public class MapperCreationProcessor implements ModelElementProcessor candidatesWithMathingTargetType = new ArrayList(); - for ( T method : methods ) { - if ( method.getSourceParameters().size() != 1 ) { - continue; - } - - if ( method.matches( parameterType, returnType ) ) { - candidatesWithMathingTargetType.add( method ); - } - } - - List candidatesWithBestMatchingSourceType = new ArrayList(); - 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 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 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 getBestMatchBasedOnXmlElementDecl( - List 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; - } - }