mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
This commit is contained in:
parent
a2e1404b93
commit
51cdbd67e3
78
core/src/main/java/org/mapstruct/Condition.java
Normal file
78
core/src/main/java/org/mapstruct/Condition.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation marks a method as a <em>presence check method</em> to check check for presence in beans.
|
||||||
|
* <p>
|
||||||
|
* By default bean properties are checked against {@code null} or using a presence check method in the source bean.
|
||||||
|
* If a presence check method is available then it will be used instead.
|
||||||
|
* <p>
|
||||||
|
* Presence check methods have to return {@code boolean}.
|
||||||
|
* The following parameters are accepted for the presence check methods:
|
||||||
|
* <ul>
|
||||||
|
* <li>The parameter with the value of the source property.
|
||||||
|
* e.g. the value given by calling {@code getName()} for the name property of the source bean</li>
|
||||||
|
* <li>The mapping source parameter</li>
|
||||||
|
* <li>{@code @}{@link Context} parameter</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <strong>Note:</strong> The usage of this annotation is <em>mandatory</em>
|
||||||
|
* for a method to be considered as a presence check method.
|
||||||
|
*
|
||||||
|
* <pre><code>
|
||||||
|
* public class PresenceCheckUtils {
|
||||||
|
*
|
||||||
|
* @Condition
|
||||||
|
* public static boolean isNotEmpty(String value) {
|
||||||
|
* return value != null && !value.isEmpty();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Mapper(uses = PresenceCheckUtils.class)
|
||||||
|
* public interface MovieMapper {
|
||||||
|
*
|
||||||
|
* MovieDto map(Movie movie);
|
||||||
|
* }
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
* The following implementation of {@code MovieMapper} will be generated:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <code>
|
||||||
|
* public class MovieMapperImpl implements MovieMapper {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public MovieDto map(Movie movie) {
|
||||||
|
* if ( movie == null ) {
|
||||||
|
* return null;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* MovieDto movieDto = new MovieDto();
|
||||||
|
*
|
||||||
|
* if ( PresenceCheckUtils.isNotEmpty( movie.getTitle() ) ) {
|
||||||
|
* movieDto.setTitle( movie.getTitle() );
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return movieDto;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface Condition {
|
||||||
|
|
||||||
|
}
|
@ -316,6 +316,74 @@ public @interface Mapping {
|
|||||||
*/
|
*/
|
||||||
String[] qualifiedByName() default { };
|
String[] qualifiedByName() default { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A qualifier can be specified to aid the selection process of a suitable presence check method.
|
||||||
|
* This is useful in case multiple presence check methods qualify and thus would result in an
|
||||||
|
* 'Ambiguous presence check methods found' error.
|
||||||
|
* A qualifier is a custom annotation and can be placed on a hand written mapper class or a method.
|
||||||
|
* This is similar to the {@link #qualifiedBy()}, but it is only applied for {@link Condition} methods.
|
||||||
|
*
|
||||||
|
* @return the qualifiers
|
||||||
|
* @see Qualifier
|
||||||
|
* @see #qualifiedBy()
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
Class<? extends Annotation>[] conditionQualifiedBy() default { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String-based form of qualifiers for condition / presence check methods;
|
||||||
|
* When looking for a suitable presence check method for a given property, MapStruct will
|
||||||
|
* only consider those methods carrying directly or indirectly (i.e. on the class-level) a {@link Named} annotation
|
||||||
|
* for each of the specified qualifier names.
|
||||||
|
*
|
||||||
|
* This is similar like {@link #qualifiedByName()} but it is only applied for {@link Condition} methods.
|
||||||
|
* <p>
|
||||||
|
* Note that annotation-based qualifiers are generally preferable as they allow more easily to find references and
|
||||||
|
* are safe for refactorings, but name-based qualifiers can be a less verbose alternative when requiring a large
|
||||||
|
* number of qualifiers as no custom annotation types are needed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return One or more qualifier name(s)
|
||||||
|
* @see #conditionQualifiedBy()
|
||||||
|
* @see #qualifiedByName()
|
||||||
|
* @see Named
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
String[] conditionQualifiedByName() default { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A conditionExpression {@link String} based on which the specified property is to be checked
|
||||||
|
* whether it is present or not.
|
||||||
|
* <p>
|
||||||
|
* Currently, Java is the only supported "expression language" and expressions must be given in form of Java
|
||||||
|
* expressions using the following format: {@code java(<EXPRESSION>)}. For instance the mapping:
|
||||||
|
* <pre><code>
|
||||||
|
* @Mapping(
|
||||||
|
* target = "someProp",
|
||||||
|
* conditionExpression = "java(s.getAge() < 18)"
|
||||||
|
* )
|
||||||
|
* </code></pre>
|
||||||
|
* <p>
|
||||||
|
* will cause the following target property assignment to be generated:
|
||||||
|
* <pre><code>
|
||||||
|
* if (s.getAge() < 18) {
|
||||||
|
* targetBean.setSomeProp( s.getSomeProp() );
|
||||||
|
* }
|
||||||
|
* </code></pre>
|
||||||
|
* <p>
|
||||||
|
* <p>
|
||||||
|
* Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be
|
||||||
|
* imported via {@link Mapper#imports()}.
|
||||||
|
* <p>
|
||||||
|
* This attribute can not be used together with {@link #expression()} or {@link #constant()}.
|
||||||
|
*
|
||||||
|
* @return An expression specifying a condition check for the designated property
|
||||||
|
*
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
String conditionExpression() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the result type of the mapping method to be used in case multiple mapping methods qualify.
|
* Specifies the result type of the mapping method to be used in case multiple mapping methods qualify.
|
||||||
*
|
*
|
||||||
|
@ -229,6 +229,78 @@ The source presence checker name can be changed in the MapStruct service provide
|
|||||||
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor see `CollectionMappingStrategy`, MapStruct will always generate a source property
|
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor see `CollectionMappingStrategy`, MapStruct will always generate a source property
|
||||||
null check, regardless the value of the `NullValueCheckStrategy` to avoid addition of `null` to the target collection or map.
|
null check, regardless the value of the `NullValueCheckStrategy` to avoid addition of `null` to the target collection or map.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[conditional-mapping]]
|
||||||
|
=== Conditional Mapping
|
||||||
|
|
||||||
|
Conditional Mapping is a type of <<source-presence-check>>.
|
||||||
|
The difference is that it allows users to write custom condition methods that will be invoked to check if a property needs to be mapped or not.
|
||||||
|
|
||||||
|
A custom condition method is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`.
|
||||||
|
|
||||||
|
e.g. if you only want to map a String property when it is not `null, and it is not empty then you can do something like:
|
||||||
|
|
||||||
|
.Mapper using custom condition check method
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@Mapper
|
||||||
|
public interface CarMapper {
|
||||||
|
|
||||||
|
CarDto carToCarDto(Car car);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isNotEmpty(String value) {
|
||||||
|
return value != null && !value.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
The generated mapper will look like:
|
||||||
|
|
||||||
|
.try-catch block in generated implementation
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
// GENERATED CODE
|
||||||
|
public class CarMapperImpl implements CarMapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CarDto carToCarDto(Car car) {
|
||||||
|
if ( car == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CarDto carDto = new CarDto();
|
||||||
|
|
||||||
|
if ( isNotEmpty( car.getOwner() ) ) {
|
||||||
|
carDto.setOwner( car.getOwner() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapping of other properties
|
||||||
|
|
||||||
|
return carDto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[IMPORTANT]
|
||||||
|
====
|
||||||
|
If there is a custom `@Condition` method applicable for the property it will have a precedence over a presence check method in the bean itself.
|
||||||
|
====
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
Methods annotated with `@Condition` in addition to the value of the source property can also have the source parameter as an input.
|
||||||
|
====
|
||||||
|
|
||||||
|
<<selection-based-on-qualifiers>> is also valid for `@Condition` methods.
|
||||||
|
In order to use a more specific condition method you will need to use one of `Mapping#conditionQualifiedByName` or `Mapping#conditionQualifiedBy`.
|
||||||
|
|
||||||
[[exceptions]]
|
[[exceptions]]
|
||||||
=== Exceptions
|
=== Exceptions
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import org.mapstruct.AfterMapping;
|
|||||||
import org.mapstruct.BeanMapping;
|
import org.mapstruct.BeanMapping;
|
||||||
import org.mapstruct.BeforeMapping;
|
import org.mapstruct.BeforeMapping;
|
||||||
import org.mapstruct.Builder;
|
import org.mapstruct.Builder;
|
||||||
|
import org.mapstruct.Condition;
|
||||||
import org.mapstruct.Context;
|
import org.mapstruct.Context;
|
||||||
import org.mapstruct.DecoratedWith;
|
import org.mapstruct.DecoratedWith;
|
||||||
import org.mapstruct.EnumMapping;
|
import org.mapstruct.EnumMapping;
|
||||||
@ -61,6 +62,7 @@ import org.mapstruct.tools.gem.GemDefinition;
|
|||||||
@GemDefinition(ValueMappings.class)
|
@GemDefinition(ValueMappings.class)
|
||||||
@GemDefinition(Context.class)
|
@GemDefinition(Context.class)
|
||||||
@GemDefinition(Builder.class)
|
@GemDefinition(Builder.class)
|
||||||
|
@GemDefinition(Condition.class)
|
||||||
|
|
||||||
@GemDefinition(MappingControl.class)
|
@GemDefinition(MappingControl.class)
|
||||||
@GemDefinition(MappingControls.class)
|
@GemDefinition(MappingControls.class)
|
||||||
|
@ -1155,6 +1155,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
.dependsOn( mapping.getDependsOn() )
|
.dependsOn( mapping.getDependsOn() )
|
||||||
.defaultValue( mapping.getDefaultValue() )
|
.defaultValue( mapping.getDefaultValue() )
|
||||||
.defaultJavaExpression( mapping.getDefaultJavaExpression() )
|
.defaultJavaExpression( mapping.getDefaultJavaExpression() )
|
||||||
|
.conditionJavaExpression( mapping.getConditionJavaExpression() )
|
||||||
.mirror( mapping.getMirror() )
|
.mirror( mapping.getMirror() )
|
||||||
.options( mapping )
|
.options( mapping )
|
||||||
.build();
|
.build();
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.internal.model;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.model.common.ModelElement;
|
||||||
|
import org.mapstruct.ap.internal.model.common.PresenceCheck;
|
||||||
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class MethodReferencePresenceCheck extends ModelElement implements PresenceCheck {
|
||||||
|
|
||||||
|
protected final MethodReference methodReference;
|
||||||
|
|
||||||
|
public MethodReferencePresenceCheck(MethodReference methodReference) {
|
||||||
|
this.methodReference = methodReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Type> getImportTypes() {
|
||||||
|
return methodReference.getImportTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodReference getMethodReference() {
|
||||||
|
return methodReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( o == null || getClass() != o.getClass() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MethodReferencePresenceCheck that = (MethodReferencePresenceCheck) o;
|
||||||
|
return Objects.equals( methodReference, that.methodReference );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash( methodReference );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.internal.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
|
import org.mapstruct.ap.internal.model.common.PresenceCheck;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public final class PresenceCheckMethodResolver {
|
||||||
|
|
||||||
|
private PresenceCheckMethodResolver() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PresenceCheck getPresenceCheck(
|
||||||
|
Method method,
|
||||||
|
SelectionParameters selectionParameters,
|
||||||
|
MappingBuilderContext ctx
|
||||||
|
) {
|
||||||
|
SelectedMethod<SourceMethod> matchingMethod = findMatchingPresenceCheckMethod(
|
||||||
|
method,
|
||||||
|
selectionParameters,
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( matchingMethod == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodReference methodReference = getPresenceCheckMethodReference( method, matchingMethod, ctx );
|
||||||
|
|
||||||
|
return new MethodReferencePresenceCheck( methodReference );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SelectedMethod<SourceMethod> findMatchingPresenceCheckMethod(
|
||||||
|
Method method,
|
||||||
|
SelectionParameters selectionParameters,
|
||||||
|
MappingBuilderContext ctx
|
||||||
|
) {
|
||||||
|
MethodSelectors selectors = new MethodSelectors(
|
||||||
|
ctx.getTypeUtils(),
|
||||||
|
ctx.getElementUtils(),
|
||||||
|
ctx.getTypeFactory(),
|
||||||
|
ctx.getMessager()
|
||||||
|
);
|
||||||
|
|
||||||
|
Type booleanType = ctx.getTypeFactory().getType( Boolean.class );
|
||||||
|
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
|
||||||
|
method,
|
||||||
|
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||||
|
Collections.emptyList(),
|
||||||
|
booleanType,
|
||||||
|
booleanType,
|
||||||
|
SelectionCriteria.forPresenceCheckMethods( selectionParameters )
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( matchingMethods.isEmpty() ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( matchingMethods.size() > 1 ) {
|
||||||
|
ctx.getMessager().printMessage(
|
||||||
|
method.getExecutable(),
|
||||||
|
Message.GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD,
|
||||||
|
selectionParameters.getSourceRHS().getSourceType().describe(),
|
||||||
|
matchingMethods.stream()
|
||||||
|
.map( SelectedMethod::getMethod )
|
||||||
|
.map( Method::describe )
|
||||||
|
.collect( Collectors.joining( ", " ) )
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingMethods.get( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodReference getPresenceCheckMethodReference(
|
||||||
|
Method method,
|
||||||
|
SelectedMethod<SourceMethod> matchingMethod,
|
||||||
|
MappingBuilderContext ctx
|
||||||
|
) {
|
||||||
|
|
||||||
|
Parameter providingParameter =
|
||||||
|
method.getContextProvidedMethods().getParameterForProvidedMethod( matchingMethod.getMethod() );
|
||||||
|
if ( providingParameter != null ) {
|
||||||
|
return MethodReference.forParameterProvidedMethod(
|
||||||
|
matchingMethod.getMethod(),
|
||||||
|
providingParameter,
|
||||||
|
matchingMethod.getParameterBindings()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MapperReference ref = MapperReference.findMapperReference(
|
||||||
|
ctx.getMapperReferences(),
|
||||||
|
matchingMethod.getMethod()
|
||||||
|
);
|
||||||
|
|
||||||
|
return MethodReference.forMapperReference(
|
||||||
|
matchingMethod.getMethod(),
|
||||||
|
ref,
|
||||||
|
matchingMethod.getParameterBindings()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<>( methodsProvidedByParams.size() + sourceModelMethods.size() );
|
||||||
|
|
||||||
|
for ( SourceMethod methodProvidedByParams : methodsProvidedByParams ) {
|
||||||
|
// add only methods from context that do have the @Condition annotation
|
||||||
|
if ( methodProvidedByParams.isPresenceCheck() ) {
|
||||||
|
availableMethods.add( methodProvidedByParams );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
availableMethods.addAll( sourceModelMethods );
|
||||||
|
|
||||||
|
return availableMethods;
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,9 @@ import java.util.Objects;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.gem.BuilderGem;
|
||||||
|
import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem;
|
||||||
|
import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem;
|
||||||
import org.mapstruct.ap.internal.model.assignment.AdderWrapper;
|
import org.mapstruct.ap.internal.model.assignment.AdderWrapper;
|
||||||
import org.mapstruct.ap.internal.model.assignment.ArrayCopyWrapper;
|
import org.mapstruct.ap.internal.model.assignment.ArrayCopyWrapper;
|
||||||
import org.mapstruct.ap.internal.model.assignment.EnumConstantWrapper;
|
import org.mapstruct.ap.internal.model.assignment.EnumConstantWrapper;
|
||||||
@ -32,6 +35,7 @@ import org.mapstruct.ap.internal.model.common.PresenceCheck;
|
|||||||
import org.mapstruct.ap.internal.model.common.SourceRHS;
|
import org.mapstruct.ap.internal.model.common.SourceRHS;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck;
|
import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck;
|
||||||
|
import org.mapstruct.ap.internal.model.presence.JavaExpressionPresenceCheck;
|
||||||
import org.mapstruct.ap.internal.model.presence.NullPresenceCheck;
|
import org.mapstruct.ap.internal.model.presence.NullPresenceCheck;
|
||||||
import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck;
|
import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck;
|
||||||
import org.mapstruct.ap.internal.model.source.DelegatingOptions;
|
import org.mapstruct.ap.internal.model.source.DelegatingOptions;
|
||||||
@ -40,9 +44,6 @@ import org.mapstruct.ap.internal.model.source.MappingOptions;
|
|||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||||
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||||
import org.mapstruct.ap.internal.gem.BuilderGem;
|
|
||||||
import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem;
|
|
||||||
import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem;
|
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.NativeTypes;
|
import org.mapstruct.ap.internal.util.NativeTypes;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
@ -52,12 +53,12 @@ import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
|||||||
|
|
||||||
import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS;
|
import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS;
|
||||||
import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE;
|
import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE;
|
||||||
|
import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT;
|
||||||
|
import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_NULL;
|
||||||
import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping;
|
import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping;
|
||||||
import static org.mapstruct.ap.internal.model.ForgedMethod.forParameterMapping;
|
import static org.mapstruct.ap.internal.model.ForgedMethod.forParameterMapping;
|
||||||
import static org.mapstruct.ap.internal.model.ForgedMethod.forPropertyMapping;
|
import static org.mapstruct.ap.internal.model.ForgedMethod.forPropertyMapping;
|
||||||
import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT;
|
import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT;
|
||||||
import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT;
|
|
||||||
import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_NULL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to
|
* Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to
|
||||||
@ -142,6 +143,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
// initial properties
|
// initial properties
|
||||||
private String defaultValue;
|
private String defaultValue;
|
||||||
private String defaultJavaExpression;
|
private String defaultJavaExpression;
|
||||||
|
private String conditionJavaExpression;
|
||||||
private SourceReference sourceReference;
|
private SourceReference sourceReference;
|
||||||
private SourceRHS rightHandSide;
|
private SourceRHS rightHandSide;
|
||||||
private FormattingParameters formattingParameters;
|
private FormattingParameters formattingParameters;
|
||||||
@ -182,6 +184,11 @@ public class PropertyMapping extends ModelElement {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PropertyMappingBuilder conditionJavaExpression(String conditionJavaExpression) {
|
||||||
|
this.conditionJavaExpression = conditionJavaExpression;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public PropertyMappingBuilder forgeMethodWithMappingReferences(MappingReferences mappingReferences) {
|
public PropertyMappingBuilder forgeMethodWithMappingReferences(MappingReferences mappingReferences) {
|
||||||
this.forgeMethodWithMappingReferences = mappingReferences;
|
this.forgeMethodWithMappingReferences = mappingReferences;
|
||||||
return this;
|
return this;
|
||||||
@ -557,13 +564,19 @@ public class PropertyMapping extends ModelElement {
|
|||||||
// simple property
|
// simple property
|
||||||
else if ( !sourceReference.isNested() ) {
|
else if ( !sourceReference.isNested() ) {
|
||||||
String sourceRef = sourceParam.getName() + "." + ValueProvider.of( propertyEntry.getReadAccessor() );
|
String sourceRef = sourceParam.getName() + "." + ValueProvider.of( propertyEntry.getReadAccessor() );
|
||||||
return new SourceRHS( sourceParam.getName(),
|
SourceRHS sourceRHS = new SourceRHS(
|
||||||
|
sourceParam.getName(),
|
||||||
sourceRef,
|
sourceRef,
|
||||||
getSourcePresenceCheckerRef( sourceReference ),
|
null,
|
||||||
propertyEntry.getType(),
|
propertyEntry.getType(),
|
||||||
existingVariableNames,
|
existingVariableNames,
|
||||||
sourceReference.toString()
|
sourceReference.toString()
|
||||||
);
|
);
|
||||||
|
sourceRHS.setSourcePresenceCheckerReference( getSourcePresenceCheckerRef(
|
||||||
|
sourceReference,
|
||||||
|
sourceRHS
|
||||||
|
) );
|
||||||
|
return sourceRHS;
|
||||||
}
|
}
|
||||||
// nested property given as dot path
|
// nested property given as dot path
|
||||||
else {
|
else {
|
||||||
@ -598,11 +611,15 @@ public class PropertyMapping extends ModelElement {
|
|||||||
String sourceRef = forgedName + "( " + sourceParam.getName() + " )";
|
String sourceRef = forgedName + "( " + sourceParam.getName() + " )";
|
||||||
SourceRHS sourceRhs = new SourceRHS( sourceParam.getName(),
|
SourceRHS sourceRhs = new SourceRHS( sourceParam.getName(),
|
||||||
sourceRef,
|
sourceRef,
|
||||||
getSourcePresenceCheckerRef( sourceReference ),
|
null,
|
||||||
sourceType,
|
sourceType,
|
||||||
existingVariableNames,
|
existingVariableNames,
|
||||||
sourceReference.toString()
|
sourceReference.toString()
|
||||||
);
|
);
|
||||||
|
sourceRhs.setSourcePresenceCheckerReference( getSourcePresenceCheckerRef(
|
||||||
|
sourceReference,
|
||||||
|
sourceRhs
|
||||||
|
) );
|
||||||
|
|
||||||
// create a local variable to which forged method can be assigned.
|
// create a local variable to which forged method can be assigned.
|
||||||
String desiredName = propertyEntry.getName();
|
String desiredName = propertyEntry.getName();
|
||||||
@ -613,7 +630,25 @@ public class PropertyMapping extends ModelElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReference ) {
|
private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReference,
|
||||||
|
SourceRHS sourceRHS) {
|
||||||
|
|
||||||
|
if ( conditionJavaExpression != null ) {
|
||||||
|
return new JavaExpressionPresenceCheck( conditionJavaExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionParameters selectionParameters = this.selectionParameters != null ?
|
||||||
|
this.selectionParameters.withSourceRHS( sourceRHS ) :
|
||||||
|
SelectionParameters.forSourceRHS( sourceRHS );
|
||||||
|
PresenceCheck presenceCheck = PresenceCheckMethodResolver.getPresenceCheck(
|
||||||
|
method,
|
||||||
|
selectionParameters,
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
if ( presenceCheck != null ) {
|
||||||
|
return presenceCheck;
|
||||||
|
}
|
||||||
|
|
||||||
PresenceCheck sourcePresenceChecker = null;
|
PresenceCheck sourcePresenceChecker = null;
|
||||||
if ( !sourceReference.getPropertyEntries().isEmpty() ) {
|
if ( !sourceReference.getPropertyEntries().isEmpty() ) {
|
||||||
Parameter sourceParam = sourceReference.getParameter();
|
Parameter sourceParam = sourceReference.getParameter();
|
||||||
|
@ -7,7 +7,6 @@ package org.mapstruct.ap.internal.model.common;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -31,7 +30,7 @@ public class SourceRHS extends ModelElement implements Assignment {
|
|||||||
private String sourceLoopVarName;
|
private String sourceLoopVarName;
|
||||||
private final Set<String> existingVariableNames;
|
private final Set<String> existingVariableNames;
|
||||||
private final String sourceErrorMessagePart;
|
private final String sourceErrorMessagePart;
|
||||||
private final PresenceCheck sourcePresenceCheckerReference;
|
private PresenceCheck sourcePresenceCheckerReference;
|
||||||
private boolean useElementAsSourceTypeForMatching = false;
|
private boolean useElementAsSourceTypeForMatching = false;
|
||||||
private final String sourceParameterName;
|
private final String sourceParameterName;
|
||||||
|
|
||||||
@ -65,6 +64,10 @@ public class SourceRHS extends ModelElement implements Assignment {
|
|||||||
return sourcePresenceCheckerReference;
|
return sourcePresenceCheckerReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSourcePresenceCheckerReference(PresenceCheck sourcePresenceCheckerReference) {
|
||||||
|
this.sourcePresenceCheckerReference = sourcePresenceCheckerReference;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getSourceType() {
|
public Type getSourceType() {
|
||||||
return sourceType;
|
return sourceType;
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
package org.mapstruct.ap.internal.model.presence;
|
package org.mapstruct.ap.internal.model.presence;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.internal.model.presence;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.model.common.ModelElement;
|
||||||
|
import org.mapstruct.ap.internal.model.common.PresenceCheck;
|
||||||
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class JavaExpressionPresenceCheck extends ModelElement implements PresenceCheck {
|
||||||
|
|
||||||
|
private final String javaExpression;
|
||||||
|
|
||||||
|
public JavaExpressionPresenceCheck(String javaExpression) {
|
||||||
|
this.javaExpression = javaExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJavaExpression() {
|
||||||
|
return javaExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Type> getImportTypes() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( o == null || getClass() != o.getClass() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JavaExpressionPresenceCheck that = (JavaExpressionPresenceCheck) o;
|
||||||
|
return Objects.equals( javaExpression, that.javaExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash( javaExpression );
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
private final String constant;
|
private final String constant;
|
||||||
private final String javaExpression;
|
private final String javaExpression;
|
||||||
private final String defaultJavaExpression;
|
private final String defaultJavaExpression;
|
||||||
|
private final String conditionJavaExpression;
|
||||||
private final String targetName;
|
private final String targetName;
|
||||||
private final String defaultValue;
|
private final String defaultValue;
|
||||||
private final FormattingParameters formattingParameters;
|
private final FormattingParameters formattingParameters;
|
||||||
@ -114,6 +115,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
String constant = mapping.constant().getValue();
|
String constant = mapping.constant().getValue();
|
||||||
String expression = getExpression( mapping, method, messager );
|
String expression = getExpression( mapping, method, messager );
|
||||||
String defaultExpression = getDefaultExpression( mapping, method, messager );
|
String defaultExpression = getDefaultExpression( mapping, method, messager );
|
||||||
|
String conditionExpression = getConditionExpression( mapping, method, messager );
|
||||||
String dateFormat = mapping.dateFormat().getValue();
|
String dateFormat = mapping.dateFormat().getValue();
|
||||||
String numberFormat = mapping.numberFormat().getValue();
|
String numberFormat = mapping.numberFormat().getValue();
|
||||||
String defaultValue = mapping.defaultValue().getValue();
|
String defaultValue = mapping.defaultValue().getValue();
|
||||||
@ -132,6 +134,8 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
SelectionParameters selectionParams = new SelectionParameters(
|
SelectionParameters selectionParams = new SelectionParameters(
|
||||||
mapping.qualifiedBy().get(),
|
mapping.qualifiedBy().get(),
|
||||||
mapping.qualifiedByName().get(),
|
mapping.qualifiedByName().get(),
|
||||||
|
mapping.conditionQualifiedBy().get(),
|
||||||
|
mapping.conditionQualifiedByName().get(),
|
||||||
mapping.resultType().getValue(),
|
mapping.resultType().getValue(),
|
||||||
typeUtils
|
typeUtils
|
||||||
);
|
);
|
||||||
@ -145,6 +149,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
constant,
|
constant,
|
||||||
expression,
|
expression,
|
||||||
defaultExpression,
|
defaultExpression,
|
||||||
|
conditionExpression,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
mapping.ignore().get(),
|
mapping.ignore().get(),
|
||||||
formattingParam,
|
formattingParam,
|
||||||
@ -174,6 +179,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
true,
|
true,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -261,6 +267,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
String constant,
|
String constant,
|
||||||
String javaExpression,
|
String javaExpression,
|
||||||
String defaultJavaExpression,
|
String defaultJavaExpression,
|
||||||
|
String conditionJavaExpression,
|
||||||
String defaultValue,
|
String defaultValue,
|
||||||
boolean isIgnored,
|
boolean isIgnored,
|
||||||
FormattingParameters formattingParameters,
|
FormattingParameters formattingParameters,
|
||||||
@ -279,6 +286,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
this.constant = constant;
|
this.constant = constant;
|
||||||
this.javaExpression = javaExpression;
|
this.javaExpression = javaExpression;
|
||||||
this.defaultJavaExpression = defaultJavaExpression;
|
this.defaultJavaExpression = defaultJavaExpression;
|
||||||
|
this.conditionJavaExpression = conditionJavaExpression;
|
||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
this.isIgnored = isIgnored;
|
this.isIgnored = isIgnored;
|
||||||
this.formattingParameters = formattingParameters;
|
this.formattingParameters = formattingParameters;
|
||||||
@ -330,6 +338,27 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
return javaExpressionMatcher.group( 1 ).trim();
|
return javaExpressionMatcher.group( 1 ).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getConditionExpression(MappingGem mapping, ExecutableElement element,
|
||||||
|
FormattingMessager messager) {
|
||||||
|
if ( !mapping.conditionExpression().hasValue() ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( mapping.conditionExpression().get() );
|
||||||
|
|
||||||
|
if ( !javaExpressionMatcher.matches() ) {
|
||||||
|
messager.printMessage(
|
||||||
|
element,
|
||||||
|
mapping.mirror(),
|
||||||
|
mapping.conditionExpression().getAnnotationValue(),
|
||||||
|
Message.PROPERTYMAPPING_INVALID_CONDITION_EXPRESSION
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return javaExpressionMatcher.group( 1 ).trim();
|
||||||
|
}
|
||||||
|
|
||||||
public String getTargetName() {
|
public String getTargetName() {
|
||||||
return targetName;
|
return targetName;
|
||||||
}
|
}
|
||||||
@ -364,6 +393,10 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
return defaultJavaExpression;
|
return defaultJavaExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getConditionJavaExpression() {
|
||||||
|
return conditionJavaExpression;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDefaultValue() {
|
public String getDefaultValue() {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
@ -452,6 +485,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
null, // constant
|
null, // constant
|
||||||
null, // expression
|
null, // expression
|
||||||
null, // defaultExpression
|
null, // defaultExpression
|
||||||
|
null, // conditionExpression
|
||||||
null,
|
null,
|
||||||
isIgnored,
|
isIgnored,
|
||||||
formattingParameters,
|
formattingParameters,
|
||||||
@ -481,6 +515,7 @@ public class MappingOptions extends DelegatingOptions {
|
|||||||
constant,
|
constant,
|
||||||
javaExpression,
|
javaExpression,
|
||||||
defaultJavaExpression,
|
defaultJavaExpression,
|
||||||
|
conditionJavaExpression,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
isIgnored,
|
isIgnored,
|
||||||
formattingParameters,
|
formattingParameters,
|
||||||
|
@ -90,6 +90,14 @@ public interface Method {
|
|||||||
*/
|
*/
|
||||||
boolean isObjectFactory();
|
boolean isObjectFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the method is designated as a presence check method
|
||||||
|
* @return {@code true} if it is a presence check method
|
||||||
|
*/
|
||||||
|
default boolean isPresenceCheck() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the parameter designated as target type (if present) {@link org.mapstruct.TargetType }
|
* Returns the parameter designated as target type (if present) {@link org.mapstruct.TargetType }
|
||||||
*
|
*
|
||||||
|
@ -23,6 +23,8 @@ public class SelectionParameters {
|
|||||||
|
|
||||||
private final List<TypeMirror> qualifiers;
|
private final List<TypeMirror> qualifiers;
|
||||||
private final List<String> qualifyingNames;
|
private final List<String> qualifyingNames;
|
||||||
|
private final List<TypeMirror> conditionQualifiers;
|
||||||
|
private final List<String> conditionQualifyingNames;
|
||||||
private final TypeMirror resultType;
|
private final TypeMirror resultType;
|
||||||
private final TypeUtils typeUtils;
|
private final TypeUtils typeUtils;
|
||||||
private final SourceRHS sourceRHS;
|
private final SourceRHS sourceRHS;
|
||||||
@ -39,6 +41,8 @@ public class SelectionParameters {
|
|||||||
return new SelectionParameters(
|
return new SelectionParameters(
|
||||||
selectionParameters.qualifiers,
|
selectionParameters.qualifiers,
|
||||||
selectionParameters.qualifyingNames,
|
selectionParameters.qualifyingNames,
|
||||||
|
selectionParameters.conditionQualifiers,
|
||||||
|
selectionParameters.conditionQualifyingNames,
|
||||||
null,
|
null,
|
||||||
selectionParameters.typeUtils
|
selectionParameters.typeUtils
|
||||||
);
|
);
|
||||||
@ -46,13 +50,32 @@ public class SelectionParameters {
|
|||||||
|
|
||||||
public SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames, TypeMirror resultType,
|
public SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames, TypeMirror resultType,
|
||||||
TypeUtils typeUtils) {
|
TypeUtils typeUtils) {
|
||||||
this( qualifiers, qualifyingNames, resultType, typeUtils, null );
|
this(
|
||||||
|
qualifiers,
|
||||||
|
qualifyingNames,
|
||||||
|
Collections.emptyList(),
|
||||||
|
Collections.emptyList(),
|
||||||
|
resultType,
|
||||||
|
typeUtils,
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames, TypeMirror resultType,
|
public SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames,
|
||||||
|
List<TypeMirror> conditionQualifiers, List<String> conditionQualifyingNames,
|
||||||
|
TypeMirror resultType,
|
||||||
|
TypeUtils typeUtils) {
|
||||||
|
this( qualifiers, qualifyingNames, conditionQualifiers, conditionQualifyingNames, resultType, typeUtils, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames,
|
||||||
|
List<TypeMirror> conditionQualifiers, List<String> conditionQualifyingNames,
|
||||||
|
TypeMirror resultType,
|
||||||
TypeUtils typeUtils, SourceRHS sourceRHS) {
|
TypeUtils typeUtils, SourceRHS sourceRHS) {
|
||||||
this.qualifiers = qualifiers;
|
this.qualifiers = qualifiers;
|
||||||
this.qualifyingNames = qualifyingNames;
|
this.qualifyingNames = qualifyingNames;
|
||||||
|
this.conditionQualifiers = conditionQualifiers;
|
||||||
|
this.conditionQualifyingNames = conditionQualifyingNames;
|
||||||
this.resultType = resultType;
|
this.resultType = resultType;
|
||||||
this.typeUtils = typeUtils;
|
this.typeUtils = typeUtils;
|
||||||
this.sourceRHS = sourceRHS;
|
this.sourceRHS = sourceRHS;
|
||||||
@ -74,6 +97,21 @@ public class SelectionParameters {
|
|||||||
return qualifyingNames;
|
return qualifyingNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return qualifiers used for further select the appropriate presence check method based on class and name
|
||||||
|
*/
|
||||||
|
public List<TypeMirror> getConditionQualifiers() {
|
||||||
|
return conditionQualifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return qualifyingNames, used in combination with with @Named
|
||||||
|
* @see #getConditionQualifiers()
|
||||||
|
*/
|
||||||
|
public List<String> getConditionQualifyingNames() {
|
||||||
|
return conditionQualifyingNames;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return resultType used for further select the appropriate mapping method based on resultType (bean mapping)
|
* @return resultType used for further select the appropriate mapping method based on resultType (bean mapping)
|
||||||
@ -119,6 +157,14 @@ public class SelectionParameters {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !Objects.equals( this.conditionQualifiers, other.conditionQualifiers ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !Objects.equals( this.conditionQualifyingNames, other.conditionQualifyingNames ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( !Objects.equals( this.sourceRHS, other.sourceRHS ) ) {
|
if ( !Objects.equals( this.sourceRHS, other.sourceRHS ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -151,8 +197,22 @@ public class SelectionParameters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SelectionParameters withSourceRHS(SourceRHS sourceRHS) {
|
||||||
|
return new SelectionParameters(
|
||||||
|
this.qualifiers,
|
||||||
|
this.qualifyingNames,
|
||||||
|
this.conditionQualifiers,
|
||||||
|
this.conditionQualifyingNames,
|
||||||
|
null,
|
||||||
|
this.typeUtils,
|
||||||
|
sourceRHS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static SelectionParameters forSourceRHS(SourceRHS sourceRHS) {
|
public static SelectionParameters forSourceRHS(SourceRHS sourceRHS) {
|
||||||
return new SelectionParameters(
|
return new SelectionParameters(
|
||||||
|
Collections.emptyList(),
|
||||||
|
Collections.emptyList(),
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
null,
|
null,
|
||||||
|
@ -14,6 +14,8 @@ import java.util.stream.Collectors;
|
|||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.gem.ConditionGem;
|
||||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.common.Accessibility;
|
import org.mapstruct.ap.internal.model.common.Accessibility;
|
||||||
@ -47,6 +49,7 @@ public class SourceMethod implements Method {
|
|||||||
private final Parameter mappingTargetParameter;
|
private final Parameter mappingTargetParameter;
|
||||||
private final Parameter targetTypeParameter;
|
private final Parameter targetTypeParameter;
|
||||||
private final boolean isObjectFactory;
|
private final boolean isObjectFactory;
|
||||||
|
private final boolean isPresenceCheck;
|
||||||
private final Type returnType;
|
private final Type returnType;
|
||||||
private final Accessibility accessibility;
|
private final Accessibility accessibility;
|
||||||
private final List<Type> exceptionTypes;
|
private final List<Type> exceptionTypes;
|
||||||
@ -230,6 +233,7 @@ public class SourceMethod implements Method {
|
|||||||
this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters );
|
this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters );
|
||||||
this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null;
|
this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null;
|
||||||
this.isObjectFactory = determineIfIsObjectFactory();
|
this.isObjectFactory = determineIfIsObjectFactory();
|
||||||
|
this.isPresenceCheck = determineIfIsPresenceCheck();
|
||||||
|
|
||||||
this.typeUtils = builder.typeUtils;
|
this.typeUtils = builder.typeUtils;
|
||||||
this.typeFactory = builder.typeFactory;
|
this.typeFactory = builder.typeFactory;
|
||||||
@ -247,6 +251,19 @@ public class SourceMethod implements Method {
|
|||||||
&& ( hasObjectFactoryAnnotation || hasNoSourceParameters );
|
&& ( hasObjectFactoryAnnotation || hasNoSourceParameters );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean determineIfIsPresenceCheck() {
|
||||||
|
if ( returnType.isPrimitive() ) {
|
||||||
|
if ( !returnType.getName().equals( "boolean" ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !returnType.getFullyQualifiedName().equals( Boolean.class.getCanonicalName() ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConditionGem.instanceOn( executable ) != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getDeclaringMapper() {
|
public Type getDeclaringMapper() {
|
||||||
return declaringMapper;
|
return declaringMapper;
|
||||||
@ -519,6 +536,11 @@ public class SourceMethod implements Method {
|
|||||||
return Executables.isLifecycleCallbackMethod( getExecutable() );
|
return Executables.isLifecycleCallbackMethod( getExecutable() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPresenceCheck() {
|
||||||
|
return isPresenceCheck;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAfterMappingMethod() {
|
public boolean isAfterMappingMethod() {
|
||||||
return Executables.isAfterMappingMethod( getExecutable() );
|
return Executables.isAfterMappingMethod( getExecutable() );
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,8 @@ public class CreateOrUpdateSelector implements MethodSelector {
|
|||||||
Type returnType,
|
Type returnType,
|
||||||
SelectionCriteria criteria) {
|
SelectionCriteria criteria) {
|
||||||
|
|
||||||
if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired() ) {
|
if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired()
|
||||||
|
|| criteria.isPresenceCheckRequired() ) {
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,9 @@ public class MethodFamilySelector implements MethodSelector {
|
|||||||
List<SelectedMethod<T>> result = new ArrayList<>( methods.size() );
|
List<SelectedMethod<T>> result = new ArrayList<>( methods.size() );
|
||||||
for ( SelectedMethod<T> method : methods ) {
|
for ( SelectedMethod<T> method : methods ) {
|
||||||
if ( method.getMethod().isObjectFactory() == criteria.isObjectFactoryRequired()
|
if ( method.getMethod().isObjectFactory() == criteria.isObjectFactoryRequired()
|
||||||
&& method.getMethod().isLifecycleCallbackMethod() == criteria.isLifecycleCallbackRequired() ) {
|
&& method.getMethod().isLifecycleCallbackMethod() == criteria.isLifecycleCallbackRequired()
|
||||||
|
&& method.getMethod().isPresenceCheck() == criteria.isPresenceCheckRequired()
|
||||||
|
) {
|
||||||
|
|
||||||
result.add( method );
|
result.add( method );
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ public class MethodSelectors {
|
|||||||
new XmlElementDeclSelector( typeUtils ),
|
new XmlElementDeclSelector( typeUtils ),
|
||||||
new InheritanceSelector(),
|
new InheritanceSelector(),
|
||||||
new CreateOrUpdateSelector(),
|
new CreateOrUpdateSelector(),
|
||||||
|
new SourceRhsSelector(),
|
||||||
new FactoryParameterSelector() );
|
new FactoryParameterSelector() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,20 +26,23 @@ public class SelectionCriteria {
|
|||||||
private final String targetPropertyName;
|
private final String targetPropertyName;
|
||||||
private final TypeMirror qualifyingResultType;
|
private final TypeMirror qualifyingResultType;
|
||||||
private final SourceRHS sourceRHS;
|
private final SourceRHS sourceRHS;
|
||||||
private boolean preferUpdateMapping;
|
private Type type;
|
||||||
private final boolean objectFactoryRequired;
|
|
||||||
private final boolean lifecycleCallbackRequired;
|
|
||||||
private final boolean allowDirect;
|
private final boolean allowDirect;
|
||||||
private final boolean allowConversion;
|
private final boolean allowConversion;
|
||||||
private final boolean allowMappingMethod;
|
private final boolean allowMappingMethod;
|
||||||
private final boolean allow2Steps;
|
private final boolean allow2Steps;
|
||||||
|
|
||||||
public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl,
|
public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl,
|
||||||
String targetPropertyName, boolean preferUpdateMapping, boolean objectFactoryRequired,
|
String targetPropertyName, Type type) {
|
||||||
boolean lifecycleCallbackRequired) {
|
|
||||||
if ( selectionParameters != null ) {
|
if ( selectionParameters != null ) {
|
||||||
|
if ( type == Type.PRESENCE_CHECK ) {
|
||||||
|
qualifiers.addAll( selectionParameters.getConditionQualifiers() );
|
||||||
|
qualifiedByNames.addAll( selectionParameters.getConditionQualifyingNames() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
qualifiers.addAll( selectionParameters.getQualifiers() );
|
qualifiers.addAll( selectionParameters.getQualifiers() );
|
||||||
qualifiedByNames.addAll( selectionParameters.getQualifyingNames() );
|
qualifiedByNames.addAll( selectionParameters.getQualifyingNames() );
|
||||||
|
}
|
||||||
qualifyingResultType = selectionParameters.getResultType();
|
qualifyingResultType = selectionParameters.getResultType();
|
||||||
sourceRHS = selectionParameters.getSourceRHS();
|
sourceRHS = selectionParameters.getSourceRHS();
|
||||||
}
|
}
|
||||||
@ -60,23 +63,28 @@ public class SelectionCriteria {
|
|||||||
this.allow2Steps = true;
|
this.allow2Steps = true;
|
||||||
}
|
}
|
||||||
this.targetPropertyName = targetPropertyName;
|
this.targetPropertyName = targetPropertyName;
|
||||||
this.preferUpdateMapping = preferUpdateMapping;
|
this.type = type;
|
||||||
this.objectFactoryRequired = objectFactoryRequired;
|
|
||||||
this.lifecycleCallbackRequired = lifecycleCallbackRequired;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if factory methods should be selected, false otherwise.
|
* @return true if factory methods should be selected, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isObjectFactoryRequired() {
|
public boolean isObjectFactoryRequired() {
|
||||||
return objectFactoryRequired;
|
return type == Type.OBJECT_FACTORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if lifecycle callback methods should be selected, false otherwise.
|
* @return true if lifecycle callback methods should be selected, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isLifecycleCallbackRequired() {
|
public boolean isLifecycleCallbackRequired() {
|
||||||
return lifecycleCallbackRequired;
|
return type == Type.LIFECYCLE_CALLBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if presence check methods should be selected, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public boolean isPresenceCheckRequired() {
|
||||||
|
return type == Type.PRESENCE_CHECK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TypeMirror> getQualifiers() {
|
public List<TypeMirror> getQualifiers() {
|
||||||
@ -96,7 +104,7 @@ public class SelectionCriteria {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPreferUpdateMapping() {
|
public boolean isPreferUpdateMapping() {
|
||||||
return preferUpdateMapping;
|
return type == Type.PREFER_UPDATE_MAPPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceRHS getSourceRHS() {
|
public SourceRHS getSourceRHS() {
|
||||||
@ -104,7 +112,7 @@ public class SelectionCriteria {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setPreferUpdateMapping(boolean preferUpdateMapping) {
|
public void setPreferUpdateMapping(boolean preferUpdateMapping) {
|
||||||
this.preferUpdateMapping = preferUpdateMapping;
|
this.type = preferUpdateMapping ? Type.PREFER_UPDATE_MAPPING : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasQualfiers() {
|
public boolean hasQualfiers() {
|
||||||
@ -135,17 +143,26 @@ public class SelectionCriteria {
|
|||||||
selectionParameters,
|
selectionParameters,
|
||||||
mappingControl,
|
mappingControl,
|
||||||
targetPropertyName,
|
targetPropertyName,
|
||||||
preferUpdateMapping,
|
preferUpdateMapping ? Type.PREFER_UPDATE_MAPPING : null
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SelectionCriteria forFactoryMethods(SelectionParameters selectionParameters) {
|
public static SelectionCriteria forFactoryMethods(SelectionParameters selectionParameters) {
|
||||||
return new SelectionCriteria( selectionParameters, null, null, false, true, false );
|
return new SelectionCriteria( selectionParameters, null, null, Type.OBJECT_FACTORY );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SelectionCriteria forLifecycleMethods(SelectionParameters selectionParameters) {
|
public static SelectionCriteria forLifecycleMethods(SelectionParameters selectionParameters) {
|
||||||
return new SelectionCriteria( selectionParameters, null, null, false, false, true );
|
return new SelectionCriteria( selectionParameters, null, null, Type.LIFECYCLE_CALLBACK );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectionCriteria forPresenceCheckMethods(SelectionParameters selectionParameters) {
|
||||||
|
return new SelectionCriteria( selectionParameters, null, null, Type.PRESENCE_CHECK );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
PREFER_UPDATE_MAPPING,
|
||||||
|
OBJECT_FACTORY,
|
||||||
|
LIFECYCLE_CALLBACK,
|
||||||
|
PRESENCE_CHECK,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.internal.model.source.selector;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||||
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector that tries to resolve an ambiquity between methods that contain source parameters and
|
||||||
|
* {@link org.mapstruct.ap.internal.model.common.SourceRHS SourceRHS} type parameters.
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class SourceRhsSelector implements MethodSelector {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(Method mappingMethod,
|
||||||
|
List<SelectedMethod<T>> candidates,
|
||||||
|
List<Type> sourceTypes, Type mappingTargetType,
|
||||||
|
Type returnType, SelectionCriteria criteria) {
|
||||||
|
if ( candidates.size() < 2 || criteria.getSourceRHS() == null ) {
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SelectedMethod<T>> sourceRHSFavoringCandidates = new ArrayList<>();
|
||||||
|
|
||||||
|
for ( SelectedMethod<T> candidate : candidates ) {
|
||||||
|
for ( ParameterBinding parameterBinding : candidate.getParameterBindings() ) {
|
||||||
|
if ( parameterBinding.getSourceRHS() != null ) {
|
||||||
|
sourceRHSFavoringCandidates.add( candidate );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !sourceRHSFavoringCandidates.isEmpty() ) {
|
||||||
|
return sourceRHSFavoringCandidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
}
|
@ -89,7 +89,7 @@ public class TypeSelector implements MethodSelector {
|
|||||||
List<ParameterBinding> availableParams = new ArrayList<>( method.getParameters().size() + 3 );
|
List<ParameterBinding> availableParams = new ArrayList<>( method.getParameters().size() + 3 );
|
||||||
|
|
||||||
if ( sourceRHS != null ) {
|
if ( sourceRHS != null ) {
|
||||||
availableParams.addAll( ParameterBinding.fromParameters( method.getContextParameters() ) );
|
availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) );
|
||||||
availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) );
|
availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -23,6 +23,7 @@ import javax.lang.model.type.ExecutableType;
|
|||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.gem.BeanMappingGem;
|
import org.mapstruct.ap.internal.gem.BeanMappingGem;
|
||||||
|
import org.mapstruct.ap.internal.gem.ConditionGem;
|
||||||
import org.mapstruct.ap.internal.gem.IterableMappingGem;
|
import org.mapstruct.ap.internal.gem.IterableMappingGem;
|
||||||
import org.mapstruct.ap.internal.gem.MapMappingGem;
|
import org.mapstruct.ap.internal.gem.MapMappingGem;
|
||||||
import org.mapstruct.ap.internal.gem.MappingGem;
|
import org.mapstruct.ap.internal.gem.MappingGem;
|
||||||
@ -225,7 +226,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
}
|
}
|
||||||
// otherwise add reference to existing mapper method
|
// otherwise add reference to existing mapper method
|
||||||
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( method, parameters, returnType )
|
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( method, parameters, returnType )
|
||||||
|| isValidLifecycleCallbackMethod( method ) ) {
|
|| isValidLifecycleCallbackMethod( method )
|
||||||
|
|| isValidPresenceCheckMethod( method, returnType ) ) {
|
||||||
return getReferencedMethod( usedMapper, methodType, method, mapperToImplement, parameters );
|
return getReferencedMethod( usedMapper, methodType, method, mapperToImplement, parameters );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -333,7 +335,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
|
|
||||||
List<SourceMethod> contextProvidedMethods = new ArrayList<>( contextParamMethods.size() );
|
List<SourceMethod> contextProvidedMethods = new ArrayList<>( contextParamMethods.size() );
|
||||||
for ( SourceMethod sourceMethod : contextParamMethods ) {
|
for ( SourceMethod sourceMethod : contextParamMethods ) {
|
||||||
if ( sourceMethod.isLifecycleCallbackMethod() || sourceMethod.isObjectFactory() ) {
|
if ( sourceMethod.isLifecycleCallbackMethod() || sourceMethod.isObjectFactory()
|
||||||
|
|| sourceMethod.isPresenceCheck() ) {
|
||||||
contextProvidedMethods.add( sourceMethod );
|
contextProvidedMethods.add( sourceMethod );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,10 +392,22 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
return ObjectFactoryGem.instanceOn( method ) != null;
|
return ObjectFactoryGem.instanceOn( method ) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isValidPresenceCheckMethod(ExecutableElement method, Type returnType) {
|
||||||
|
return isBoolean( returnType ) && hasConditionAnnotation( method );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasConditionAnnotation(ExecutableElement method) {
|
||||||
|
return ConditionGem.instanceOn( method ) != null;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isVoid(Type returnType) {
|
private boolean isVoid(Type returnType) {
|
||||||
return returnType.getTypeMirror().getKind() == TypeKind.VOID;
|
return returnType.getTypeMirror().getKind() == TypeKind.VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBoolean(Type returnType) {
|
||||||
|
return Boolean.class.getCanonicalName().equals( returnType.getBoxedEquivalent().getFullyQualifiedName() );
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isValidReferencedOrFactoryMethod(int sourceParamCount, int targetParamCount,
|
private boolean isValidReferencedOrFactoryMethod(int sourceParamCount, int targetParamCount,
|
||||||
List<Parameter> parameters) {
|
List<Parameter> parameters) {
|
||||||
int validSourceParameters = 0;
|
int validSourceParameters = 0;
|
||||||
|
@ -66,6 +66,7 @@ public enum Message {
|
|||||||
PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED("Expression and a qualifier both defined in @Mapping, either define an expression or a qualifier."),
|
PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED("Expression and a qualifier both defined in @Mapping, either define an expression or a qualifier."),
|
||||||
PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java(<EXPRESSION>)\"." ),
|
PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java(<EXPRESSION>)\"." ),
|
||||||
PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java(<EXPRESSION>)\"." ),
|
PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java(<EXPRESSION>)\"." ),
|
||||||
|
PROPERTYMAPPING_INVALID_CONDITION_EXPRESSION( "Value for condition expression must be given in the form \"java(<EXPRESSION>)\"." ),
|
||||||
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ),
|
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ),
|
||||||
PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ),
|
PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ),
|
||||||
PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s). Did you mean \"%s\"?" ),
|
PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s). Did you mean \"%s\"?" ),
|
||||||
@ -120,6 +121,7 @@ public enum Message {
|
|||||||
GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ),
|
GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ),
|
||||||
GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ),
|
GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ),
|
||||||
GENERAL_AMBIGUOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ),
|
GENERAL_AMBIGUOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ),
|
||||||
|
GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD( "Ambiguous presence check methods found for checking %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ),
|
||||||
GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ),
|
GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ),
|
||||||
GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ),
|
GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ),
|
||||||
GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ),
|
GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ),
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<#--
|
||||||
|
|
||||||
|
Copyright MapStruct Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
-->
|
||||||
|
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" -->
|
||||||
|
<@includeModel object=methodReference/>
|
@ -0,0 +1,9 @@
|
|||||||
|
<#--
|
||||||
|
|
||||||
|
Copyright MapStruct Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
-->
|
||||||
|
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.JavaExpressionPresenceCheck" -->
|
||||||
|
${javaExpression}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional;
|
||||||
|
|
||||||
|
public class Employee {
|
||||||
|
private String name;
|
||||||
|
private String ssid;
|
||||||
|
private String nin;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSsid() {
|
||||||
|
return ssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSsid(String ssid) {
|
||||||
|
this.ssid = ssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNin() {
|
||||||
|
return nin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNin(String nin) {
|
||||||
|
this.nin = nin;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional;
|
||||||
|
|
||||||
|
public class EmployeeDto {
|
||||||
|
private String name;
|
||||||
|
private String country;
|
||||||
|
private String uniqueIdNumber;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountry() {
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCountry(String country) {
|
||||||
|
this.country = country;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUniqueIdNumber() {
|
||||||
|
return uniqueIdNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUniqueIdNumber(String uniqueIdNumber) {
|
||||||
|
this.uniqueIdNumber = uniqueIdNumber;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class BasicEmployee {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class BasicEmployeeDto {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String strategy;
|
||||||
|
|
||||||
|
public BasicEmployeeDto(String name) {
|
||||||
|
this( name, "default" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicEmployeeDto(String name, String strategy) {
|
||||||
|
this.name = name;
|
||||||
|
this.strategy = strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStrategy() {
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
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.ap.testutil.runner.GeneratedSource;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("2051")
|
||||||
|
@WithClasses({
|
||||||
|
BasicEmployee.class,
|
||||||
|
BasicEmployeeDto.class
|
||||||
|
})
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
public class ConditionalMappingTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final GeneratedSource generatedSource = new GeneratedSource();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodInMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodInMapper() {
|
||||||
|
generatedSource.addComparisonToFixtureFor( ConditionalMethodInMapper.class );
|
||||||
|
ConditionalMethodInMapper mapper = ConditionalMethodInMapper.INSTANCE;
|
||||||
|
|
||||||
|
BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( "Tester" );
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( "" ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( " " ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodAndBeanPresenceCheckMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodAndBeanPresenceCheckMapper() {
|
||||||
|
ConditionalMethodAndBeanPresenceCheckMapper mapper = ConditionalMethodAndBeanPresenceCheckMapper.INSTANCE;
|
||||||
|
|
||||||
|
BasicEmployee employee = mapper.map( new ConditionalMethodAndBeanPresenceCheckMapper.EmployeeDto( "Tester" ) );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( "Tester" );
|
||||||
|
|
||||||
|
employee = mapper.map( new ConditionalMethodAndBeanPresenceCheckMapper.EmployeeDto( "" ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
|
||||||
|
employee = mapper.map( new ConditionalMethodAndBeanPresenceCheckMapper.EmployeeDto( " " ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodInUsesMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodInUsesMapper() {
|
||||||
|
ConditionalMethodInUsesMapper mapper = ConditionalMethodInUsesMapper.INSTANCE;
|
||||||
|
|
||||||
|
BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( "Tester" );
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( "" ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( " " ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodInUsesStaticMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodInUsesStaticMapper() {
|
||||||
|
ConditionalMethodInUsesStaticMapper mapper = ConditionalMethodInUsesStaticMapper.INSTANCE;
|
||||||
|
|
||||||
|
BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( "Tester" );
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( "" ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( " " ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodInContextMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodInUsesContextMapper() {
|
||||||
|
ConditionalMethodInContextMapper mapper = ConditionalMethodInContextMapper.INSTANCE;
|
||||||
|
|
||||||
|
ConditionalMethodInContextMapper.PresenceUtils utils = new ConditionalMethodInContextMapper.PresenceUtils();
|
||||||
|
BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ), utils );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( "Tester" );
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( "" ), utils );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( " " ), utils );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodWithSourceParameterMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodWithSourceParameter() {
|
||||||
|
ConditionalMethodWithSourceParameterMapper mapper = ConditionalMethodWithSourceParameterMapper.INSTANCE;
|
||||||
|
|
||||||
|
BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( "Tester", "map" ) );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( "Tester" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodWithSourceParameterAndValueMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodWithSourceParameterAndValue() {
|
||||||
|
generatedSource.addComparisonToFixtureFor( ConditionalMethodWithSourceParameterAndValueMapper.class );
|
||||||
|
ConditionalMethodWithSourceParameterAndValueMapper mapper =
|
||||||
|
ConditionalMethodWithSourceParameterAndValueMapper.INSTANCE;
|
||||||
|
|
||||||
|
BasicEmployee employee = mapper.map( new BasicEmployeeDto( " ", "empty" ) );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( " " );
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( " ", "blank" ) );
|
||||||
|
assertThat( employee.getName() ).isNull();
|
||||||
|
|
||||||
|
employee = mapper.map( new BasicEmployeeDto( "Tester", "blank" ) );
|
||||||
|
assertThat( employee.getName() ).isEqualTo( "Tester" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ErroneousAmbiguousConditionalMethodMapper.class
|
||||||
|
})
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(
|
||||||
|
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||||
|
type = ErroneousAmbiguousConditionalMethodMapper.class,
|
||||||
|
line = 17,
|
||||||
|
message = "Ambiguous presence check methods found for checking String: " +
|
||||||
|
"boolean isNotBlank(String value), " +
|
||||||
|
"boolean isNotEmpty(String value). " +
|
||||||
|
"See https://mapstruct.org/faq/#ambiguous for more info."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void ambiguousConditionalMethod() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodForCollectionMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodForCollection() {
|
||||||
|
ConditionalMethodForCollectionMapper mapper = ConditionalMethodForCollectionMapper.INSTANCE;
|
||||||
|
|
||||||
|
ConditionalMethodForCollectionMapper.Author author = new ConditionalMethodForCollectionMapper.Author();
|
||||||
|
ConditionalMethodForCollectionMapper.AuthorDto dto = mapper.map( author );
|
||||||
|
|
||||||
|
assertThat( dto.getBooks() ).isNull();
|
||||||
|
|
||||||
|
author.setBooks( Collections.emptyList() );
|
||||||
|
dto = mapper.map( author );
|
||||||
|
|
||||||
|
assertThat( dto.getBooks() ).isNull();
|
||||||
|
|
||||||
|
author.setBooks( Arrays.asList(
|
||||||
|
new ConditionalMethodForCollectionMapper.Book( "Test" ),
|
||||||
|
new ConditionalMethodForCollectionMapper.Book( "Test Vol. 2" )
|
||||||
|
) );
|
||||||
|
dto = mapper.map( author );
|
||||||
|
|
||||||
|
assertThat( dto.getBooks() )
|
||||||
|
.extracting( ConditionalMethodForCollectionMapper.BookDto::getName )
|
||||||
|
.containsExactly( "Test", "Test Vol. 2" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
OptionalLikeConditionalMapper.class
|
||||||
|
})
|
||||||
|
@IssueKey("2084")
|
||||||
|
public void optionalLikeConditional() {
|
||||||
|
OptionalLikeConditionalMapper mapper = OptionalLikeConditionalMapper.INSTANCE;
|
||||||
|
|
||||||
|
OptionalLikeConditionalMapper.Target target = mapper.map( new OptionalLikeConditionalMapper.Source(
|
||||||
|
OptionalLikeConditionalMapper.Nullable.ofNullable( "test" ) ) );
|
||||||
|
|
||||||
|
assertThat( target.getValue() ).isEqualTo( "test" );
|
||||||
|
|
||||||
|
target = mapper.map(
|
||||||
|
new OptionalLikeConditionalMapper.Source( OptionalLikeConditionalMapper.Nullable.undefined() )
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat( target.getValue() ).isEqualTo( "initial" );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ConditionalMethodAndBeanPresenceCheckMapper {
|
||||||
|
|
||||||
|
ConditionalMethodAndBeanPresenceCheckMapper INSTANCE = Mappers.getMapper(
|
||||||
|
ConditionalMethodAndBeanPresenceCheckMapper.class );
|
||||||
|
|
||||||
|
BasicEmployee map(EmployeeDto employee);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmployeeDto {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public EmployeeDto(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasName() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ConditionalMethodForCollectionMapper {
|
||||||
|
|
||||||
|
ConditionalMethodForCollectionMapper INSTANCE = Mappers.getMapper( ConditionalMethodForCollectionMapper.class );
|
||||||
|
|
||||||
|
AuthorDto map(Author author);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default <T> boolean isNotEmpty(Collection<T> collection) {
|
||||||
|
return collection != null && !collection.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Author {
|
||||||
|
private List<Book> books;
|
||||||
|
|
||||||
|
public List<Book> getBooks() {
|
||||||
|
return books;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasBooks() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBooks(List<Book> books) {
|
||||||
|
this.books = books;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Book {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public Book(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthorDto {
|
||||||
|
private List<BookDto> books;
|
||||||
|
|
||||||
|
public List<BookDto> getBooks() {
|
||||||
|
return books;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBooks(List<BookDto> books) {
|
||||||
|
this.books = books;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BookDto {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ConditionalMethodInContextMapper {
|
||||||
|
|
||||||
|
ConditionalMethodInContextMapper INSTANCE = Mappers.getMapper( ConditionalMethodInContextMapper.class );
|
||||||
|
|
||||||
|
BasicEmployee map(BasicEmployeeDto employee, @Context PresenceUtils utils);
|
||||||
|
|
||||||
|
class PresenceUtils {
|
||||||
|
@Condition
|
||||||
|
public boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ConditionalMethodInMapper {
|
||||||
|
|
||||||
|
ConditionalMethodInMapper INSTANCE = Mappers.getMapper( ConditionalMethodInMapper.class );
|
||||||
|
|
||||||
|
BasicEmployee map(BasicEmployeeDto employee);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper(uses = ConditionalMethodInUsesMapper.PresenceUtils.class)
|
||||||
|
public interface ConditionalMethodInUsesMapper {
|
||||||
|
|
||||||
|
ConditionalMethodInUsesMapper INSTANCE = Mappers.getMapper( ConditionalMethodInUsesMapper.class );
|
||||||
|
|
||||||
|
BasicEmployee map(BasicEmployeeDto employee);
|
||||||
|
|
||||||
|
class PresenceUtils {
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
public boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper(uses = ConditionalMethodInUsesStaticMapper.PresenceUtils.class)
|
||||||
|
public interface ConditionalMethodInUsesStaticMapper {
|
||||||
|
|
||||||
|
ConditionalMethodInUsesStaticMapper INSTANCE = Mappers.getMapper( ConditionalMethodInUsesStaticMapper.class );
|
||||||
|
|
||||||
|
BasicEmployee map(BasicEmployeeDto employee);
|
||||||
|
|
||||||
|
interface PresenceUtils {
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
static boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ConditionalMethodWithSourceParameterAndValueMapper {
|
||||||
|
|
||||||
|
ConditionalMethodWithSourceParameterAndValueMapper INSTANCE = Mappers.getMapper(
|
||||||
|
ConditionalMethodWithSourceParameterAndValueMapper.class );
|
||||||
|
|
||||||
|
BasicEmployee map(BasicEmployeeDto employee);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isPresent(BasicEmployeeDto source, String value) {
|
||||||
|
if ( value == null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch ( source.getStrategy() ) {
|
||||||
|
case "blank":
|
||||||
|
return !value.trim().isEmpty();
|
||||||
|
case "empty":
|
||||||
|
return !value.isEmpty();
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ConditionalMethodWithSourceParameterMapper {
|
||||||
|
|
||||||
|
ConditionalMethodWithSourceParameterMapper INSTANCE =
|
||||||
|
Mappers.getMapper( ConditionalMethodWithSourceParameterMapper.class );
|
||||||
|
|
||||||
|
BasicEmployee map(BasicEmployeeDto employee);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean shouldMap(BasicEmployeeDto source) {
|
||||||
|
return "map".equals( source.getStrategy() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousAmbiguousConditionalMethodMapper {
|
||||||
|
|
||||||
|
BasicEmployee map(BasicEmployeeDto employee);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isNotEmpty(String value) {
|
||||||
|
return value != null && value.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface OptionalLikeConditionalMapper {
|
||||||
|
|
||||||
|
OptionalLikeConditionalMapper INSTANCE = Mappers.getMapper( OptionalLikeConditionalMapper.class );
|
||||||
|
|
||||||
|
Target map(Source source);
|
||||||
|
|
||||||
|
default <T> T map(Nullable<T> nullable) {
|
||||||
|
return nullable.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default <T> boolean isPresent(Nullable<T> nullable) {
|
||||||
|
return nullable.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nullable<T> {
|
||||||
|
|
||||||
|
private final T value;
|
||||||
|
private final boolean present;
|
||||||
|
|
||||||
|
private Nullable(T value, boolean present) {
|
||||||
|
this.value = value;
|
||||||
|
this.present = present;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPresent() {
|
||||||
|
return present;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Nullable<T> undefined() {
|
||||||
|
return new Nullable<>( null, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Nullable<T> ofNullable(T value) {
|
||||||
|
return new Nullable<>( value, true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Source {
|
||||||
|
protected final Nullable<String> value;
|
||||||
|
|
||||||
|
public Source(Nullable<String> value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Nullable<String> getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
protected String value = "initial";
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.expression;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.test.conditional.Employee;
|
||||||
|
import org.mapstruct.ap.test.conditional.EmployeeDto;
|
||||||
|
import org.mapstruct.ap.test.conditional.basic.BasicEmployee;
|
||||||
|
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 static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("2051")
|
||||||
|
@WithClasses({
|
||||||
|
Employee.class,
|
||||||
|
EmployeeDto.class
|
||||||
|
})
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
public class ConditionalExpressionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodsInUtilClassMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalExpressionInStaticClassMethod() {
|
||||||
|
ConditionalMethodsInUtilClassMapper mapper = ConditionalMethodsInUtilClassMapper.INSTANCE;
|
||||||
|
|
||||||
|
EmployeeDto dto = new EmployeeDto();
|
||||||
|
dto.setName( "Tester" );
|
||||||
|
dto.setUniqueIdNumber( "SSID-001" );
|
||||||
|
dto.setCountry( null );
|
||||||
|
|
||||||
|
Employee employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
|
||||||
|
dto.setCountry( "UK" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isEqualTo( "SSID-001" );
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
|
||||||
|
dto.setCountry( "US" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isEqualTo( "SSID-001" );
|
||||||
|
|
||||||
|
dto.setCountry( "CH" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
SimpleConditionalExpressionMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalSimpleExpression() {
|
||||||
|
SimpleConditionalExpressionMapper mapper = SimpleConditionalExpressionMapper.INSTANCE;
|
||||||
|
|
||||||
|
SimpleConditionalExpressionMapper.Target target =
|
||||||
|
mapper.map( new SimpleConditionalExpressionMapper.Source( 50 ) );
|
||||||
|
assertThat( target.getValue() ).isEqualTo( 50 );
|
||||||
|
|
||||||
|
target = mapper.map( new SimpleConditionalExpressionMapper.Source( 101 ) );
|
||||||
|
assertThat( target.getValue() ).isEqualTo( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(type = ErroneousConditionExpressionMapper.class,
|
||||||
|
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||||
|
line = 19,
|
||||||
|
message = "Value for condition expression must be given in the form \"java(<EXPRESSION>)\"."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@WithClasses({
|
||||||
|
BasicEmployee.class,
|
||||||
|
ErroneousConditionExpressionMapper.class
|
||||||
|
} )
|
||||||
|
public void invalidJavaConditionExpression() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.expression;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.ap.test.conditional.Employee;
|
||||||
|
import org.mapstruct.ap.test.conditional.EmployeeDto;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper(imports = ConditionalMethodsInUtilClassMapper.StaticUtil.class)
|
||||||
|
public interface ConditionalMethodsInUtilClassMapper {
|
||||||
|
|
||||||
|
ConditionalMethodsInUtilClassMapper INSTANCE = Mappers.getMapper( ConditionalMethodsInUtilClassMapper.class );
|
||||||
|
|
||||||
|
@Mapping(target = "ssid", source = "uniqueIdNumber",
|
||||||
|
conditionExpression = "java(StaticUtil.isAmericanCitizen( employee ))")
|
||||||
|
@Mapping(target = "nin", source = "uniqueIdNumber",
|
||||||
|
conditionExpression = "java(StaticUtil.isBritishCitizen( employee ))")
|
||||||
|
Employee map(EmployeeDto employee);
|
||||||
|
|
||||||
|
interface StaticUtil {
|
||||||
|
|
||||||
|
static boolean isAmericanCitizen(EmployeeDto employeeDto) {
|
||||||
|
return "US".equals( employeeDto.getCountry() );
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isBritishCitizen(EmployeeDto employeeDto) {
|
||||||
|
return "UK".equals( employeeDto.getCountry() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.expression;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.ap.test.conditional.EmployeeDto;
|
||||||
|
import org.mapstruct.ap.test.conditional.basic.BasicEmployee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousConditionExpressionMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "name", conditionExpression = "!employee.getName().isEmpty()")
|
||||||
|
BasicEmployee map(EmployeeDto employee);
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.expression;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface SimpleConditionalExpressionMapper {
|
||||||
|
|
||||||
|
SimpleConditionalExpressionMapper INSTANCE = Mappers.getMapper( SimpleConditionalExpressionMapper.class );
|
||||||
|
|
||||||
|
@Mapping(target = "value", conditionExpression = "java(source.getValue() < 100)")
|
||||||
|
Target map(Source source);
|
||||||
|
|
||||||
|
class Source {
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
public Source(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.qualifier;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
|
import org.mapstruct.Qualifier;
|
||||||
|
import org.mapstruct.ap.test.conditional.Employee;
|
||||||
|
import org.mapstruct.ap.test.conditional.EmployeeDto;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper(uses = ConditionalMethodWithClassQualifiersMapper.StaticUtil.class)
|
||||||
|
public interface ConditionalMethodWithClassQualifiersMapper {
|
||||||
|
|
||||||
|
ConditionalMethodWithClassQualifiersMapper INSTANCE =
|
||||||
|
Mappers.getMapper( ConditionalMethodWithClassQualifiersMapper.class );
|
||||||
|
|
||||||
|
@Mapping(target = "ssid", source = "uniqueIdNumber",
|
||||||
|
conditionQualifiedBy = UtilConditions.class, conditionQualifiedByName = "american")
|
||||||
|
@Mapping(target = "nin", source = "uniqueIdNumber",
|
||||||
|
conditionQualifiedBy = UtilConditions.class, conditionQualifiedByName = "british")
|
||||||
|
Employee map(EmployeeDto employee);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UtilConditions
|
||||||
|
interface StaticUtil {
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
@Named("american")
|
||||||
|
static boolean isAmericanCitizen(EmployeeDto employerDto) {
|
||||||
|
return "US".equals( employerDto.getCountry() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
@Named("british")
|
||||||
|
static boolean isBritishCitizen(EmployeeDto employeeDto) {
|
||||||
|
return "UK".equals( employeeDto.getCountry() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@interface UtilConditions {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.qualifier;
|
||||||
|
|
||||||
|
import org.mapstruct.Condition;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
|
import org.mapstruct.ap.test.conditional.Employee;
|
||||||
|
import org.mapstruct.ap.test.conditional.EmployeeDto;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper(uses = ConditionalMethodWithSourceParameterMapper.StaticUtil.class)
|
||||||
|
public interface ConditionalMethodWithSourceParameterMapper {
|
||||||
|
|
||||||
|
ConditionalMethodWithSourceParameterMapper INSTANCE =
|
||||||
|
Mappers.getMapper( ConditionalMethodWithSourceParameterMapper.class );
|
||||||
|
|
||||||
|
@Mapping(target = "ssid", source = "uniqueIdNumber", conditionQualifiedByName = "isAmericanCitizen")
|
||||||
|
@Mapping(target = "nin", source = "uniqueIdNumber", conditionQualifiedByName = "isBritishCitizen")
|
||||||
|
Employee map(EmployeeDto employee);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
default boolean isNotBlank(String value) {
|
||||||
|
return value != null && !value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
@Named("isAmericanCitizen")
|
||||||
|
default boolean isAmericanCitizen(EmployeeDto employerDto) {
|
||||||
|
return "US".equals( employerDto.getCountry() );
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StaticUtil {
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
@Named("isBritishCitizen")
|
||||||
|
static boolean isBritishCitizen(EmployeeDto employeeDto) {
|
||||||
|
return "UK".equals( employeeDto.getCountry() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.qualifier;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.test.conditional.Employee;
|
||||||
|
import org.mapstruct.ap.test.conditional.EmployeeDto;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("2051")
|
||||||
|
@WithClasses({
|
||||||
|
Employee.class,
|
||||||
|
EmployeeDto.class
|
||||||
|
})
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
public class ConditionalQualifierTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodWithSourceParameterMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalMethodWithSourceParameter() {
|
||||||
|
ConditionalMethodWithSourceParameterMapper mapper = ConditionalMethodWithSourceParameterMapper.INSTANCE;
|
||||||
|
|
||||||
|
EmployeeDto dto = new EmployeeDto();
|
||||||
|
dto.setName( "Tester" );
|
||||||
|
dto.setUniqueIdNumber( "SSID-001" );
|
||||||
|
dto.setCountry( null );
|
||||||
|
|
||||||
|
Employee employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
|
||||||
|
dto.setCountry( "UK" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isEqualTo( "SSID-001" );
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
|
||||||
|
dto.setCountry( "US" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isEqualTo( "SSID-001" );
|
||||||
|
|
||||||
|
dto.setCountry( "CH" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ConditionalMethodWithClassQualifiersMapper.class
|
||||||
|
})
|
||||||
|
public void conditionalClassQualifiers() {
|
||||||
|
ConditionalMethodWithClassQualifiersMapper mapper = ConditionalMethodWithClassQualifiersMapper.INSTANCE;
|
||||||
|
|
||||||
|
EmployeeDto dto = new EmployeeDto();
|
||||||
|
dto.setName( "Tester" );
|
||||||
|
dto.setUniqueIdNumber( "SSID-001" );
|
||||||
|
dto.setCountry( null );
|
||||||
|
|
||||||
|
Employee employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
|
||||||
|
dto.setCountry( "UK" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isEqualTo( "SSID-001" );
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
|
||||||
|
dto.setCountry( "US" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isEqualTo( "SSID-001" );
|
||||||
|
|
||||||
|
dto.setCountry( "CH" );
|
||||||
|
employee = mapper.map( dto );
|
||||||
|
assertThat( employee.getNin() ).isNull();
|
||||||
|
assertThat( employee.getSsid() ).isNull();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import javax.annotation.processing.Generated;
|
||||||
|
|
||||||
|
@Generated(
|
||||||
|
value = "org.mapstruct.ap.MappingProcessor",
|
||||||
|
date = "2021-04-19T21:10:40+0200",
|
||||||
|
comments = "version: , compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)"
|
||||||
|
)
|
||||||
|
public class ConditionalMethodInMapperImpl implements ConditionalMethodInMapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicEmployee map(BasicEmployeeDto employee) {
|
||||||
|
if ( employee == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicEmployee basicEmployee = new BasicEmployee();
|
||||||
|
|
||||||
|
if ( isNotBlank( employee.getName() ) ) {
|
||||||
|
basicEmployee.setName( employee.getName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return basicEmployee;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.conditional.basic;
|
||||||
|
|
||||||
|
import javax.annotation.processing.Generated;
|
||||||
|
|
||||||
|
@Generated(
|
||||||
|
value = "org.mapstruct.ap.MappingProcessor",
|
||||||
|
date = "2021-04-19T21:10:38+0200",
|
||||||
|
comments = "version: , compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)"
|
||||||
|
)
|
||||||
|
public class ConditionalMethodWithSourceParameterAndValueMapperImpl implements ConditionalMethodWithSourceParameterAndValueMapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicEmployee map(BasicEmployeeDto employee) {
|
||||||
|
if ( employee == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicEmployee basicEmployee = new BasicEmployee();
|
||||||
|
|
||||||
|
if ( isPresent( employee, employee.getName() ) ) {
|
||||||
|
basicEmployee.setName( employee.getName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return basicEmployee;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user