mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1398 allowing @ObjectFactory
methods on context
This commit is contained in:
parent
db851701ef
commit
e67c849c17
@ -32,8 +32,9 @@ import java.lang.annotation.Target;
|
||||
* {@code @}{@link BeforeMapping}/{@code @}{@link AfterMapping} methods, which are called on the provided context
|
||||
* parameter value if applicable.
|
||||
* <p>
|
||||
* <strong>Note:</strong> no {@code null} checks are performed before calling before/after mapping methods on context
|
||||
* parameters. The caller needs to make sure that no {@code null} are passed in that case.
|
||||
* <strong>Note:</strong> no {@code null} checks are performed before calling before/after mapping methods or object
|
||||
* factory methods on {@code @}{@link Context} annotated parameters. The caller needs to make sure that no {@code null}
|
||||
* are passed in that case.
|
||||
* <p>
|
||||
* For generated code to call a method that is declared with {@code @Context} parameters, the declaration of the mapping
|
||||
* method being generated needs to contain at least those (or assignable) {@code @Context} parameters as well. MapStruct
|
||||
@ -135,7 +136,55 @@ import java.lang.annotation.Target;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
* <p>
|
||||
* <strong>Example 3:</strong> Using {@code @Context} parameters for creating an entity object by calling an
|
||||
* {@code @}{@link ObjectFactory} methods:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* // type of the context parameter
|
||||
* public class ContextObjectFactory {
|
||||
* @PersistenceContext(unitName = "my-unit")
|
||||
* private EntityManager em;
|
||||
*
|
||||
* @ObjectFactory
|
||||
* public Valve create( String id ) {
|
||||
* Query query = em.createNamedQuery("Valve.findById");
|
||||
* query.setParameter("id", id);
|
||||
* Valve result = query.getSingleResult();
|
||||
* if ( result != null ) {
|
||||
* result = new Valve( id );
|
||||
* }
|
||||
* return result;
|
||||
* }
|
||||
*
|
||||
* }
|
||||
*
|
||||
* @Mapper
|
||||
* public interface ContextWithObjectFactoryMapper {
|
||||
* Valve map(ValveDto dto, @Context ContextObjectFactory factory);
|
||||
* }
|
||||
*
|
||||
*
|
||||
* // generates:
|
||||
*
|
||||
* public class ContextWithObjectFactoryMapperImpl implements ContextWithObjectFactoryMapper {
|
||||
*
|
||||
* @Override
|
||||
* public Valve map(ValveDto dto, ContextObjectFactory factory) {
|
||||
* if ( dto == null ) {
|
||||
* return null;
|
||||
* }
|
||||
*
|
||||
* Valve valve = factory.create();
|
||||
*
|
||||
* valve.setOneWay( dto.isOneWay() );
|
||||
*
|
||||
* return valve;
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
* @author Andreas Gudian
|
||||
* @since 1.2
|
||||
*/
|
||||
|
@ -29,14 +29,15 @@ import java.lang.annotation.Target;
|
||||
* By default beans are created during the mapping process with the default constructor. If a factory method with a
|
||||
* return type that is assignable to the required object type is present, then the factory method is used instead.
|
||||
* <p>
|
||||
* Factory methods can be defined without parameters, with an {@code @}{@link TargetType} parameter, a {@code @}
|
||||
* {@link Context} parameter, or with the mapping source parameter. If any of those parameters are defined, then
|
||||
* the mapping method that is supposed to use the factory method needs to be declared with an assignable result type,
|
||||
* assignable context parameter, and/or assignable source types.
|
||||
* Factory methods can be defined without parameters, with an {@code @}{@link TargetType} parameter, a
|
||||
* {@code @}{@link Context} parameter, or with the mapping source parameter. If any of those parameters are defined,
|
||||
* then the mapping method that is supposed to use the factory method needs to be declared with an assignable result
|
||||
* type, assignable context parameter, and/or assignable source types.
|
||||
* <p>
|
||||
* <strong>Note:</strong> the usage of this annotation is <em>optional</em> if no source parameters are part of the
|
||||
* signature, i.e. it is declared without parameters or only with {@code @}{@link TargetType} and/or {@code @}
|
||||
* {@link Context}.
|
||||
* <strong>Note:</strong> the usage of this annotation is <em>optional</em> when used in the {@link Mapper#uses()}
|
||||
* if no source parameters are part of the signature, i.e. it is declared without parameters or only with
|
||||
* {@code @}{@link TargetType} and/or {@code @}{@link Context}. It is however <em>mandatory</em> when used inside
|
||||
* an {@code @}{@link Context} annotated class.
|
||||
* <p>
|
||||
* <strong>Example:</strong> Using a factory method for entities to check whether the entity already exists in the
|
||||
* EntityManager and then returns the managed instance:
|
||||
|
@ -1119,6 +1119,8 @@ public class CarMapperImpl implements CarMapper {
|
||||
|
||||
Additional _context_ or _state_ information can be passed through generated mapping methods to custom methods with `@Context` parameters. Such parameters are passed to other mapping methods, `@ObjectFactory` methods (see <<object-factories>>) or `@BeforeMapping` / `@AfterMapping` methods (see <<customizing-mappings-with-before-and-after>>) when applicable and can thus be used in custom code.
|
||||
|
||||
`@Context` parameters are searched for `@ObjectFactory` methods, which are called on the provided context parameter value if applicable.
|
||||
|
||||
`@Context` parameters are also searched for `@BeforeMapping` / `@AfterMapping` methods, which are called on the provided context parameter value if applicable.
|
||||
|
||||
*Note:* no `null` checks are performed before calling before/after mapping methods on context parameters. The caller needs to make sure that `null` is not passed in that case.
|
||||
|
@ -190,10 +190,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
MethodReference factoryMethod = null;
|
||||
if ( !method.isUpdateMethod() ) {
|
||||
factoryMethod = ctx.getMappingResolver().getFactoryMethod(
|
||||
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod(
|
||||
method,
|
||||
method.getResultType(),
|
||||
selectionParameters
|
||||
selectionParameters,
|
||||
ctx
|
||||
);
|
||||
}
|
||||
|
||||
@ -246,14 +247,14 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
sortPropertyMappingsByDependencies();
|
||||
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleCallbackFactory.beforeMappingMethods(
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleMethodResolver.beforeMappingMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
ctx,
|
||||
existingVariableNames
|
||||
);
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx, existingVariableNames );
|
||||
LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariableNames );
|
||||
|
||||
if (factoryMethod != null && method instanceof ForgedMethod ) {
|
||||
( (ForgedMethod) method ).addThrownTypes( factoryMethod.getThrownTypes() );
|
||||
|
@ -141,8 +141,8 @@ public class CollectionAssignmentBuilder {
|
||||
);
|
||||
}
|
||||
|
||||
Assignment factoryMethod = ctx.getMappingResolver()
|
||||
.getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( sourceRHS ) );
|
||||
Assignment factoryMethod = ObjectFactoryMethodResolver
|
||||
.getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( sourceRHS ), ctx );
|
||||
result = new UpdateWrapper(
|
||||
result,
|
||||
method.getThrownTypes(),
|
||||
|
@ -138,19 +138,19 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
||||
|
||||
MethodReference factoryMethod = null;
|
||||
if ( !method.isUpdateMethod() ) {
|
||||
factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, method.getResultType(), null );
|
||||
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, method.getResultType(), null, ctx );
|
||||
}
|
||||
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
existingVariables.add( loopVariableName );
|
||||
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleCallbackFactory.beforeMappingMethods(
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleMethodResolver.beforeMappingMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
ctx,
|
||||
existingVariables
|
||||
);
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods = LifecycleCallbackFactory.afterMappingMethods(
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods = LifecycleMethodResolver.afterMappingMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
ctx,
|
||||
|
@ -104,9 +104,9 @@ public class EnumMappingMethod extends MappingMethod {
|
||||
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
LifecycleMethodResolver.beforeMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
|
||||
return new EnumMappingMethod( method, enumMappings, beforeMappingMethods, afterMappingMethods );
|
||||
}
|
||||
|
@ -38,9 +38,9 @@ import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public final class LifecycleCallbackFactory {
|
||||
public final class LifecycleMethodResolver {
|
||||
|
||||
private LifecycleCallbackFactory() {
|
||||
private LifecycleMethodResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,8 +54,7 @@ public final class LifecycleCallbackFactory {
|
||||
SelectionParameters selectionParameters,
|
||||
MappingBuilderContext ctx,
|
||||
Set<String> existingVariableNames) {
|
||||
return collectLifecycleCallbackMethods(
|
||||
method,
|
||||
return collectLifecycleCallbackMethods( method,
|
||||
selectionParameters,
|
||||
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
||||
ctx,
|
||||
@ -73,8 +72,7 @@ public final class LifecycleCallbackFactory {
|
||||
SelectionParameters selectionParameters,
|
||||
MappingBuilderContext ctx,
|
||||
Set<String> existingVariableNames) {
|
||||
return collectLifecycleCallbackMethods(
|
||||
method,
|
||||
return collectLifecycleCallbackMethods( method,
|
||||
selectionParameters,
|
||||
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
||||
ctx,
|
||||
@ -93,7 +91,9 @@ public final class LifecycleCallbackFactory {
|
||||
List<SourceMethod> availableMethods =
|
||||
new ArrayList<SourceMethod>( methodsProvidedByParams.size() + sourceModelMethods.size() );
|
||||
|
||||
availableMethods.addAll( methodsProvidedByParams );
|
||||
for ( SourceMethod methodProvidedByParams : methodsProvidedByParams ) {
|
||||
availableMethods.add( methodProvidedByParams );
|
||||
}
|
||||
availableMethods.addAll( sourceModelMethods );
|
||||
|
||||
return availableMethods;
|
||||
@ -144,7 +144,7 @@ public final class LifecycleCallbackFactory {
|
||||
existingVariableNames ) );
|
||||
}
|
||||
else {
|
||||
MapperReference mapperReference = findMapperReference(
|
||||
MapperReference mapperReference = MapperReference.findMapperReference(
|
||||
ctx.getMapperReferences(),
|
||||
candidate.getMethod() );
|
||||
|
||||
@ -158,17 +158,6 @@ public final class LifecycleCallbackFactory {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static MapperReference findMapperReference(List<MapperReference> mapperReferences, SourceMethod method) {
|
||||
for ( MapperReference ref : mapperReferences ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
ref.setUsed( ref.isUsed() || !method.isStatic() );
|
||||
ref.setTypeRequiresImport( true );
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<SourceMethod> filterBeforeMappingMethods(List<SourceMethod> methods) {
|
||||
List<SourceMethod> result = new ArrayList<SourceMethod>();
|
||||
for ( SourceMethod method : methods ) {
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -36,8 +38,6 @@ import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
/**
|
||||
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
|
||||
* values are mapped either by a {@link TypeConversion} or another mapping method if required.
|
||||
@ -191,7 +191,8 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
MethodReference factoryMethod = null;
|
||||
if ( !method.isUpdateMethod() ) {
|
||||
factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, method.getResultType(), null );
|
||||
factoryMethod = ObjectFactoryMethodResolver
|
||||
.getFactoryMethod( method, method.getResultType(), null, ctx );
|
||||
}
|
||||
|
||||
keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes(), keyTargetType, false );
|
||||
@ -199,9 +200,9 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, null, ctx, existingVariables );
|
||||
LifecycleMethodResolver.beforeMappingMethods( method, null, ctx, existingVariables );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, null, ctx, existingVariables );
|
||||
LifecycleMethodResolver.afterMappingMethods( method, null, ctx, existingVariables );
|
||||
|
||||
return new MapMappingMethod(
|
||||
method,
|
||||
|
@ -18,7 +18,9 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import java.util.List;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
|
||||
/**
|
||||
* A reference to another mapper class, which itself may be generated or hand-written.
|
||||
@ -34,4 +36,15 @@ public abstract class MapperReference extends Field {
|
||||
public MapperReference(Type type, String variableName, boolean isUsed) {
|
||||
super( type, variableName, isUsed );
|
||||
}
|
||||
|
||||
public static MapperReference findMapperReference(List<MapperReference> mapperReferences, SourceMethod method) {
|
||||
for ( MapperReference ref : mapperReferences ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
ref.setUsed( ref.isUsed() || !method.isStatic() );
|
||||
ref.setTypeRequiresImport( true );
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -107,18 +107,6 @@ public class MappingBuilderContext {
|
||||
SelectionParameters selectionParameters, SourceRHS sourceRHS,
|
||||
boolean preferUpdateMethods);
|
||||
|
||||
/**
|
||||
* returns a no arg factory method
|
||||
*
|
||||
* @param mappingMethod target mapping method
|
||||
* @param target return type to match
|
||||
* @param selectionParameters parameters used in the selection process
|
||||
*
|
||||
* @return a method reference to the factory method, or null if no suitable, or ambiguous method found
|
||||
*
|
||||
*/
|
||||
MethodReference getFactoryMethod(Method mappingMethod, Type target, SelectionParameters selectionParameters);
|
||||
|
||||
Set<VirtualMappingMethod> getUsedVirtualMappings();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class ObjectFactoryMethodResolver {
|
||||
|
||||
private ObjectFactoryMethodResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a no arg factory method
|
||||
*
|
||||
* @param method target mapping method
|
||||
* @param targetType return type to match
|
||||
* @param selectionParameters parameters used in the selection process
|
||||
* @param ctx
|
||||
*
|
||||
* @return a method reference to the factory method, or null if no suitable, or ambiguous method found
|
||||
*
|
||||
*/
|
||||
public static MethodReference getFactoryMethod( Method method,
|
||||
Type targetType,
|
||||
SelectionParameters selectionParameters,
|
||||
MappingBuilderContext ctx) {
|
||||
|
||||
MethodSelectors selectors =
|
||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory() );
|
||||
|
||||
List<SelectedMethod<SourceMethod>> matchingFactoryMethods =
|
||||
selectors.getMatchingMethods(
|
||||
method,
|
||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||
java.util.Collections.<Type> emptyList(),
|
||||
targetType.getEffectiveType(),
|
||||
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
||||
|
||||
if (matchingFactoryMethods.isEmpty()) {
|
||||
return findBuilderFactoryMethod( targetType );
|
||||
}
|
||||
|
||||
if ( matchingFactoryMethods.size() > 1 ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
||||
targetType.getEffectiveType(),
|
||||
Strings.join( matchingFactoryMethods, ", " ) );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
SelectedMethod<SourceMethod> matchingFactoryMethod = first( matchingFactoryMethods );
|
||||
|
||||
Parameter providingParameter =
|
||||
method.getContextProvidedMethods().getParameterForProvidedMethod( matchingFactoryMethod.getMethod() );
|
||||
|
||||
if ( providingParameter != null ) {
|
||||
return MethodReference.forParameterProvidedMethod(
|
||||
matchingFactoryMethod.getMethod(),
|
||||
providingParameter,
|
||||
matchingFactoryMethod.getParameterBindings() );
|
||||
}
|
||||
else {
|
||||
MapperReference ref = MapperReference.findMapperReference(
|
||||
ctx.getMapperReferences(),
|
||||
matchingFactoryMethod.getMethod() );
|
||||
|
||||
return MethodReference.forMapperReference(
|
||||
matchingFactoryMethod.getMethod(),
|
||||
ref,
|
||||
matchingFactoryMethod.getParameterBindings() );
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodReference findBuilderFactoryMethod(Type targetType) {
|
||||
BuilderType builder = targetType.getBuilderType();
|
||||
if ( builder == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ExecutableElement builderCreationMethod = builder.getBuilderCreationMethod();
|
||||
if ( builderCreationMethod.getKind() == ElementKind.CONSTRUCTOR ) {
|
||||
// If the builder creation method is a constructor it would be handled properly down the line
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( !builder.getBuildingType().isAssignableTo( targetType ) ) {
|
||||
//TODO print error message
|
||||
return null;
|
||||
}
|
||||
|
||||
return MethodReference.forStaticBuilder(
|
||||
builderCreationMethod.getSimpleName().toString(),
|
||||
builder.getOwningType()
|
||||
);
|
||||
}
|
||||
|
||||
private static List<SourceMethod> getAllAvailableMethods(Method method, List<SourceMethod> sourceModelMethods) {
|
||||
ParameterProvidedMethods contextProvidedMethods = method.getContextProvidedMethods();
|
||||
if ( contextProvidedMethods.isEmpty() ) {
|
||||
return sourceModelMethods;
|
||||
}
|
||||
|
||||
List<SourceMethod> methodsProvidedByParams = contextProvidedMethods
|
||||
.getAllProvidedMethodsInParameterOrder( method.getContextParameters() );
|
||||
|
||||
List<SourceMethod> availableMethods =
|
||||
new ArrayList<SourceMethod>( methodsProvidedByParams.size() + sourceModelMethods.size() );
|
||||
|
||||
for ( SourceMethod methodProvidedByParams : methodsProvidedByParams ) {
|
||||
// add only methods from context that do have the @ObjectFactory annotation
|
||||
if ( methodProvidedByParams.hasObjectFactoryAnnotation() ) {
|
||||
availableMethods.add( methodProvidedByParams );
|
||||
}
|
||||
}
|
||||
availableMethods.addAll( sourceModelMethods );
|
||||
|
||||
return availableMethods;
|
||||
}
|
||||
|
||||
}
|
@ -18,13 +18,18 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT;
|
||||
import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Collections.last;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
|
||||
@ -54,16 +59,11 @@ import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
||||
import org.mapstruct.ap.internal.util.Executables;
|
||||
import org.mapstruct.ap.internal.util.MapperConfiguration;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.NativeTypes;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
import org.mapstruct.ap.internal.util.ValueProvider;
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT;
|
||||
import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Collections.last;
|
||||
import org.mapstruct.ap.internal.util.NativeTypes;
|
||||
|
||||
/**
|
||||
* Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to
|
||||
* {@code int Target#bar}. Name and type of source and target property can differ. If they have different types, the
|
||||
@ -433,8 +433,8 @@ public class PropertyMapping extends ModelElement {
|
||||
boolean mapNullToDefault = method.getMapperConfiguration().
|
||||
getNullValueMappingStrategy() == NullValueMappingStrategyPrism.RETURN_DEFAULT;
|
||||
|
||||
Assignment factory = ctx.getMappingResolver()
|
||||
.getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ) );
|
||||
Assignment factory = ObjectFactoryMethodResolver
|
||||
.getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ), ctx );
|
||||
return new UpdateWrapper( rhs, method.getThrownTypes(), factory, isFieldAssignment(), targetType,
|
||||
!rhs.isSourceReferenceParameter(), mapNullToDefault );
|
||||
}
|
||||
@ -816,7 +816,7 @@ public class PropertyMapping extends ModelElement {
|
||||
getNullValueMappingStrategy() == NullValueMappingStrategyPrism.RETURN_DEFAULT;
|
||||
|
||||
Assignment factoryMethod =
|
||||
ctx.getMappingResolver().getFactoryMethod( method, targetType, null );
|
||||
ObjectFactoryMethodResolver.getFactoryMethod( method, targetType, null, ctx );
|
||||
|
||||
assignment = new UpdateWrapper( assignment, method.getThrownTypes(), factoryMethod,
|
||||
isFieldAssignment(), targetType, false, mapNullToDefault );
|
||||
|
@ -122,9 +122,9 @@ public class ValueMappingMethod extends MappingMethod {
|
||||
SelectionParameters selectionParameters = getSelectionParameters( method, ctx.getTypeUtils() );
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
LifecycleMethodResolver.beforeMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
|
||||
// finally return a mapping
|
||||
return new ValueMappingMethod( method, mappingEntries, nullTarget, defaultTarget,
|
||||
|
@ -79,12 +79,12 @@ public class SourceMethod implements Method {
|
||||
private List<SourceMethod> applicablePrototypeMethods;
|
||||
private List<SourceMethod> applicableReversePrototypeMethods;
|
||||
|
||||
private Boolean isBeanMapping;
|
||||
private Boolean isEnumMapping;
|
||||
private Boolean isValueMapping;
|
||||
private Boolean isIterableMapping;
|
||||
private Boolean isMapMapping;
|
||||
private Boolean isStreamMapping;
|
||||
private final boolean hasObjectFactoryAnnotation;
|
||||
|
||||
public static class Builder {
|
||||
|
||||
@ -231,7 +231,8 @@ public class SourceMethod implements Method {
|
||||
|
||||
this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
|
||||
this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters );
|
||||
this.isObjectFactory = determineIfIsObjectFactory( executable );
|
||||
this.hasObjectFactoryAnnotation = ObjectFactoryPrism.getInstanceOn( executable ) != null;
|
||||
this.isObjectFactory = determineIfIsObjectFactory();
|
||||
|
||||
this.typeUtils = builder.typeUtils;
|
||||
this.typeFactory = builder.typeFactory;
|
||||
@ -240,13 +241,12 @@ public class SourceMethod implements Method {
|
||||
this.mapperToImplement = builder.definingType;
|
||||
}
|
||||
|
||||
private boolean determineIfIsObjectFactory(ExecutableElement executable) {
|
||||
boolean hasFactoryAnnotation = ObjectFactoryPrism.getInstanceOn( executable ) != null;
|
||||
private boolean determineIfIsObjectFactory() {
|
||||
boolean hasNoSourceParameters = getSourceParameters().isEmpty();
|
||||
boolean hasNoMappingTargetParam = getMappingTargetParameter() == null;
|
||||
return !isLifecycleCallbackMethod() && !returnType.isVoid()
|
||||
&& hasNoMappingTargetParam
|
||||
&& ( hasFactoryAnnotation || hasNoSourceParameters );
|
||||
&& ( hasObjectFactoryAnnotation || hasNoSourceParameters );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -606,4 +606,8 @@ public class SourceMethod implements Method {
|
||||
public boolean isUpdateMethod() {
|
||||
return getMappingTargetParameter() != null;
|
||||
}
|
||||
|
||||
public boolean hasObjectFactoryAnnotation() {
|
||||
return hasObjectFactoryAnnotation;
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
}
|
||||
|
||||
ParameterProvidedMethods contextProvidedMethods =
|
||||
retrieveLifecycleMethodsFromContext( contextParameters, mapperToImplement, mapperConfig );
|
||||
retrieveContextProvidedMethods( contextParameters, mapperToImplement, mapperConfig );
|
||||
|
||||
return new SourceMethod.Builder()
|
||||
.setExecutable( method )
|
||||
@ -275,7 +275,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
.build();
|
||||
}
|
||||
|
||||
private ParameterProvidedMethods retrieveLifecycleMethodsFromContext(
|
||||
private ParameterProvidedMethods retrieveContextProvidedMethods(
|
||||
List<Parameter> contextParameters, TypeElement mapperToImplement, MapperConfiguration mapperConfig) {
|
||||
|
||||
ParameterProvidedMethods.Builder builder = ParameterProvidedMethods.builder();
|
||||
@ -289,14 +289,14 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
mapperConfig,
|
||||
Collections.<SourceMethod> emptyList() );
|
||||
|
||||
List<SourceMethod> lifecycleMethods = new ArrayList<SourceMethod>( contextParamMethods.size() );
|
||||
List<SourceMethod> contextProvidedMethods = new ArrayList<SourceMethod>( contextParamMethods.size() );
|
||||
for ( SourceMethod sourceMethod : contextParamMethods ) {
|
||||
if ( sourceMethod.isLifecycleCallbackMethod() ) {
|
||||
lifecycleMethods.add( sourceMethod );
|
||||
if ( sourceMethod.isLifecycleCallbackMethod() || sourceMethod.isObjectFactory() ) {
|
||||
contextProvidedMethods.add( sourceMethod );
|
||||
}
|
||||
}
|
||||
|
||||
builder.addMethodsForParameter( contextParam, lifecycleMethods );
|
||||
builder.addMethodsForParameter( contextParam, contextProvidedMethods );
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
|
@ -26,7 +26,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
@ -45,7 +44,6 @@ import org.mapstruct.ap.internal.model.MappingBuilderContext.MappingResolver;
|
||||
import org.mapstruct.ap.internal.model.MethodReference;
|
||||
import org.mapstruct.ap.internal.model.VirtualMappingMethod;
|
||||
import org.mapstruct.ap.internal.model.common.Assignment;
|
||||
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||
import org.mapstruct.ap.internal.model.common.ConversionContext;
|
||||
import org.mapstruct.ap.internal.model.common.DefaultConversionContext;
|
||||
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
||||
@ -129,65 +127,6 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
return usedVirtualMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodReference getFactoryMethod(final Method mappingMethod, Type targetType,
|
||||
SelectionParameters selectionParameters) {
|
||||
|
||||
List<SelectedMethod<Method>> matchingFactoryMethods =
|
||||
methodSelectors.getMatchingMethods(
|
||||
mappingMethod,
|
||||
sourceModel,
|
||||
java.util.Collections.<Type> emptyList(),
|
||||
targetType.getEffectiveType(),
|
||||
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
||||
|
||||
if (matchingFactoryMethods.isEmpty()) {
|
||||
return findBuilderFactoryMethod( targetType );
|
||||
}
|
||||
|
||||
if ( matchingFactoryMethods.size() > 1 ) {
|
||||
messager.printMessage(
|
||||
mappingMethod.getExecutable(),
|
||||
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
||||
targetType.getEffectiveType(),
|
||||
Strings.join( matchingFactoryMethods, ", " ) );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
SelectedMethod<Method> matchingFactoryMethod = first( matchingFactoryMethods );
|
||||
|
||||
MapperReference ref = findMapperReference( matchingFactoryMethod.getMethod() );
|
||||
|
||||
return MethodReference.forMapperReference(
|
||||
matchingFactoryMethod.getMethod(),
|
||||
ref,
|
||||
matchingFactoryMethod.getParameterBindings() );
|
||||
}
|
||||
|
||||
private MethodReference findBuilderFactoryMethod(Type targetType) {
|
||||
BuilderType builder = targetType.getBuilderType();
|
||||
if ( builder == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ExecutableElement builderCreationMethod = builder.getBuilderCreationMethod();
|
||||
if ( builderCreationMethod.getKind() == ElementKind.CONSTRUCTOR ) {
|
||||
// If the builder creation method is a constructor it would be handled properly down the line
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( !builder.getBuildingType().isAssignableTo( targetType ) ) {
|
||||
//TODO print error message
|
||||
return null;
|
||||
}
|
||||
|
||||
return MethodReference.forStaticBuilder(
|
||||
builderCreationMethod.getSimpleName().toString(),
|
||||
builder.getOwningType()
|
||||
);
|
||||
}
|
||||
|
||||
private MapperReference findMapperReference(Method method) {
|
||||
for ( MapperReference ref : mapperReferences ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.context.objectfactory;
|
||||
|
||||
import org.mapstruct.ObjectFactory;
|
||||
|
||||
/**
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class ContextObjectFactory {
|
||||
|
||||
@ObjectFactory
|
||||
public Valve create() {
|
||||
return new Valve("123id");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.context.objectfactory;
|
||||
|
||||
import org.mapstruct.Context;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface ContextWithObjectFactoryMapper {
|
||||
|
||||
ContextWithObjectFactoryMapper INSTANCE = Mappers.getMapper( ContextWithObjectFactoryMapper.class );
|
||||
|
||||
Valve map(ValveDto dto, @Context ContextObjectFactory factory);
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.context.objectfactory;
|
||||
|
||||
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.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@IssueKey( "1398" )
|
||||
@WithClasses({
|
||||
Valve.class,
|
||||
ValveDto.class,
|
||||
ContextObjectFactory.class,
|
||||
ContextWithObjectFactoryMapper.class})
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class ContextWithObjectFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testFactoryCalled( ) {
|
||||
ValveDto dto = new ValveDto();
|
||||
dto.setOneWay( true );
|
||||
|
||||
Valve result = ContextWithObjectFactoryMapper.INSTANCE.map( dto, new ContextObjectFactory() );
|
||||
|
||||
assertThat( result ).isNotNull();
|
||||
assertThat( result.isOneWay() ).isTrue();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.context.objectfactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class Valve {
|
||||
|
||||
private boolean oneWay;
|
||||
private final String id;
|
||||
|
||||
public Valve(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isOneWay() {
|
||||
return oneWay;
|
||||
}
|
||||
|
||||
public void setOneWay(boolean oneWay) {
|
||||
this.oneWay = oneWay;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.context.objectfactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class ValveDto {
|
||||
|
||||
private boolean oneWay;
|
||||
|
||||
public boolean isOneWay() {
|
||||
return oneWay;
|
||||
}
|
||||
|
||||
public void setOneWay(boolean oneWay) {
|
||||
this.oneWay = oneWay;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user