mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#695 user control over mapping means (direct, method, conversion, 2step)
This commit is contained in:
parent
2d3761051a
commit
58da2d293f
@ -11,6 +11,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
|
||||
/**
|
||||
@ -156,4 +158,18 @@ public @interface BeanMapping {
|
||||
* @since 1.3
|
||||
*/
|
||||
Builder builder() default @Builder;
|
||||
|
||||
/**
|
||||
* Allows detailed control over the mapping process.
|
||||
*
|
||||
* @return the mapping control
|
||||
*
|
||||
* @since 1.4
|
||||
*
|
||||
* @see org.mapstruct.control.DeepClone
|
||||
* @see org.mapstruct.control.NoComplexMapping
|
||||
* @see org.mapstruct.control.MappingControl
|
||||
*/
|
||||
Class<? extends Annotation> mappingControl() default MappingControl.class;
|
||||
|
||||
}
|
||||
|
@ -10,10 +10,12 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
|
||||
/**
|
||||
* Configures the mapping between two iterable like types, e.g. {@code List<String>} and {@code List<Date>}.
|
||||
*
|
||||
@ -121,4 +123,18 @@ public @interface IterableMapping {
|
||||
* @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapping.
|
||||
*/
|
||||
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
|
||||
|
||||
/**
|
||||
* Allows detailed control over the mapping process.
|
||||
*
|
||||
* @return the mapping control
|
||||
*
|
||||
* @since 1.4
|
||||
*
|
||||
* @see org.mapstruct.control.DeepClone
|
||||
* @see org.mapstruct.control.NoComplexMapping
|
||||
* @see org.mapstruct.control.MappingControl
|
||||
*/
|
||||
Class<? extends Annotation> elementMappingControl() default MappingControl.class;
|
||||
|
||||
}
|
||||
|
@ -10,10 +10,12 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
|
||||
/**
|
||||
* Configures the mapping between two map types, e.g. Map<String, String> and Map<Long, Date>.
|
||||
*
|
||||
@ -167,4 +169,32 @@ public @interface MapMapping {
|
||||
* @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapping.
|
||||
*/
|
||||
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
|
||||
|
||||
/**
|
||||
* Allows detailed control over the key mapping process.
|
||||
*
|
||||
* @return the mapping control
|
||||
*
|
||||
* @since 1.4
|
||||
|
||||
* @see org.mapstruct.control.DeepClone
|
||||
* @see org.mapstruct.control.NoComplexMapping
|
||||
* @see org.mapstruct.control.MappingControl
|
||||
*/
|
||||
Class<? extends Annotation> keyMappingControl() default MappingControl.class;
|
||||
|
||||
|
||||
/**
|
||||
* Allows detailed control over the value mapping process.
|
||||
*
|
||||
* @return the mapping control
|
||||
*
|
||||
* @since 1.4
|
||||
*
|
||||
* @see org.mapstruct.control.DeepClone
|
||||
* @see org.mapstruct.control.NoComplexMapping
|
||||
* @see org.mapstruct.control.MappingControl
|
||||
*/
|
||||
Class<? extends Annotation> valueMappingControl() default MappingControl.class;
|
||||
|
||||
}
|
||||
|
@ -5,11 +5,13 @@
|
||||
*/
|
||||
package org.mapstruct;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
@ -281,4 +283,17 @@ public @interface Mapper {
|
||||
* @since 1.3
|
||||
*/
|
||||
Builder builder() default @Builder;
|
||||
|
||||
/**
|
||||
* Allows detailed control over the mapping process.
|
||||
*
|
||||
* @return the mapping control
|
||||
*
|
||||
* @since 1.4
|
||||
*
|
||||
* @see org.mapstruct.control.DeepClone
|
||||
* @see org.mapstruct.control.NoComplexMapping
|
||||
* @see org.mapstruct.control.MappingControl
|
||||
*/
|
||||
Class<? extends Annotation> mappingControl() default MappingControl.class;
|
||||
}
|
||||
|
@ -5,11 +5,13 @@
|
||||
*/
|
||||
package org.mapstruct;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
@ -255,4 +257,19 @@ public @interface MapperConfig {
|
||||
* @since 1.3
|
||||
*/
|
||||
Builder builder() default @Builder;
|
||||
|
||||
/**
|
||||
* Allows detailed control over the mapping process.
|
||||
*
|
||||
* @return the mapping control
|
||||
*
|
||||
* @since 1.4
|
||||
*
|
||||
* @see org.mapstruct.control.DeepClone
|
||||
* @see org.mapstruct.control.NoComplexMapping
|
||||
* @see org.mapstruct.control.MappingControl
|
||||
*/
|
||||
Class<? extends Annotation> mappingControl() default MappingControl.class;
|
||||
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@ import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
|
||||
/**
|
||||
@ -394,4 +396,19 @@ public @interface Mapping {
|
||||
NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
|
||||
default NullValuePropertyMappingStrategy.SET_TO_NULL;
|
||||
|
||||
/**
|
||||
* Allows detailed control over the mapping process.
|
||||
*
|
||||
* @return the mapping control
|
||||
*
|
||||
* @since 1.4
|
||||
*
|
||||
* @see org.mapstruct.control.DeepClone
|
||||
* @see org.mapstruct.control.NoComplexMapping
|
||||
* @see org.mapstruct.control.MappingControl
|
||||
*/
|
||||
Class<? extends Annotation> mappingControl() default MappingControl.class;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
24
core/src/main/java/org/mapstruct/control/DeepClone.java
Normal file
24
core/src/main/java/org/mapstruct/control/DeepClone.java
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.control;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.mapstruct.util.Experimental;
|
||||
|
||||
/**
|
||||
* Clones a source type to a target type (assuming source and target are of the same type).
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Experimental
|
||||
@MappingControl( MappingControl.Use.MAPPING_METHOD )
|
||||
public @interface DeepClone {
|
||||
}
|
150
core/src/main/java/org/mapstruct/control/MappingControl.java
Normal file
150
core/src/main/java/org/mapstruct/control/MappingControl.java
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.control;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* Controls which means of mapping are considered between the source and the target in mappings.
|
||||
*
|
||||
* <p>
|
||||
* There are several applications of <code>MappingControl</code> conceivable. One application, "deep cloning" is
|
||||
* explained below in the example.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Another application is controlling so called "complex mappings", which are not always desirable and sometimes lead to
|
||||
* unexpected behaviour and prolonged compilation time.
|
||||
* </p>
|
||||
*
|
||||
* <p><strong>Example:</strong>Cloning of an object</p>
|
||||
* <p>
|
||||
* When all methods are allowed, MapStruct would make a shallow copy. It would take the <code>ShelveDTO</code> in
|
||||
* the <code>FridgeDTO</code> and directly enter that as target on the target <code>FridgeDTO</code>. By disabling all
|
||||
* other kinds of mappings apart from {@link MappingControl.Use#MAPPING_METHOD}, see {@link DeepClone} MapStruct is
|
||||
* forced to generate mapping methods all through the object graph `FridgeDTO` and hence create a deep clone.
|
||||
* </p>
|
||||
* <pre><code class='java'>
|
||||
* public class FridgeDTO {
|
||||
*
|
||||
* private ShelveDTO shelve;
|
||||
*
|
||||
* public ShelveDTO getShelve() {
|
||||
* return shelve;
|
||||
* }
|
||||
*
|
||||
* public void setShelve(ShelveDTO shelve) {
|
||||
* this.shelve = shelve;
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
* <pre><code class='java'>
|
||||
* public class ShelveDTO {
|
||||
*
|
||||
* private CoolBeerDTO coolBeer;
|
||||
*
|
||||
* public CoolBeerDTO getCoolBeer() {
|
||||
* return coolBeer;
|
||||
* }
|
||||
*
|
||||
* public void setCoolBeer(CoolBeerDTO coolBeer) {
|
||||
* this.coolBeer = coolBeer;
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
* <pre><code class='java'>
|
||||
* public class CoolBeerDTO {
|
||||
*
|
||||
* private String beerCount;
|
||||
*
|
||||
* public String getBeerCount() {
|
||||
* return beerCount;
|
||||
* }
|
||||
*
|
||||
* public void setBeerCount(String beerCount) {
|
||||
* this.beerCount = beerCount;
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* <pre><code class='java'>
|
||||
* @Mapper(mappingControl = DeepClone.class)
|
||||
* public interface CloningMapper {
|
||||
*
|
||||
* CloningMapper INSTANCE = Mappers.getMapper( CloningMapper.class );
|
||||
*
|
||||
* FridgeDTO clone(FridgeDTO in);
|
||||
*
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Repeatable(MappingControls.class)
|
||||
@Target( ElementType.ANNOTATION_TYPE )
|
||||
@MappingControl( MappingControl.Use.DIRECT )
|
||||
@MappingControl( MappingControl.Use.BUILT_IN_CONVERSION )
|
||||
@MappingControl( MappingControl.Use.MAPPING_METHOD )
|
||||
@MappingControl( MappingControl.Use.COMPLEX_MAPPING )
|
||||
public @interface MappingControl {
|
||||
|
||||
Use value();
|
||||
|
||||
enum Use {
|
||||
|
||||
/**
|
||||
* Controls the mapping, allows for type conversion from source type to target type
|
||||
* <p>
|
||||
* Type conversions are typically supported directly in Java. The "toString()" is such an example,
|
||||
* which allows for mapping for instance a {@link java.lang.Number} type to a {@link java.lang.String}.
|
||||
* <p>
|
||||
* Please refer to the MapStruct guide for more info.
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
BUILT_IN_CONVERSION,
|
||||
|
||||
/**
|
||||
* Controls the mapping from source to target type, allows mapping by calling:
|
||||
* <ol>
|
||||
* <li>A type conversion, passed into a mapping method</li>
|
||||
* <li>A mapping method, passed into a type conversion</li>
|
||||
* <li>A mapping method passed into another mapping method</li>
|
||||
* </ol>
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
COMPLEX_MAPPING,
|
||||
/**
|
||||
* Controls the mapping, allows for a direct mapping from source type to target type.
|
||||
* <p>
|
||||
* This means if source type and target type are of the same type, MapStruct will not perform
|
||||
* any mappings anymore and assign the target to the source direct.
|
||||
* <p>
|
||||
* An exception are types from the package {@link java}, which will be mapped always directly.
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
DIRECT,
|
||||
|
||||
/**
|
||||
* Controls the mapping, allows for Direct Mapping from source type to target type.
|
||||
* <p>
|
||||
* The mapping method can be either a custom referred mapping method, or a MapStruct built in
|
||||
* mapping method.
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
MAPPING_METHOD
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.control;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Allows multiple {@link MappingControl} on a class declaration.
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface MappingControls {
|
||||
|
||||
MappingControl[] value();
|
||||
}
|
@ -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.control;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.mapstruct.util.Experimental;
|
||||
|
||||
/**
|
||||
* Disables complex mappings, mappings that require 2 mapping means (method, built-in conversion) to constitute
|
||||
* a mapping from source to target.
|
||||
*
|
||||
* @see MappingControl.Use#COMPLEX_MAPPING
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Experimental
|
||||
@MappingControl( MappingControl.Use.DIRECT )
|
||||
@MappingControl( MappingControl.Use.BUILT_IN_CONVERSION )
|
||||
@MappingControl( MappingControl.Use.MAPPING_METHOD )
|
||||
public @interface NoComplexMapping {
|
||||
}
|
@ -145,15 +145,27 @@ That way it is possible to map arbitrary deep object graphs. When mapping from e
|
||||
|
||||
When generating the implementation of a mapping method, MapStruct will apply the following routine for each attribute pair in the source and target object:
|
||||
|
||||
* If source and target attribute have the same type, the value will be simply copied from source to target. If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target attribute.
|
||||
* If source and target attribute type differ, check whether there is another mapping method which has the type of the source attribute as parameter type and the type of the target attribute as return type. If such a method exists it will be invoked in the generated mapping implementation.
|
||||
* If no such method exists MapStruct will look whether a built-in conversion for the source and target type of the attribute exists. If this is the case, the generated mapping code will apply this conversion.
|
||||
* If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do the mapping between the source and target attributes.
|
||||
* If MapStruct could not create a name based mapping method an error will be raised at build time, indicating the non-mappable attribute and its path.
|
||||
. If source and target attribute have the same type, the value will be simply copied *direct* from source to target. If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target attribute.
|
||||
. If source and target attribute type differ, check whether there is another *mapping method* which has the type of the source attribute as parameter type and the type of the target attribute as return type. If such a method exists it will be invoked in the generated mapping implementation.
|
||||
. If no such method exists MapStruct will look whether a *built-in conversion* for the source and target type of the attribute exists. If this is the case, the generated mapping code will apply this conversion.
|
||||
. If no such method exists MapStruct will apply *complex* conversions:
|
||||
.. mapping method, the result mapped by mapping method, like this: `target = method1( method2( source ) )`
|
||||
.. built-in conversion, the result mapped by mapping method, like this: `target = method( conversion( source ) )`
|
||||
.. mapping method, the result mapped by build-in conversion, like this: `target = conversion( method( source ) )`
|
||||
. If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do the mapping between the source and target attributes.
|
||||
. If MapStruct could not create a name based mapping method an error will be raised at build time, indicating the non-mappable attribute and its path.
|
||||
|
||||
A mapping control (`MappingControl`) can be defined on all levels (`@MappingConfig`, `@Mapper`, `@BeanMapping`, `@Mapping`), the latter taking precedence over the former. For example: `@Mapper( mappingControl = NoComplexMapping.class )` takes precedence over `@MapperConfig( mappingControl = DeepClone.class )`. `@IterableMapping` and `@MapMapping` work similar as `@Mapping`. MappingControl is experimental from MapStruct 1.4.
|
||||
`MappingControl` has an enum that corresponds to the first 4 options above: `MappingControl.Use#DIRECT`, `MappingControl.Use#MAPPING_METHOD`, `MappingControl.Use#BUILT_IN_CONVERSION` and `MappingControl.Use#COMPLEX_MAPPING` the presence of which allows the user to switch *on* a option. The absence of an enum switches *off* a mapping option. Default they are all present enabling all mapping options.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
In order to stop MapStruct from generating automatic sub-mapping methods, one can use `@Mapper( disableSubMappingMethodsGeneration = true )`.
|
||||
In order to stop MapStruct from generating automatic sub-mapping methods as in 5. above, one can use `@Mapper( disableSubMappingMethodsGeneration = true )`.
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The user has full control over the mapping by means of meta annotations. Some handy ones have been defined such as `@DeepClone` which only allows direct mappings. The result: if source and target type are the same, MapStruct will make a deep clone of the source. Sub-mappings-methods have to be allowed (default option).
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
|
@ -29,6 +29,8 @@ import org.mapstruct.Qualifier;
|
||||
import org.mapstruct.TargetType;
|
||||
import org.mapstruct.ValueMapping;
|
||||
import org.mapstruct.ValueMappings;
|
||||
import org.mapstruct.control.MappingControl;
|
||||
import org.mapstruct.control.MappingControls;
|
||||
import org.mapstruct.tools.gem.GemDefinition;
|
||||
|
||||
/**
|
||||
@ -58,6 +60,9 @@ import org.mapstruct.tools.gem.GemDefinition;
|
||||
@GemDefinition(Context.class)
|
||||
@GemDefinition(Builder.class)
|
||||
|
||||
@GemDefinition(MappingControl.class)
|
||||
@GemDefinition(MappingControls.class)
|
||||
|
||||
// external types
|
||||
@GemDefinition(XmlElementDecl.class)
|
||||
@GemDefinition(XmlElementRef.class)
|
||||
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.gem;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak
|
||||
*/
|
||||
public enum MappingControlUseGem {
|
||||
|
||||
BUILT_IN_CONVERSION,
|
||||
COMPLEX_MAPPING,
|
||||
DIRECT,
|
||||
MAPPING_METHOD
|
||||
}
|
@ -577,6 +577,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
.targetPropertyName( targetPropertyName )
|
||||
.formattingParameters( mapping.getFormattingParameters() )
|
||||
.selectionParameters( mapping.getSelectionParameters() )
|
||||
.options( mapping )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.mirror( mapping.getMirror() )
|
||||
|
@ -77,6 +77,7 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
||||
);
|
||||
|
||||
SelectionCriteria criteria = SelectionCriteria.forMappingMethods( selectionParameters,
|
||||
method.getOptions().getIterableMapping().getMappingControl( ctx.getElementUtils() ),
|
||||
callingContextTargetPropertyName,
|
||||
false
|
||||
);
|
||||
|
@ -87,8 +87,12 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
SourceRHS keySourceRHS = new SourceRHS( "entry.getKey()", keySourceType, new HashSet<>(), "map key" );
|
||||
|
||||
SelectionCriteria keyCriteria =
|
||||
SelectionCriteria.forMappingMethods( keySelectionParameters, null, false );
|
||||
SelectionCriteria keyCriteria = SelectionCriteria.forMappingMethods(
|
||||
keySelectionParameters,
|
||||
method.getOptions().getMapMapping().getKeyMappingControl( ctx.getElementUtils() ),
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment(
|
||||
method,
|
||||
@ -130,8 +134,11 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
||||
SourceRHS valueSourceRHS = new SourceRHS( "entry.getValue()", valueSourceType, new HashSet<>(),
|
||||
"map value" );
|
||||
|
||||
SelectionCriteria valueCriteria =
|
||||
SelectionCriteria.forMappingMethods( valueSelectionParameters, null, false );
|
||||
SelectionCriteria valueCriteria = SelectionCriteria.forMappingMethods(
|
||||
valueSelectionParameters,
|
||||
method.getOptions().getMapMapping().getValueMappingControl( ctx.getElementUtils() ),
|
||||
null,
|
||||
false );
|
||||
|
||||
Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment(
|
||||
method,
|
||||
|
@ -31,6 +31,8 @@ import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.SourceRHS;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.DelegatingOptions;
|
||||
import org.mapstruct.ap.internal.model.source.MappingControl;
|
||||
import org.mapstruct.ap.internal.model.source.MappingOptions;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||
@ -154,6 +156,7 @@ public class PropertyMapping extends ModelElement {
|
||||
private SourceRHS rightHandSide;
|
||||
private FormattingParameters formattingParameters;
|
||||
private SelectionParameters selectionParameters;
|
||||
private MappingControl mappingControl;
|
||||
private MappingReferences forgeMethodWithMappingReferences;
|
||||
private boolean forceUpdateMethod;
|
||||
private boolean forgedNamedBased = true;
|
||||
@ -216,6 +219,7 @@ public class PropertyMapping extends ModelElement {
|
||||
}
|
||||
|
||||
public PropertyMappingBuilder options(DelegatingOptions options) {
|
||||
this.mappingControl = options.getMappingControl( ctx.getElementUtils() );
|
||||
this.nvcs = options.getNullValueCheckStrategy();
|
||||
if ( method.isUpdateMethod() ) {
|
||||
this.nvpms = options.getNullValuePropertyMappingStrategy();
|
||||
@ -242,9 +246,11 @@ public class PropertyMapping extends ModelElement {
|
||||
preferUpdateMethods = method.getMappingTargetParameter() != null;
|
||||
}
|
||||
|
||||
SelectionCriteria criteria = SelectionCriteria.forMappingMethods( selectionParameters,
|
||||
targetPropertyName,
|
||||
preferUpdateMethods
|
||||
SelectionCriteria criteria = SelectionCriteria.forMappingMethods(
|
||||
selectionParameters,
|
||||
mappingControl,
|
||||
targetPropertyName,
|
||||
preferUpdateMethods
|
||||
);
|
||||
|
||||
// forge a method instead of resolving one when there are mapping options.
|
||||
@ -741,6 +747,7 @@ public class PropertyMapping extends ModelElement {
|
||||
|
||||
private String constantExpression;
|
||||
private FormattingParameters formattingParameters;
|
||||
private MappingControl mappingControl;
|
||||
private SelectionParameters selectionParameters;
|
||||
|
||||
ConstantMappingBuilder() {
|
||||
@ -762,6 +769,11 @@ public class PropertyMapping extends ModelElement {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConstantMappingBuilder options(MappingOptions options) {
|
||||
this.mappingControl = options.getMappingControl( ctx.getElementUtils() );
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMapping build() {
|
||||
// source
|
||||
String sourceErrorMessagePart = "constant '" + constantExpression + "'";
|
||||
@ -783,7 +795,8 @@ public class PropertyMapping extends ModelElement {
|
||||
Type sourceType = ctx.getTypeFactory().getTypeForLiteral( baseForLiteral );
|
||||
|
||||
SelectionCriteria criteria = SelectionCriteria.forMappingMethods( selectionParameters,
|
||||
targetPropertyName,
|
||||
mappingControl,
|
||||
targetPropertyName,
|
||||
method.getMappingTargetParameter() != null
|
||||
);
|
||||
|
||||
|
@ -286,6 +286,10 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return (typeMirror.getKind() == TypeKind.TYPEVAR);
|
||||
}
|
||||
|
||||
public boolean isJavaLangType() {
|
||||
return packageName != null && packageName.startsWith( "java." );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this type is a sub-type of {@link java.util.stream.Stream}.
|
||||
*
|
||||
|
@ -11,6 +11,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
@ -141,6 +142,15 @@ public class BeanMappingOptions extends DelegatingOptions {
|
||||
.orElse( next().getBuilder() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingControl getMappingControl(Elements elementUtils) {
|
||||
return Optional.ofNullable( beanMapping ).map( BeanMappingGem::mappingControl )
|
||||
.filter( GemValue::hasValue )
|
||||
.map( GemValue::getValue )
|
||||
.map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) )
|
||||
.orElse( next().getMappingControl( elementUtils ) );
|
||||
}
|
||||
|
||||
// @BeanMapping specific
|
||||
|
||||
public SelectionParameters getSelectionParameters() {
|
||||
|
@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.model.source;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import org.mapstruct.ap.internal.option.Options;
|
||||
import org.mapstruct.ap.internal.gem.BuilderGem;
|
||||
@ -121,6 +122,11 @@ public class DefaultOptions extends DelegatingOptions {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingControl getMappingControl(Elements elementUtils) {
|
||||
return MappingControl.fromTypeMirror( mapper.mappingControl().getDefaultValue(), elementUtils );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnnotation() {
|
||||
return false;
|
||||
|
@ -11,6 +11,7 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import org.mapstruct.ap.internal.gem.BuilderGem;
|
||||
import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem;
|
||||
@ -100,6 +101,10 @@ public abstract class DelegatingOptions {
|
||||
return next.getBuilder();
|
||||
}
|
||||
|
||||
public MappingControl getMappingControl(Elements elementUtils) {
|
||||
return next.getMappingControl( elementUtils );
|
||||
}
|
||||
|
||||
DelegatingOptions next() {
|
||||
return next;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.model.source;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
||||
@ -101,6 +102,14 @@ public class IterableMappingOptions extends DelegatingOptions {
|
||||
.orElse( next().getNullValueMappingStrategy() );
|
||||
}
|
||||
|
||||
public MappingControl getElementMappingControl(Elements elementUtils) {
|
||||
return Optional.ofNullable( iterableMapping ).map( IterableMappingGem::elementMappingControl )
|
||||
.filter( GemValue::hasValue )
|
||||
.map( GemValue::getValue )
|
||||
.map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) )
|
||||
.orElse( next().getMappingControl( elementUtils ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnnotation() {
|
||||
return iterableMapping != null;
|
||||
|
@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.model.source;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
||||
@ -145,6 +146,22 @@ public class MapMappingOptions extends DelegatingOptions {
|
||||
.orElse( next().getNullValueMappingStrategy() );
|
||||
}
|
||||
|
||||
public MappingControl getKeyMappingControl(Elements elementUtils) {
|
||||
return Optional.ofNullable( mapMapping ).map( MapMappingGem::keyMappingControl )
|
||||
.filter( GemValue::hasValue )
|
||||
.map( GemValue::getValue )
|
||||
.map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) )
|
||||
.orElse( next().getMappingControl( elementUtils ) );
|
||||
}
|
||||
|
||||
public MappingControl getValueMappingControl(Elements elementUtils) {
|
||||
return Optional.ofNullable( mapMapping ).map( MapMappingGem::valueMappingControl )
|
||||
.filter( GemValue::hasValue )
|
||||
.map( GemValue::getValue )
|
||||
.map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) )
|
||||
.orElse( next().getMappingControl( elementUtils ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnnotation() {
|
||||
return mapMapping != null;
|
||||
|
@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.model.source;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import org.mapstruct.ap.internal.gem.BuilderGem;
|
||||
import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem;
|
||||
@ -129,6 +130,13 @@ public class MapperConfigOptions extends DelegatingOptions {
|
||||
return mapperConfig.builder().hasValue() ? mapperConfig.builder().get() : next().getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingControl getMappingControl(Elements elementUtils) {
|
||||
return mapperConfig.mappingControl().hasValue() ?
|
||||
MappingControl.fromTypeMirror( mapperConfig.mappingControl().getValue(), elementUtils ) :
|
||||
next().getMappingControl( elementUtils );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnnotation() {
|
||||
return mapperConfig != null;
|
||||
|
@ -11,6 +11,7 @@ import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import org.mapstruct.ap.internal.option.Options;
|
||||
import org.mapstruct.ap.internal.gem.BuilderGem;
|
||||
@ -159,6 +160,13 @@ public class MapperOptions extends DelegatingOptions {
|
||||
return mapper.builder().hasValue() ? mapper.builder().get() : next().getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingControl getMappingControl(Elements elementUtils) {
|
||||
return mapper.mappingControl().hasValue() ?
|
||||
MappingControl.fromTypeMirror( mapper.mappingControl().getValue(), elementUtils ) :
|
||||
next().getMappingControl( elementUtils );
|
||||
}
|
||||
|
||||
// @Mapper specific
|
||||
|
||||
public DeclaredType mapperConfigType() {
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import org.mapstruct.ap.internal.gem.MappingControlGem;
|
||||
import org.mapstruct.ap.internal.gem.MappingControlUseGem;
|
||||
import org.mapstruct.ap.internal.gem.MappingControlsGem;
|
||||
|
||||
public class MappingControl {
|
||||
|
||||
private static final String JAVA_LANG_ANNOTATION_PGK = "java.lang.annotation";
|
||||
private static final String ORG_MAPSTRUCT_PKG = "org.mapstruct";
|
||||
private static final String MAPPING_CONTROL_FQN = "org.mapstruct.control.MappingControl";
|
||||
private static final String MAPPING_CONTROLS_FQN = "org.mapstruct.control.MappingControls";
|
||||
|
||||
private boolean allowDirect = false;
|
||||
private boolean allowTypeConversion = false;
|
||||
private boolean allowMappingMethod = false;
|
||||
private boolean allow2Steps = false;
|
||||
|
||||
public static MappingControl fromTypeMirror(TypeMirror mirror, Elements elementUtils) {
|
||||
MappingControl mappingControl = new MappingControl();
|
||||
if ( TypeKind.DECLARED == mirror.getKind() ) {
|
||||
resolveControls( mappingControl, ( (DeclaredType) mirror ).asElement(), new HashSet<>(), elementUtils );
|
||||
}
|
||||
return mappingControl;
|
||||
}
|
||||
|
||||
private MappingControl() {
|
||||
}
|
||||
|
||||
public boolean allowDirect() {
|
||||
return allowDirect;
|
||||
}
|
||||
|
||||
public boolean allowTypeConversion() {
|
||||
return allowTypeConversion;
|
||||
}
|
||||
|
||||
public boolean allowMappingMethod() {
|
||||
return allowMappingMethod;
|
||||
}
|
||||
|
||||
public boolean allowBy2Steps() {
|
||||
return allow2Steps;
|
||||
}
|
||||
|
||||
private static void resolveControls(MappingControl control, Element element, Set<Element> handledElements,
|
||||
Elements elementUtils) {
|
||||
for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) {
|
||||
Element lElement = annotationMirror.getAnnotationType().asElement();
|
||||
if ( isAnnotation( lElement, MAPPING_CONTROL_FQN ) ) {
|
||||
determineMappingControl( control, MappingControlGem.instanceOn( element ) );
|
||||
}
|
||||
else if ( isAnnotation( lElement, MAPPING_CONTROLS_FQN ) ) {
|
||||
MappingControlsGem.instanceOn( element )
|
||||
.value()
|
||||
.get()
|
||||
.forEach( m -> determineMappingControl( control, m ) );
|
||||
}
|
||||
else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK, elementUtils )
|
||||
&& !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG, elementUtils )
|
||||
&& !handledElements.contains( lElement )
|
||||
) {
|
||||
// recur over annotation mirrors
|
||||
handledElements.add( lElement );
|
||||
resolveControls( control, lElement, handledElements, elementUtils );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void determineMappingControl(MappingControl in, MappingControlGem gem) {
|
||||
MappingControlUseGem use = MappingControlUseGem.valueOf( gem.value().get() );
|
||||
switch ( use ) {
|
||||
case DIRECT:
|
||||
in.allowDirect = true;
|
||||
break;
|
||||
case MAPPING_METHOD:
|
||||
in.allowMappingMethod = true;
|
||||
break;
|
||||
case BUILT_IN_CONVERSION:
|
||||
in.allowTypeConversion = true;
|
||||
break;
|
||||
case COMPLEX_MAPPING:
|
||||
in.allow2Steps = true;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAnnotationInPackage(Element element, String packageFQN, Elements elementUtils) {
|
||||
if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) {
|
||||
return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isAnnotation(Element element, String annotationFQN) {
|
||||
if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) {
|
||||
return annotationFQN.equals( ( (TypeElement) element ).getQualifiedName().toString() );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -17,13 +17,14 @@ import java.util.stream.Collectors;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
||||
import org.mapstruct.ap.internal.gem.MappingGem;
|
||||
import org.mapstruct.ap.internal.gem.MappingsGem;
|
||||
import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem;
|
||||
import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem;
|
||||
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.tools.gem.GemValue;
|
||||
@ -409,6 +410,15 @@ public class MappingOptions extends DelegatingOptions {
|
||||
.orElse( next().getNullValuePropertyMappingStrategy() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingControl getMappingControl(Elements elementUtils) {
|
||||
return Optional.ofNullable( mapping ).map( MappingGem::mappingControl )
|
||||
.filter( GemValue::hasValue )
|
||||
.map( GemValue::getValue )
|
||||
.map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) )
|
||||
.orElse( next().getMappingControl( elementUtils ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* mapping can only be inversed if the source was not a constant nor an expression nor a nested property
|
||||
* and the mapping is not a 'target-source-ignore' mapping
|
||||
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.SourceRHS;
|
||||
import org.mapstruct.ap.internal.model.source.MappingControl;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
|
||||
/**
|
||||
@ -28,9 +29,13 @@ public class SelectionCriteria {
|
||||
private boolean preferUpdateMapping;
|
||||
private final boolean objectFactoryRequired;
|
||||
private final boolean lifecycleCallbackRequired;
|
||||
private final boolean allowDirect;
|
||||
private final boolean allowConversion;
|
||||
private final boolean allowMappingMethod;
|
||||
private final boolean allow2Steps;
|
||||
|
||||
public SelectionCriteria(SelectionParameters selectionParameters, String targetPropertyName,
|
||||
boolean preferUpdateMapping, boolean objectFactoryRequired,
|
||||
public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl,
|
||||
String targetPropertyName, boolean preferUpdateMapping, boolean objectFactoryRequired,
|
||||
boolean lifecycleCallbackRequired) {
|
||||
if ( selectionParameters != null ) {
|
||||
qualifiers.addAll( selectionParameters.getQualifiers() );
|
||||
@ -42,6 +47,18 @@ public class SelectionCriteria {
|
||||
this.qualifyingResultType = null;
|
||||
sourceRHS = null;
|
||||
}
|
||||
if ( mappingControl != null ) {
|
||||
this.allowDirect = mappingControl.allowDirect();
|
||||
this.allowConversion = mappingControl.allowTypeConversion();
|
||||
this.allowMappingMethod = mappingControl.allowMappingMethod();
|
||||
this.allow2Steps = mappingControl.allowBy2Steps();
|
||||
}
|
||||
else {
|
||||
this.allowDirect = true;
|
||||
this.allowConversion = true;
|
||||
this.allowMappingMethod = true;
|
||||
this.allow2Steps = true;
|
||||
}
|
||||
this.targetPropertyName = targetPropertyName;
|
||||
this.preferUpdateMapping = preferUpdateMapping;
|
||||
this.objectFactoryRequired = objectFactoryRequired;
|
||||
@ -94,17 +111,41 @@ public class SelectionCriteria {
|
||||
return !qualifiedByNames.isEmpty() || !qualifiers.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAllowDirect() {
|
||||
return allowDirect;
|
||||
}
|
||||
|
||||
public boolean isAllowConversion() {
|
||||
return allowConversion;
|
||||
}
|
||||
|
||||
public boolean isAllowMappingMethod() {
|
||||
return allowMappingMethod;
|
||||
}
|
||||
|
||||
public boolean isAllow2Steps() {
|
||||
return allow2Steps;
|
||||
}
|
||||
|
||||
public static SelectionCriteria forMappingMethods(SelectionParameters selectionParameters,
|
||||
MappingControl mappingControl,
|
||||
String targetPropertyName, boolean preferUpdateMapping) {
|
||||
|
||||
return new SelectionCriteria( selectionParameters, targetPropertyName, preferUpdateMapping, false, false );
|
||||
return new SelectionCriteria(
|
||||
selectionParameters,
|
||||
mappingControl,
|
||||
targetPropertyName,
|
||||
preferUpdateMapping,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public static SelectionCriteria forFactoryMethods(SelectionParameters selectionParameters) {
|
||||
return new SelectionCriteria( selectionParameters, null, false, true, false );
|
||||
return new SelectionCriteria( selectionParameters, null, null, false, true, false );
|
||||
}
|
||||
|
||||
public static SelectionCriteria forLifecycleMethods(SelectionParameters selectionParameters) {
|
||||
return new SelectionCriteria( selectionParameters, null, false, false, true );
|
||||
return new SelectionCriteria( selectionParameters, null, null, false, false, true );
|
||||
}
|
||||
}
|
||||
|
@ -177,16 +177,20 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
|
||||
private Assignment getTargetAssignment(Type sourceType, Type targetType) {
|
||||
|
||||
Assignment referencedMethod;
|
||||
|
||||
// first simple mapping method
|
||||
Assignment referencedMethod = resolveViaMethod( sourceType, targetType, false );
|
||||
if ( referencedMethod != null ) {
|
||||
referencedMethod.setAssignment( sourceRHS );
|
||||
return referencedMethod;
|
||||
if ( allowMappingMethod() ) {
|
||||
referencedMethod = resolveViaMethod( sourceType, targetType, false );
|
||||
if ( referencedMethod != null ) {
|
||||
referencedMethod.setAssignment( sourceRHS );
|
||||
return referencedMethod;
|
||||
}
|
||||
}
|
||||
|
||||
// then direct assignable
|
||||
if ( !hasQualfiers() ) {
|
||||
if ( sourceType.isAssignableTo( targetType ) ||
|
||||
if ( ( sourceType.isAssignableTo( targetType ) && allowDirect( sourceType, targetType ) ) ||
|
||||
isAssignableThroughCollectionCopyConstructor( sourceType, targetType ) ) {
|
||||
Assignment simpleAssignment = sourceRHS;
|
||||
return simpleAssignment;
|
||||
@ -206,49 +210,53 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
}
|
||||
|
||||
// then type conversion
|
||||
if ( !hasQualfiers() ) {
|
||||
ConversionAssignment conversion = resolveViaConversion( sourceType, targetType );
|
||||
if ( allowConversion() ) {
|
||||
if ( !hasQualfiers() ) {
|
||||
ConversionAssignment conversion = resolveViaConversion( sourceType, targetType );
|
||||
if ( conversion != null ) {
|
||||
conversion.reportMessageWhenNarrowing( messager, this );
|
||||
conversion.getAssignment().setAssignment( sourceRHS );
|
||||
return conversion.getAssignment();
|
||||
}
|
||||
}
|
||||
|
||||
// check for a built-in method
|
||||
if ( !hasQualfiers() ) {
|
||||
Assignment builtInMethod = resolveViaBuiltInMethod( sourceType, targetType );
|
||||
if ( builtInMethod != null ) {
|
||||
builtInMethod.setAssignment( sourceRHS );
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return builtInMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( allow2Steps() ) {
|
||||
// 2 step method, first: method(method(source))
|
||||
referencedMethod = resolveViaMethodAndMethod( sourceType, targetType );
|
||||
if ( referencedMethod != null ) {
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return referencedMethod;
|
||||
}
|
||||
|
||||
// 2 step method, then: method(conversion(source))
|
||||
referencedMethod = resolveViaConversionAndMethod( sourceType, targetType );
|
||||
if ( referencedMethod != null ) {
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return referencedMethod;
|
||||
}
|
||||
|
||||
// stop here when looking for update methods.
|
||||
selectionCriteria.setPreferUpdateMapping( false );
|
||||
|
||||
// 2 step method, finally: conversion(method(source))
|
||||
ConversionAssignment conversion = resolveViaMethodAndConversion( sourceType, targetType );
|
||||
if ( conversion != null ) {
|
||||
conversion.reportMessageWhenNarrowing( messager, this );
|
||||
conversion.getAssignment().setAssignment( sourceRHS );
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return conversion.getAssignment();
|
||||
}
|
||||
}
|
||||
|
||||
// check for a built-in method
|
||||
if (!hasQualfiers() ) {
|
||||
Assignment builtInMethod = resolveViaBuiltInMethod( sourceType, targetType );
|
||||
if ( builtInMethod != null ) {
|
||||
builtInMethod.setAssignment( sourceRHS );
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return builtInMethod;
|
||||
}
|
||||
}
|
||||
|
||||
// 2 step method, first: method(method(source))
|
||||
referencedMethod = resolveViaMethodAndMethod( sourceType, targetType );
|
||||
if ( referencedMethod != null ) {
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return referencedMethod;
|
||||
}
|
||||
|
||||
// 2 step method, then: method(conversion(source))
|
||||
referencedMethod = resolveViaConversionAndMethod( sourceType, targetType );
|
||||
if ( referencedMethod != null ) {
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return referencedMethod;
|
||||
}
|
||||
|
||||
// stop here when looking for update methods.
|
||||
selectionCriteria.setPreferUpdateMapping( false );
|
||||
|
||||
// 2 step method, finally: conversion(method(source))
|
||||
ConversionAssignment conversion = resolveViaMethodAndConversion( sourceType, targetType );
|
||||
if ( conversion != null ) {
|
||||
usedSupportedMappings.addAll( supportingMethodCandidates );
|
||||
return conversion.getAssignment();
|
||||
}
|
||||
|
||||
if ( hasQualfiers() ) {
|
||||
messager.printMessage(
|
||||
mappingMethod.getExecutable(),
|
||||
@ -258,7 +266,8 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
Strings.join( selectionCriteria.getQualifiedByNames(), ", " )
|
||||
);
|
||||
}
|
||||
else {
|
||||
else if ( allowMappingMethod() ) {
|
||||
// only forge if we would allow mapping method
|
||||
return forger.get();
|
||||
}
|
||||
|
||||
@ -270,6 +279,26 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
return selectionCriteria != null && selectionCriteria.hasQualfiers();
|
||||
}
|
||||
|
||||
private boolean allowDirect( Type sourceType, Type targetType ) {
|
||||
if ( sourceType.isPrimitive() || targetType.isPrimitive()
|
||||
|| sourceType.isJavaLangType() || targetType.isJavaLangType() ) {
|
||||
return true;
|
||||
}
|
||||
return selectionCriteria != null && selectionCriteria.isAllowDirect();
|
||||
}
|
||||
|
||||
private boolean allowConversion() {
|
||||
return selectionCriteria != null && selectionCriteria.isAllowConversion();
|
||||
}
|
||||
|
||||
private boolean allowMappingMethod() {
|
||||
return selectionCriteria != null && selectionCriteria.isAllowMappingMethod();
|
||||
}
|
||||
|
||||
private boolean allow2Steps() {
|
||||
return selectionCriteria != null && selectionCriteria.isAllow2Steps();
|
||||
}
|
||||
|
||||
private ConversionAssignment resolveViaConversion(Type sourceType, Type targetType) {
|
||||
|
||||
ConversionProvider conversionProvider = conversions.getConversion( sourceType, targetType );
|
||||
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.control.DeepClone;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(mappingControl = DeepClone.class)
|
||||
public interface CloningMapper {
|
||||
|
||||
CloningMapper INSTANCE = Mappers.getMapper( CloningMapper.class );
|
||||
|
||||
FridgeDTO clone(FridgeDTO in);
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface ComplexMapper {
|
||||
|
||||
ComplexMapper INSTANCE = Mappers.getMapper( ComplexMapper.class );
|
||||
|
||||
@Mapping(target = "beerCount", source = "shelve")
|
||||
Fridge map(FridgeDTO in);
|
||||
|
||||
default String toBeerCount(ShelveDTO in) {
|
||||
return in.getCoolBeer().getBeerCount();
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.MapperConfig;
|
||||
import org.mapstruct.control.NoComplexMapping;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@MapperConfig( mappingControl = NoComplexMapping.class )
|
||||
public interface Config {
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface ConversionMapper {
|
||||
|
||||
ConversionMapper INSTANCE = Mappers.getMapper( ConversionMapper.class );
|
||||
|
||||
Fridge map(CoolBeerDTO in);
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
public class CoolBeerDTO {
|
||||
|
||||
private String beerCount;
|
||||
|
||||
public String getBeerCount() {
|
||||
return beerCount;
|
||||
}
|
||||
|
||||
public void setBeerCount(String beerCount) {
|
||||
this.beerCount = beerCount;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface DirectMapper {
|
||||
|
||||
DirectMapper INSTANCE = Mappers.getMapper( DirectMapper.class );
|
||||
|
||||
@Mapping(target = "shelve", source = "shelve")
|
||||
FridgeDTO map(FridgeDTO in);
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(mappingControl = UseDirect.class)
|
||||
public interface ErroneousComplexMapper {
|
||||
|
||||
ErroneousComplexMapper INSTANCE = Mappers.getMapper( ErroneousComplexMapper.class );
|
||||
|
||||
@Mapping(target = "beerCount", source = "shelve")
|
||||
Fridge map(FridgeDTO in);
|
||||
|
||||
default String toBeerCount(ShelveDTO in) {
|
||||
return in.getCoolBeer().getBeerCount();
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(config = Config.class)
|
||||
public interface ErroneousComplexMapperWithConfig {
|
||||
|
||||
ErroneousComplexMapperWithConfig INSTANCE = Mappers.getMapper( ErroneousComplexMapperWithConfig.class );
|
||||
|
||||
@Mapping(target = "beerCount", source = "shelve")
|
||||
Fridge map(FridgeDTO in);
|
||||
|
||||
default String toBeerCount(ShelveDTO in) {
|
||||
return in.getCoolBeer().getBeerCount();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(mappingControl = UseDirect.class)
|
||||
public interface ErroneousConversionMapper {
|
||||
|
||||
ErroneousConversionMapper INSTANCE = Mappers.getMapper( ErroneousConversionMapper.class );
|
||||
|
||||
Fridge map(CoolBeerDTO in);
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(mappingControl = UseComplex.class)
|
||||
public interface ErroneousDirectMapper {
|
||||
|
||||
ErroneousDirectMapper INSTANCE = Mappers.getMapper( ErroneousDirectMapper.class );
|
||||
|
||||
@Mapping(target = "shelve", source = "shelve")
|
||||
FridgeDTO map(FridgeDTO in);
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(mappingControl = UseDirect.class)
|
||||
public interface ErroneousMethodMapper {
|
||||
|
||||
ErroneousMethodMapper INSTANCE = Mappers.getMapper( ErroneousMethodMapper.class );
|
||||
|
||||
@Mapping(target = "beerCount", source = "shelve")
|
||||
Fridge map(FridgeDTO in);
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
public class Fridge {
|
||||
|
||||
private int beerCount;
|
||||
|
||||
public int getBeerCount() {
|
||||
return beerCount;
|
||||
}
|
||||
|
||||
public void setBeerCount(int beerCount) {
|
||||
this.beerCount = beerCount;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
public class FridgeDTO {
|
||||
|
||||
private ShelveDTO shelve;
|
||||
|
||||
public ShelveDTO getShelve() {
|
||||
return shelve;
|
||||
}
|
||||
|
||||
public void setShelve(ShelveDTO shelve) {
|
||||
this.shelve = shelve;
|
||||
}
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
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 static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@IssueKey("695")
|
||||
@WithClasses({
|
||||
CoolBeerDTO.class,
|
||||
ShelveDTO.class,
|
||||
Fridge.class,
|
||||
FridgeDTO.class,
|
||||
UseDirect.class,
|
||||
UseComplex.class
|
||||
})
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class MappingControlTest {
|
||||
|
||||
/**
|
||||
* Baseline Test, normal, direct allowed
|
||||
*/
|
||||
@Test
|
||||
@WithClasses(DirectMapper.class)
|
||||
public void directSelectionAllowed() {
|
||||
|
||||
FridgeDTO in = createFridgeDTO();
|
||||
FridgeDTO out = DirectMapper.INSTANCE.map( in );
|
||||
|
||||
assertThat( out ).isNotNull();
|
||||
assertThat( out.getShelve() ).isNotNull();
|
||||
assertThat( out.getShelve() ).isSameAs( in.getShelve() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the deep cloning annotation
|
||||
*/
|
||||
@Test
|
||||
@WithClasses(CloningMapper.class)
|
||||
public void testDeepCloning() {
|
||||
|
||||
FridgeDTO in = createFridgeDTO();
|
||||
FridgeDTO out = CloningMapper.INSTANCE.clone( in );
|
||||
|
||||
assertThat( out ).isNotNull();
|
||||
assertThat( out.getShelve() ).isNotNull();
|
||||
assertThat( out.getShelve() ).isNotSameAs( in.getShelve() );
|
||||
assertThat( out.getShelve().getCoolBeer() ).isNotSameAs( in.getShelve().getCoolBeer() );
|
||||
assertThat( out.getShelve().getCoolBeer().getBeerCount() ).isEqualTo( "5" );
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a nice test. MapStruct looks for a way to map ShelveDto to ShelveDto.
|
||||
* <p>
|
||||
* MapStruct gets too creative when we allow complex (2 step mappings) to convert if we also allow
|
||||
* it to forge methods (which is contradiction with the fact that we do not allow methods on this mapper)
|
||||
*/
|
||||
@Test
|
||||
@WithClasses(ErroneousDirectMapper.class)
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousDirectMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 17,
|
||||
messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \".*\\.ShelveDTO shelve\".*"
|
||||
)
|
||||
})
|
||||
public void directSelectionNotAllowed() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Baseline Test, normal, method allowed
|
||||
*/
|
||||
@Test
|
||||
@WithClasses(MethodMapper.class)
|
||||
public void methodSelectionAllowed() {
|
||||
Fridge fridge = MethodMapper.INSTANCE.map( createFridgeDTO() );
|
||||
|
||||
assertThat( fridge ).isNotNull();
|
||||
assertThat( fridge.getBeerCount() ).isEqualTo( 5 );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses(ErroneousMethodMapper.class)
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousMethodMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 17,
|
||||
messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*"
|
||||
)
|
||||
})
|
||||
public void methodSelectionNotAllowed() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Baseline Test, normal, conversion allowed
|
||||
*/
|
||||
@Test
|
||||
@WithClasses(ConversionMapper.class)
|
||||
public void conversionSelectionAllowed() {
|
||||
Fridge fridge = ConversionMapper.INSTANCE.map( createFridgeDTO().getShelve().getCoolBeer() );
|
||||
|
||||
assertThat( fridge ).isNotNull();
|
||||
assertThat( fridge.getBeerCount() ).isEqualTo( 5 );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses(ErroneousConversionMapper.class)
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousConversionMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 16,
|
||||
messageRegExp = "Can't map property \".*\\.String beerCount\" to \"int beerCount\".*"
|
||||
)
|
||||
})
|
||||
public void conversionSelectionNotAllowed() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Baseline Test, normal, complex mapping allowed
|
||||
*/
|
||||
@Test
|
||||
@WithClasses(ComplexMapper.class)
|
||||
public void complexSelectionAllowed() {
|
||||
Fridge fridge = ComplexMapper.INSTANCE.map( createFridgeDTO() );
|
||||
|
||||
assertThat( fridge ).isNotNull();
|
||||
assertThat( fridge.getBeerCount() ).isEqualTo( 5 );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses(ErroneousComplexMapper.class)
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousComplexMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 17,
|
||||
messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*"
|
||||
)
|
||||
})
|
||||
public void complexSelectionNotAllowed() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ Config.class, ErroneousComplexMapperWithConfig.class })
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousComplexMapperWithConfig.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 17,
|
||||
messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*"
|
||||
)
|
||||
})
|
||||
public void complexSelectionNotAllowedWithConfig() {
|
||||
}
|
||||
|
||||
private FridgeDTO createFridgeDTO() {
|
||||
FridgeDTO fridgeDTO = new FridgeDTO();
|
||||
ShelveDTO shelveDTO = new ShelveDTO();
|
||||
CoolBeerDTO coolBeerDTO = new CoolBeerDTO();
|
||||
fridgeDTO.setShelve( shelveDTO );
|
||||
shelveDTO.setCoolBeer( coolBeerDTO );
|
||||
coolBeerDTO.setBeerCount( "5" );
|
||||
return fridgeDTO;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface MethodMapper {
|
||||
|
||||
MethodMapper INSTANCE = Mappers.getMapper( MethodMapper.class );
|
||||
|
||||
@Mapping(target = "beerCount", source = "shelve")
|
||||
Fridge map(FridgeDTO in);
|
||||
|
||||
default int map(ShelveDTO in) {
|
||||
return Integer.valueOf( in.getCoolBeer().getBeerCount() );
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
public class ShelveDTO {
|
||||
|
||||
private CoolBeerDTO coolBeer;
|
||||
|
||||
public CoolBeerDTO getCoolBeer() {
|
||||
return coolBeer;
|
||||
}
|
||||
|
||||
public void setCoolBeer(CoolBeerDTO coolBeer) {
|
||||
this.coolBeer = coolBeer;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
import org.mapstruct.util.Experimental;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Experimental
|
||||
@MappingControl( MappingControl.Use.COMPLEX_MAPPING )
|
||||
public @interface UseComplex {
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.mappingcontrol;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.mapstruct.control.MappingControl;
|
||||
import org.mapstruct.util.Experimental;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Experimental
|
||||
@MappingControl( MappingControl.Use.DIRECT )
|
||||
public @interface UseDirect {
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user