mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1242 Fix favoring of a single factory method with source params before others without source params.
From the Javadoc of @ObjectFactory: If there are two factory methods, both serving the same type, one with no parameters and one taking sources as input, then the one with the source parameters is favored. If there are multiple such factories, an ambiguity error is shown.
This commit is contained in:
parent
3004ea28c5
commit
d9821a0cc8
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.source.selector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
|
||||
/**
|
||||
* For factory methods, the candidate list is checked if it contains a method with a source parameter which is to be
|
||||
* favored compared to factory methods without a source parameter. It returns the original list of candidates in case of
|
||||
* ambiguities.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class FactoryParameterSelector implements MethodSelector {
|
||||
|
||||
@Override
|
||||
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||
List<SelectedMethod<T>> methods,
|
||||
List<Type>sourceTypes,
|
||||
Type targetType,
|
||||
SelectionCriteria criteria) {
|
||||
if ( !criteria.isObjectFactoryRequired() || methods.size() <= 1 ) {
|
||||
return methods;
|
||||
}
|
||||
|
||||
List<SelectedMethod<T>> sourceParamFactoryMethods = new ArrayList<SelectedMethod<T>>( methods.size() );
|
||||
|
||||
for ( SelectedMethod<T> candidate : methods ) {
|
||||
if ( !candidate.getMethod().getSourceParameters().isEmpty() ) {
|
||||
sourceParamFactoryMethods.add( candidate );
|
||||
}
|
||||
}
|
||||
|
||||
if ( sourceParamFactoryMethods.size() == 1 ) {
|
||||
// there is exactly one candidate with source params, so favor that one.
|
||||
return sourceParamFactoryMethods;
|
||||
}
|
||||
|
||||
// let the caller produce an ambiguity error referencing all possibly matching methods
|
||||
return methods;
|
||||
}
|
||||
}
|
@ -46,7 +46,8 @@ public class MethodSelectors {
|
||||
new TargetTypeSelector( typeUtils, elementUtils ),
|
||||
new XmlElementDeclSelector( typeUtils, elementUtils ),
|
||||
new InheritanceSelector(),
|
||||
new CreateOrUpdateSelector() );
|
||||
new CreateOrUpdateSelector(),
|
||||
new FactoryParameterSelector() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ObjectFactory;
|
||||
|
||||
/**
|
||||
* Results in an ambiguous factory method error, as there are two methods with matching source types available.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
@Mapper(uses = TargetFactories.class)
|
||||
public abstract class ErroneousIssue1242MapperMultipleSources {
|
||||
abstract TargetA toTargetA(SourceA source);
|
||||
|
||||
abstract TargetB toTargetB(SourceB source);
|
||||
|
||||
@ObjectFactory
|
||||
protected TargetB anotherTargetBCreator(SourceB source) {
|
||||
throw new RuntimeException( "never to be called" );
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
|
||||
/**
|
||||
* Test mapper for properly resolving the best fitting factory method
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
@Mapper(uses = TargetFactories.class)
|
||||
public abstract class Issue1242Mapper {
|
||||
abstract TargetA toTargetA(SourceA source);
|
||||
|
||||
abstract TargetB toTargetB(SourceB source);
|
||||
|
||||
abstract void mergeA(SourceA source, @MappingTarget TargetA target);
|
||||
|
||||
abstract void mergeB(SourceB source, @MappingTarget TargetB target);
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* Tests that if multiple factory methods are applicable but only one of them has a source parameter, the one with the
|
||||
* source param is chosen.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
@IssueKey("1242")
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
@WithClasses({
|
||||
Issue1242Mapper.class,
|
||||
SourceA.class,
|
||||
SourceB.class,
|
||||
TargetA.class,
|
||||
TargetB.class,
|
||||
TargetFactories.class
|
||||
})
|
||||
public class Issue1242Test {
|
||||
@Test
|
||||
public void factoryMethodWithSourceParamIsChosen() {
|
||||
SourceA sourceA = new SourceA();
|
||||
sourceA.setB( new SourceB() );
|
||||
|
||||
TargetA targetA = new TargetA();
|
||||
Mappers.getMapper( Issue1242Mapper.class ).mergeA( sourceA, targetA );
|
||||
|
||||
assertThat( targetA.getB() ).isNotNull();
|
||||
assertThat( targetA.getB().getPassedViaConstructor() ).isEqualTo( "created by factory" );
|
||||
|
||||
targetA = Mappers.getMapper( Issue1242Mapper.class ).toTargetA( sourceA );
|
||||
|
||||
assertThat( targetA.getB() ).isNotNull();
|
||||
assertThat( targetA.getB().getPassedViaConstructor() ).isEqualTo( "created by factory" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses(ErroneousIssue1242MapperMultipleSources.class)
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 33,
|
||||
messageRegExp = "Ambiguous factory methods found for creating .*TargetB:"
|
||||
+ " .*TargetB anotherTargetBCreator\\(.*SourceB source\\),"
|
||||
+ " .*TargetB .*TargetFactories\\.createTargetB\\(.*SourceB source,"
|
||||
+ " @TargetType .*Class<.*TargetB> clazz\\),"
|
||||
+ " .*TargetB .*TargetFactories\\.createTargetB\\(@TargetType java.lang.Class<.*TargetB> clazz\\),"
|
||||
+ " .*TargetB .*TargetFactories\\.createTargetB\\(\\).")
|
||||
})
|
||||
public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() {
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
/**
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
class SourceA {
|
||||
private SourceB b;
|
||||
|
||||
public SourceB getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB(SourceB b) {
|
||||
this.b = b;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
/**
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
class SourceB {
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
/**
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
class TargetA {
|
||||
private TargetB b;
|
||||
|
||||
public TargetB getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB(TargetB b) {
|
||||
this.b = b;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
/**
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
class TargetB {
|
||||
private final String passedViaConstructor;
|
||||
|
||||
TargetB(String passedViaConstructor) {
|
||||
this.passedViaConstructor = passedViaConstructor;
|
||||
}
|
||||
|
||||
String getPassedViaConstructor() {
|
||||
return passedViaConstructor;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright 2012-2017 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.test.bugs._1242;
|
||||
|
||||
import org.mapstruct.ObjectFactory;
|
||||
import org.mapstruct.TargetType;
|
||||
|
||||
/**
|
||||
* Contains non-conflicting factory methods for {@link TargetB}.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class TargetFactories {
|
||||
|
||||
@ObjectFactory
|
||||
protected TargetB createTargetB(SourceB source, @TargetType Class<TargetB> clazz) {
|
||||
return new TargetB( "created by factory" );
|
||||
}
|
||||
|
||||
protected TargetB createTargetB(@TargetType Class<TargetB> clazz) {
|
||||
throw new RuntimeException( "This method is not to be called" );
|
||||
}
|
||||
|
||||
protected TargetB createTargetB() {
|
||||
throw new RuntimeException( "This method is not to be called" );
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user