diff --git a/processor/src/main/java/org/mapstruct/ap/model/MappingContext.java b/processor/src/main/java/org/mapstruct/ap/model/MappingContext.java index 5fa9427cc..7f6eda32f 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/MappingContext.java +++ b/processor/src/main/java/org/mapstruct/ap/model/MappingContext.java @@ -19,8 +19,6 @@ package org.mapstruct.ap.model; import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.annotation.processing.Messager; @@ -28,11 +26,13 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; + +import org.mapstruct.ap.model.assignment.Assignment; +import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.TypeFactory; +import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.SourceMethod; import org.mapstruct.ap.option.Options; -import org.mapstruct.ap.prism.MapperPrism; -import org.mapstruct.ap.util.MapperConfig; /** * This class provides the context for the builders. @@ -61,6 +61,53 @@ import org.mapstruct.ap.util.MapperConfig; */ public class MappingContext { + /** + * Resolves the most suitable way for mapping an element (property, iterable element etc.) from source to target. + * There are 2 basic types of mappings: + * + * conversions are essentially one line mappings, such as String to Integer and Integer to Long methods come in some + * varieties: + * + * + * @author Sjaak Derksen + */ + public interface MappingResolver { + + /** + * returns a parameter assignment + * + * @param mappingMethod target mapping method + * @param mappedElement used for error messages + * @param sourceType parameter to match + * @param targetType return type to match + * @param targetPropertyName name of the target property + * @param dateFormat used for formatting dates in build in methods that need context information + * @param qualifiers used for further select the appropriate mapping method based on class and name + * @param sourceReference call to source type as string + * + * @return an assignment to a method parameter, which can either be: + *
    + *
  1. MethodReference
  2. + *
  3. TypeConversion
  4. + *
  5. Direct Assignment (empty TargetAssignment)
  6. + *
  7. null, no assignment found
  8. + *
+ */ + Assignment getTargetAssignment(Method mappingMethod, String mappedElement, Type sourceType, Type targetType, + String targetPropertyName, String dateFormat, List qualifiers, + String sourceReference); + + Set getUsedVirtualMappings(); + } + private final TypeFactory typeFactory; private final Elements elementUtils; private final Types typeUtils; @@ -69,32 +116,27 @@ public class MappingContext { private final TypeElement mapperTypeElement; private final List sourceModel; private final List mapperReferences; - private MappingResolver mappingResolver; + private final MappingResolver mappingResolver; private final List mappingsToGenerate = new ArrayList(); - /** - * Private methods which are not present in the original mapper interface and are added to map certain property - * types. - */ - private final Set usedVirtualMappings = new HashSet(); - - - - public MappingContext( TypeFactory typeFactory, - Elements elementUtils, - Types typeUtils, - Messager messager, - Options options, - TypeElement mapper, - List sourceModel ) { + public MappingContext(TypeFactory typeFactory, + Elements elementUtils, + Types typeUtils, + Messager messager, + Options options, + MappingResolver mappingResolver, + TypeElement mapper, + List sourceModel, + List mapperReferences) { this.typeFactory = typeFactory; this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; this.options = options; + this.mappingResolver = mappingResolver; this.mapperTypeElement = mapper; this.sourceModel = sourceModel; - this.mapperReferences = initReferencedMappers( mapper ); + this.mapperReferences = mapperReferences; } public TypeElement getMapperTypeElement() { @@ -133,39 +175,11 @@ public class MappingContext { return mappingResolver; } - public void setMappingResolver(MappingResolver mappingResolver) { - this.mappingResolver = mappingResolver; - } - public List getMappingsToGenerate() { return mappingsToGenerate; } public Set getUsedVirtualMappings() { - return usedVirtualMappings; + return mappingResolver.getUsedVirtualMappings(); } - - private List initReferencedMappers(TypeElement element) { - List result = new LinkedList(); - List variableNames = new LinkedList(); - - MapperConfig mapperPrism = MapperConfig.getInstanceOn( element ); - - for ( TypeMirror usedMapper : mapperPrism.uses() ) { - DefaultMapperReference mapperReference = DefaultMapperReference.getInstance( - typeFactory.getType( usedMapper ), - MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null, - typeFactory, - variableNames - ); - - result.add( mapperReference ); - variableNames.add( mapperReference.getVariableName() ); - } - - return result; - } - - - } diff --git a/processor/src/main/java/org/mapstruct/ap/model/MappingResolver.java b/processor/src/main/java/org/mapstruct/ap/model/MappingResolver.java deleted file mode 100644 index 3a7873763..000000000 --- a/processor/src/main/java/org/mapstruct/ap/model/MappingResolver.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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; - -import org.mapstruct.ap.model.assignment.Assignment; -import java.util.List; -import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.model.common.Type; -import org.mapstruct.ap.model.source.Method; - -/** - * - * @author Sjaak Derksen - */ -public interface MappingResolver { - - /** - * returns a parameter assignment - * - * @param mappingMethod target mapping method - * @param mappedElement used for error messages - * @param sourceType parameter to match - * @param targetType return type to match - * @param targetPropertyName name of the target property - * @param dateFormat used for formatting dates in build in methods that need context information - * @param qualifiers used for further select the appropriate mapping method based on class and name - * @param sourceReference call to source type as string - * - * @return an assignment to a method parameter, which can either be: - *
    - *
  1. MethodReference
  2. - *
  3. TypeConversion
  4. - *
  5. Direct Assignment (empty TargetAssignment)
  6. - *
  7. null, no assignment found
  8. - *
- */ - Assignment getTargetAssignment( - Method mappingMethod, - String mappedElement, - Type sourceType, - Type targetType, - String targetPropertyName, - String dateFormat, - List qualifiers, - String sourceReference ); - -} 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 98ea0eea1..4b03c8595 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -20,10 +20,12 @@ package org.mapstruct.ap.processor; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; + import javax.annotation.processing.Messager; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; @@ -33,14 +35,17 @@ import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; + import org.mapstruct.ap.model.BeanMappingMethod; import org.mapstruct.ap.model.Decorator; +import org.mapstruct.ap.model.DefaultMapperReference; import org.mapstruct.ap.model.DelegatingMethod; import org.mapstruct.ap.model.EnumMappingMethod; import org.mapstruct.ap.model.IterableMappingMethod; import org.mapstruct.ap.model.MapMappingMethod; import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.MapperReference; +import org.mapstruct.ap.model.MappingContext; import org.mapstruct.ap.model.MappingMethod; import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.TypeFactory; @@ -48,7 +53,7 @@ import org.mapstruct.ap.model.source.Mapping; import org.mapstruct.ap.model.source.SourceMethod; import org.mapstruct.ap.option.Options; import org.mapstruct.ap.prism.DecoratedWithPrism; -import org.mapstruct.ap.model.MappingContext; +import org.mapstruct.ap.prism.MapperPrism; import org.mapstruct.ap.processor.creation.MappingResolverImpl; import org.mapstruct.ap.util.MapperConfig; @@ -60,7 +65,6 @@ import org.mapstruct.ap.util.MapperConfig; */ public class MapperCreationProcessor implements ModelElementProcessor, Mapper> { - private Elements elementUtils; private Types typeUtils; private Messager messager; @@ -75,16 +79,20 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences = initReferencedMappers( mapperTypeElement ); + MappingContext ctx = new MappingContext( typeFactory, elementUtils, typeUtils, messager, options, + new MappingResolverImpl( context.getMessager(), elementUtils, typeUtils, typeFactory, sourceModel, mapperReferences ), mapperTypeElement, - sourceModel + sourceModel, + mapperReferences ); - ctx.setMappingResolver( new MappingResolverImpl( ctx ) ); this.mappingContext = ctx; return getMapper( mapperTypeElement, sourceModel ); } @@ -94,6 +102,27 @@ public class MapperCreationProcessor implements ModelElementProcessor initReferencedMappers(TypeElement element) { + List result = new LinkedList(); + List variableNames = new LinkedList(); + + MapperConfig mapperPrism = MapperConfig.getInstanceOn( element ); + + for ( TypeMirror usedMapper : mapperPrism.uses() ) { + DefaultMapperReference mapperReference = DefaultMapperReference.getInstance( + typeFactory.getType( usedMapper ), + MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null, + typeFactory, + variableNames + ); + + result.add( mapperReference ); + variableNames.add( mapperReference.getVariableName() ); + } + + return result; + } + private Mapper getMapper(TypeElement element, List methods) { List mapperReferences = mappingContext.getMapperReferences(); List mappingMethods = getMappingMethods( methods ); diff --git a/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java index 04b9d9b08..3faa399e8 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java @@ -18,26 +18,29 @@ */ package org.mapstruct.ap.processor.creation; -import org.mapstruct.ap.model.MappingResolver; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import javax.annotation.processing.Messager; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; import org.mapstruct.ap.conversion.ConversionProvider; import org.mapstruct.ap.conversion.Conversions; -import org.mapstruct.ap.model.assignment.Assignment; -import org.mapstruct.ap.model.MapperReference; -import org.mapstruct.ap.model.MappingContext; -import org.mapstruct.ap.model.VirtualMappingMethod; import org.mapstruct.ap.model.AssignmentFactory; import org.mapstruct.ap.model.Direct; +import org.mapstruct.ap.model.MapperReference; +import org.mapstruct.ap.model.MappingContext.MappingResolver; +import org.mapstruct.ap.model.VirtualMappingMethod; +import org.mapstruct.ap.model.assignment.Assignment; import org.mapstruct.ap.model.common.ConversionContext; import org.mapstruct.ap.model.common.DefaultConversionContext; import org.mapstruct.ap.model.common.Type; +import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.SourceMethod; import org.mapstruct.ap.model.source.builtin.BuiltInMappingMethods; @@ -46,50 +49,47 @@ import org.mapstruct.ap.model.source.selector.MethodSelectors; import org.mapstruct.ap.util.Strings; /** - * Resolves class is responsible for resolving the most suitable way to resolve a mapping from source to target. - * - * There are 2 basic types of mappings: - *
    - *
  • conversions
  • - *
  • methods
  • - *
- * - * conversions are essentially one line mappings, such as String to Integer and Integer to Long - * methods come in some varieties: - *
    - *
  • referenced mapping methods, these are methods implemented (or referenced) by the user. Sometimes indicated - * with the 'uses' in the mapping annotations or part of the abstract mapper class
  • - *
  • generated mapping methods (by means of MapStruct)
  • - *
  • built in methods
  • - *
+ * The one and only implementation of {@link MappingResolver}. The class has been split into an interface an + * implementation for the sake of avoiding package dependencies. Specifically, this implementation refers to classes + * which should not be exposed to the {@code model} package. * * @author Sjaak Derksen */ public class MappingResolverImpl implements MappingResolver { + private final Messager messager; + private final Types typeUtils; + private final TypeFactory typeFactory; + + private final List sourceModel; + private final List mapperReferences; + private final Conversions conversions; private final BuiltInMappingMethods builtInMethods; private final MethodSelectors methodSelectors; - private final MappingContext mappingContext; - - /** * Private methods which are not present in the original mapper interface and are added to map certain property - types. - * @param mappingContext + * types. */ - public MappingResolverImpl( MappingContext mappingContext ) { - this.conversions = new Conversions( mappingContext.getElementUtils(), mappingContext.getTypeFactory() ); - this.builtInMethods = new BuiltInMappingMethods( mappingContext.getTypeFactory() ); - this.methodSelectors = new MethodSelectors( - mappingContext.getTypeUtils(), - mappingContext.getElementUtils(), - mappingContext.getTypeFactory() - ); - this.mappingContext = mappingContext; - } + private final Set usedVirtualMappings = new HashSet(); + public MappingResolverImpl(Messager messager, Elements elementUtils, Types typeUtils, TypeFactory typeFactory, List sourceModel, List mapperReferences) { + this.messager = messager; + this.typeUtils = typeUtils; + this.typeFactory = typeFactory; + + this.sourceModel = sourceModel; + this.mapperReferences = mapperReferences; + + this.conversions = new Conversions( elementUtils, typeFactory ); + this.builtInMethods = new BuiltInMappingMethods( typeFactory ); + this.methodSelectors = new MethodSelectors( + typeUtils, + elementUtils, + typeFactory + ); + } /** * returns a parameter assignment @@ -122,74 +122,64 @@ public class MappingResolverImpl implements MappingResolver { List qualifiers, String sourceReference ) { - ResolvingAttempt attempt = new ResolvingAttempt( mappingMethod, + ResolvingAttempt attempt = new ResolvingAttempt( sourceModel, + mapperReferences, + mappingMethod, mappedElement, targetPropertyName, dateFormat, qualifiers, - sourceReference, - conversions, - builtInMethods, - methodSelectors, - mappingContext + sourceReference ); return attempt.getTargetAssignment( sourceType, targetType ); } + @Override + public Set getUsedVirtualMappings() { + return usedVirtualMappings; + } - private static class ResolvingAttempt { + private class ResolvingAttempt { private final Method mappingMethod; private final String mappedElement; - private final List mapperReferences; private final List methods; private final String targetPropertyName; private final String dateFormat; private final List qualifiers; private final String sourceReference; - private final Conversions conversions; - private final BuiltInMappingMethods builtInMethods; - private final MethodSelectors methodSelectors; - private final MappingContext mappingContext; // resolving via 2 steps creates the possibillity of wrong matches, first builtin method matches, // second doesn't. In that case, the first builtin method should not lead to a virtual method // so this set must be cleared. private final Set virtualMethodCandidates; - private ResolvingAttempt( Method mappingMethod, + private ResolvingAttempt( List sourceModel, + List mapperReferences, + Method mappingMethod, String mappedElement, String targetPropertyName, String dateFormat, List qualifiers, - String sourceReference, - Conversions conversions, - BuiltInMappingMethods builtInMethods, - MethodSelectors methodSelectors, - MappingContext mappingContext ) { + String sourceReference ) { this.mappingMethod = mappingMethod; this.mappedElement = mappedElement; - this.mapperReferences = mappingContext.getMapperReferences(); - this.methods = mappingContext.getSourceModel(); + this.methods = sourceModel; this.targetPropertyName = targetPropertyName; this.dateFormat = dateFormat; this.qualifiers = qualifiers; this.sourceReference = sourceReference; - this.conversions = conversions; - this.builtInMethods = builtInMethods; - this.methodSelectors = methodSelectors; - this.mappingContext = mappingContext; this.virtualMethodCandidates = new HashSet(); } private Assignment getTargetAssignment( Type sourceType, Type targetType ) { - // first simpele mapping method + // first simple mapping method Assignment referencedMethod = resolveViaMethod( sourceType, targetType ); if ( referencedMethod != null ) { referencedMethod.setAssignment( AssignmentFactory.createSimple( sourceReference ) ); - mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates ); + usedVirtualMappings.addAll( virtualMethodCandidates ); return referencedMethod; } @@ -209,21 +199,21 @@ public class MappingResolverImpl implements MappingResolver { // 2 step method, first: method(method(souurce)) referencedMethod = resolveViaMethodAndMethod( sourceType, targetType ); if ( referencedMethod != null ) { - mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates ); + usedVirtualMappings.addAll( virtualMethodCandidates ); return referencedMethod; } // 2 step method, then: method(conversion(souurce)) referencedMethod = resolveViaConversionAndMethod( sourceType, targetType ); if ( referencedMethod != null ) { - mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates ); + usedVirtualMappings.addAll( virtualMethodCandidates ); return referencedMethod; } // 2 step method, finally: conversion(method(souurce)) conversion = resolveViaMethodAndConversion( sourceType, targetType ); if ( conversion != null ) { - mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates ); + usedVirtualMappings.addAll( virtualMethodCandidates ); return conversion; } @@ -239,7 +229,7 @@ public class MappingResolverImpl implements MappingResolver { } ConversionContext ctx = - new DefaultConversionContext( mappingContext.getTypeFactory(), targetType, dateFormat ); + new DefaultConversionContext( typeFactory, targetType, dateFormat ); return conversionProvider.to( ctx ); } @@ -254,7 +244,7 @@ public class MappingResolverImpl implements MappingResolver { SourceMethod matchingSourceMethod = getBestMatch( methods, sourceType, targetType ); if ( matchingSourceMethod != null ) { - return getMappingMethodReference( matchingSourceMethod, mapperReferences, targetType ); + return getMappingMethodReference( matchingSourceMethod, targetType ); } // then a matching built-in method @@ -264,7 +254,7 @@ public class MappingResolverImpl implements MappingResolver { if ( matchingBuiltInMethod != null ) { virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod ) ); ConversionContext ctx = - new DefaultConversionContext( mappingContext.getTypeFactory(), targetType, dateFormat ); + new DefaultConversionContext( typeFactory, targetType, dateFormat ); Assignment methodReference = AssignmentFactory.createMethodReference( matchingBuiltInMethod, ctx ); methodReference.setAssignment( AssignmentFactory.createSimple( sourceReference ) ); return methodReference; @@ -424,7 +414,7 @@ public class MappingResolverImpl implements MappingResolver { returnType, Strings.join( candidates, ", " ) ); - mappingMethod.printMessage( mappingContext.getMessager(), Kind.ERROR, errorMsg ); + mappingMethod.printMessage( messager, Kind.ERROR, errorMsg ); } if ( !candidates.isEmpty() ) { @@ -435,9 +425,8 @@ public class MappingResolverImpl implements MappingResolver { } private Assignment getMappingMethodReference( SourceMethod method, - List mapperReferences, Type targetType ) { - MapperReference mapperReference = findMapperReference( mapperReferences, method ); + MapperReference mapperReference = findMapperReference( method ); return AssignmentFactory.createMethodReference( method, @@ -446,7 +435,7 @@ public class MappingResolverImpl implements MappingResolver { ); } - private MapperReference findMapperReference( List mapperReferences, SourceMethod method ) { + private MapperReference findMapperReference( SourceMethod method ) { for ( MapperReference ref : mapperReferences ) { if ( ref.getType().equals( method.getDeclaringMapper() ) ) { return ref; @@ -513,14 +502,14 @@ public class MappingResolverImpl implements MappingResolver { // constructor which accepts the source type TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty() - ? mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror() + ? typeFactory.getType( Object.class ).getTypeMirror() : sourceType.getTypeParameters().get( 0 ).getTypeMirror(); TypeMirror targetElementType = targetType.getTypeParameters().isEmpty() - ? mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror() + ? typeFactory.getType( Object.class ).getTypeMirror() : targetType.getTypeParameters().get( 0 ).getTypeMirror(); - return mappingContext.getTypeUtils().isAssignable( sourceElementType, targetElementType ); + return typeUtils.isAssignable( sourceElementType, targetElementType ); } /** @@ -544,8 +533,8 @@ public class MappingResolverImpl implements MappingResolver { TypeMirror targetValueType; if ( sourceType.getTypeParameters().isEmpty() ) { - sourceKeyType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror(); - sourceValueType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror(); + sourceKeyType = typeFactory.getType( Object.class ).getTypeMirror(); + sourceValueType = typeFactory.getType( Object.class ).getTypeMirror(); } else { sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror(); @@ -553,16 +542,16 @@ public class MappingResolverImpl implements MappingResolver { } if ( targetType.getTypeParameters().isEmpty() ) { - targetKeyType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror(); - targetValueType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror(); + targetKeyType = typeFactory.getType( Object.class ).getTypeMirror(); + targetValueType = typeFactory.getType( Object.class ).getTypeMirror(); } else { targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror(); targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror(); } - return mappingContext.getTypeUtils().isAssignable( sourceKeyType, targetKeyType ) - && mappingContext.getTypeUtils().isAssignable( sourceValueType, targetValueType ); + return typeUtils.isAssignable( sourceKeyType, targetKeyType ) + && typeUtils.isAssignable( sourceValueType, targetValueType ); } } }