#385 Introduction of BeanMapping annotation and target type selection, reorder of selectors

This commit is contained in:
sjaakd 2015-01-10 08:56:38 +01:00
parent 65c272286f
commit 4e771244ad
16 changed files with 535 additions and 17 deletions

View File

@ -0,0 +1,43 @@
/**
* Copyright 2012-2015 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;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Configures the mapping between two bean types
*
* @author Sjaak Derksen
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface BeanMapping {
/**
* Specifies the result type of the method to select in case ambiguous mappings / factory methods.
*
*
* @return the resultType to select
*/
Class resultType() default void.class;
}

View File

@ -42,9 +42,10 @@ public class MethodSelectors implements MethodSelector {
selectors =
Arrays.<MethodSelector>asList(
new TypeSelector( typeFactory ),
new InheritanceSelector(),
new QualifierSelector( typeUtils, elementUtils ),
new TargetTypeSelector( typeUtils, elementUtils ),
new XmlElementDeclSelector( typeUtils ),
new QualifierSelector( typeUtils, elementUtils )
new InheritanceSelector()
);
}

View File

@ -30,10 +30,12 @@ public class SelectionCriteria {
private final List<TypeMirror> qualifiers;
private final String targetPropertyName;
private final TypeMirror qualifyingResultType;
public SelectionCriteria(List<TypeMirror> qualifiers, String targetPropertyName) {
public SelectionCriteria(List<TypeMirror> qualifiers, String targetPropertyName, TypeMirror qualifyingResultType) {
this.qualifiers = qualifiers;
this.targetPropertyName = targetPropertyName;
this.qualifyingResultType = qualifyingResultType;
}
public List<TypeMirror> getQualifiers() {
@ -44,4 +46,7 @@ public class SelectionCriteria {
return targetPropertyName;
}
public TypeMirror getQualifyingResultType() {
return qualifyingResultType;
}
}

View File

@ -0,0 +1,69 @@
/**
* Copyright 2012-2015 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.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
/**
* This selector selects a best match based on the result type.
* <p>
* Suppose: Sedan -> Car -> Vehicle, MotorCycle -> Vehicle
* By means of this selector one can pinpoint the exact desired return type (Sedan, Car, MotorCycle, Vehicle)
*
* @author Sjaak Derksen
*/
public class TargetTypeSelector implements MethodSelector {
private final Types typeUtils;
public TargetTypeSelector( Types typeUtils, Elements elementUtils ) {
this.typeUtils = typeUtils;
}
@Override
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
Type sourceType, Type targetType,
SelectionCriteria criteria) {
TypeMirror qualifyingTypeMirror = criteria.getQualifyingResultType();
if ( qualifyingTypeMirror != null ) {
List<T> candidatesWithQualifyingTargetType = new ArrayList<T>();
for ( T method : methods ) {
TypeMirror resultTypeMirror = method.getResultType().getTypeElement().asType();
if ( typeUtils.isSameType( qualifyingTypeMirror, resultTypeMirror ) ) {
candidatesWithQualifyingTargetType.add( method );
}
}
return candidatesWithQualifyingTargetType;
}
else {
return methods;
}
}
}

View File

@ -22,6 +22,7 @@ import javax.xml.bind.annotation.XmlElementDecl;
import net.java.dev.hickory.prism.GeneratePrism;
import net.java.dev.hickory.prism.GeneratePrisms;
import org.mapstruct.BeanMapping;
import org.mapstruct.DecoratedWith;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
@ -46,6 +47,7 @@ import org.mapstruct.TargetType;
@GeneratePrism(value = Mapping.class, publicAccess = true),
@GeneratePrism(value = Mappings.class, publicAccess = true),
@GeneratePrism(value = IterableMapping.class, publicAccess = true),
@GeneratePrism(value = BeanMapping.class, publicAccess = true),
@GeneratePrism(value = MapMapping.class, publicAccess = true),
@GeneratePrism(value = TargetType.class, publicAccess = true),
@GeneratePrism(value = MappingTarget.class, publicAccess = true),

View File

@ -47,6 +47,7 @@ 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.model.source.selector.SelectionCriteria;
import org.mapstruct.ap.prism.BeanMappingPrism;
import org.mapstruct.ap.util.Strings;
/**
@ -124,14 +125,15 @@ public class MappingResolverImpl implements MappingResolver {
List<TypeMirror> qualifiers,
String sourceReference) {
SelectionCriteria criteria = new SelectionCriteria(qualifiers, targetPropertyName, null );
ResolvingAttempt attempt = new ResolvingAttempt(
sourceModel,
mappingMethod,
mappedElement,
targetPropertyName,
dateFormat,
qualifiers,
sourceReference
sourceReference,
criteria
);
return attempt.getTargetAssignment( sourceType, targetType );
@ -145,14 +147,20 @@ public class MappingResolverImpl implements MappingResolver {
@Override
public MethodReference getFactoryMethod( Method mappingMethod, Type targetType ) {
TypeMirror qualifyingResultTypeMirror = null;
BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( mappingMethod.getExecutable() );
if ( beanMappingPrism != null ) {
qualifyingResultTypeMirror = beanMappingPrism.resultType();
}
SelectionCriteria criteria = new SelectionCriteria(null, null, qualifyingResultTypeMirror );
ResolvingAttempt attempt = new ResolvingAttempt(
sourceModel,
mappingMethod,
null,
null,
null,
null,
null
criteria
);
SourceMethod matchingSourceMethod = attempt.getBestMatch( sourceModel, null, targetType );
@ -169,9 +177,8 @@ public class MappingResolverImpl implements MappingResolver {
private final Method mappingMethod;
private final String mappedElement;
private final List<SourceMethod> methods;
private final String targetPropertyName;
private final String dateFormat;
private final List<TypeMirror> qualifiers;
private final SelectionCriteria selectionCriteria;
private final String sourceReference;
// resolving via 2 steps creates the possibillity of wrong matches, first builtin method matches,
@ -182,18 +189,16 @@ public class MappingResolverImpl implements MappingResolver {
private ResolvingAttempt(List<SourceMethod> sourceModel,
Method mappingMethod,
String mappedElement,
String targetPropertyName,
String dateFormat,
List<TypeMirror> qualifiers,
String sourceReference) {
String sourceReference,
SelectionCriteria criteria) {
this.mappingMethod = mappingMethod;
this.mappedElement = mappedElement;
this.methods = filterPossibleCandidateMethods( sourceModel );
this.targetPropertyName = targetPropertyName;
this.dateFormat = dateFormat;
this.qualifiers = qualifiers;
this.sourceReference = sourceReference;
this.virtualMethodCandidates = new HashSet<VirtualMappingMethod>();
this.selectionCriteria = criteria;
}
private <T extends Method> List<T> filterPossibleCandidateMethods(List<T> candidateMethods) {
@ -440,13 +445,12 @@ public class MappingResolverImpl implements MappingResolver {
private <T extends Method> T getBestMatch(List<T> methods, Type sourceType, Type returnType) {
SelectionCriteria criteria = new SelectionCriteria( qualifiers, targetPropertyName );
List<T> candidates = methodSelectors.getMatchingMethods(
mappingMethod,
methods,
sourceType,
returnType,
criteria
selectionCriteria
);
// raise an error if more than one mapping method is suitable to map the given source type

View File

@ -0,0 +1,31 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
/**
*
* @author Sjaak Derksen
*/
public class Apple extends Fruit {
public Apple(String type) {
super( type );
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
/**
*
* @author Sjaak Derksen
*/
public class AppleDto extends FruitDto {
public AppleDto(String type) {
super( type );
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
/**
*
* @author Sjaak Derksen
*/
public class Banana extends Fruit {
public Banana(String type) {
super( type );
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
/**
*
* @author Sjaak Derksen
*/
public class BananaDto extends FruitDto {
public BananaDto(String type) {
super( type );
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
/**
*
* @author Sjaak Derksen
*/
public class ConflictingFruitFactory {
public Apple createApple() {
return new Apple( "apple" );
}
public Banana createBanana() {
return new Banana( "banana" );
}
}

View File

@ -0,0 +1,38 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper(uses = ConflictingFruitFactory.class)
public interface ErroneousFruitMapper {
ErroneousFruitMapper INSTANCE = Mappers.getMapper( ErroneousFruitMapper.class );
@Mapping(target = "type", ignore = true)
Fruit map(FruitDto source);
}

View File

@ -0,0 +1,42 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
/**
*
* @author Sjaak Derksen
*/
public class Fruit {
private String type;
public Fruit(String type) {
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,42 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
/**
*
* @author Sjaak Derksen
*/
public class FruitDto {
private String type;
public FruitDto(String type) {
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,74 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
import javax.tools.Diagnostic.Kind;
import static org.fest.assertions.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;
/**
*
* @author Sjaak Derksen
*/
@IssueKey("385")
@WithClasses({
Fruit.class,
FruitDto.class,
Apple.class,
AppleDto.class
})
@RunWith(AnnotationProcessorTestRunner.class)
public class InheritanceSelectionTest {
@Test
@WithClasses( { ConflictingFruitFactory.class, ErroneousFruitMapper.class, Banana.class } )
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousFruitMapper.class,
kind = Kind.ERROR,
line = 36,
messageRegExp = "Ambiguous mapping methods found for factorizing .*Fruit: "
+ ".*Apple .*ConflictingFruitFactory\\.createApple\\(\\), "
+ ".*Banana .*ConflictingFruitFactory\\.createBanana\\(\\)\\.")
}
)
public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() {
}
@Test
@WithClasses( { ConflictingFruitFactory.class, TargetTypeSelectingFruitMapper.class, Banana.class } )
public void testForkedInheritanceHierarchyButDefinedTargetType() {
FruitDto fruitDto = new FruitDto( null );
Fruit fruit = TargetTypeSelectingFruitMapper.INSTANCE.map( fruitDto );
assertThat( fruit ).isNotNull();
assertThat( fruit.getType() ).isEqualTo( "apple" );
}
}

View File

@ -0,0 +1,40 @@
/**
* Copyright 2012-2015 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.selection.inheritance;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper(uses = ConflictingFruitFactory.class)
public interface TargetTypeSelectingFruitMapper {
TargetTypeSelectingFruitMapper INSTANCE = Mappers.getMapper( TargetTypeSelectingFruitMapper.class );
@BeanMapping(resultType = Apple.class)
@Mapping(target = "type", ignore = true)
Fruit map(FruitDto source);
}