mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
Compare commits
25 Commits
main
...
1.3.1.Fina
Author | SHA1 | Date | |
---|---|---|---|
|
6f79a53190 | ||
|
0c1c5b7f31 | ||
|
b32cf92519 | ||
|
4dfb7d0ec9 | ||
|
66055f024b | ||
|
fe22c9b1ce | ||
|
55363dfc1c | ||
|
318b30ef23 | ||
|
a71e7c0a0b | ||
|
edcf78a34d | ||
|
ad38786117 | ||
|
23a0420359 | ||
|
2f36a41735 | ||
|
28b4e9ed45 | ||
|
211c627c25 | ||
|
69d0a2d557 | ||
|
0530a80478 | ||
|
6af13ba220 | ||
|
359630aec1 | ||
|
b2bb768f75 | ||
|
bd1f6e6f27 | ||
|
7164ef18c5 | ||
|
546c56b116 | ||
|
9801163402 | ||
|
285cb219c8 |
@ -1,4 +1,5 @@
|
|||||||
language: java
|
language: java
|
||||||
|
dist: trusty
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
install: true
|
install: true
|
||||||
@ -11,13 +12,10 @@ deploy:
|
|||||||
script: "test ${TRAVIS_TEST_RESULT} -eq 0 && mvn -s etc/travis-settings.xml -DskipTests=true deploy"
|
script: "test ${TRAVIS_TEST_RESULT} -eq 0 && mvn -s etc/travis-settings.xml -DskipTests=true deploy"
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
on:
|
on:
|
||||||
|
repo: mapstruct/mapstruct
|
||||||
branch: master
|
branch: master
|
||||||
|
|
||||||
sudo: required
|
sudo: required
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.m2
|
- $HOME/.m2
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- oracle-java8-installer
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>../parent/pom.xml</relativePath>
|
<relativePath>../parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ Gervais Blaise - https://github.com/gervaisb
|
|||||||
Gunnar Morling - https://github.com/gunnarmorling
|
Gunnar Morling - https://github.com/gunnarmorling
|
||||||
Ivo Smid - https://github.com/bedla
|
Ivo Smid - https://github.com/bedla
|
||||||
Jeff Smyth - https://github.com/smythie86
|
Jeff Smyth - https://github.com/smythie86
|
||||||
|
Jonathan Kraska - https://github.com/jakraska
|
||||||
Joshua Spoerri - https://github.com/spoerri
|
Joshua Spoerri - https://github.com/spoerri
|
||||||
Kevin Grüneberg - https://github.com/kevcodez
|
Kevin Grüneberg - https://github.com/kevcodez
|
||||||
Michael Pardo - https://github.com/pardom
|
Michael Pardo - https://github.com/pardom
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>../parent/pom.xml</relativePath>
|
<relativePath>../parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
10
core/pom.xml
10
core/pom.xml
@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>../parent/pom.xml</relativePath>
|
<relativePath>../parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@ -104,6 +104,14 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.github.siom79.japicmp</groupId>
|
<groupId>com.github.siom79.japicmp</groupId>
|
||||||
<artifactId>japicmp-maven-plugin</artifactId>
|
<artifactId>japicmp-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<parameter>
|
||||||
|
<excludes>
|
||||||
|
<!-- Exclude Builder from check see https://github.com/siom79/japicmp/issues/249 -->
|
||||||
|
<exclude>org.mapstruct.Builder</exclude>
|
||||||
|
</excludes>
|
||||||
|
</parameter>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
@ -29,4 +29,12 @@ public @interface Builder {
|
|||||||
* @return the method that needs to tbe invoked on the builder
|
* @return the method that needs to tbe invoked on the builder
|
||||||
*/
|
*/
|
||||||
String buildMethod() default "build";
|
String buildMethod() default "build";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggling builders on / off. Builders are sometimes used solely for unit testing (fluent testdata)
|
||||||
|
* MapStruct will need to use the regular getters /setters in that case.
|
||||||
|
*
|
||||||
|
* @return when true, no builder patterns will be applied
|
||||||
|
*/
|
||||||
|
boolean disableBuilder() default false;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>../parent/pom.xml</relativePath>
|
<relativePath>../parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>../parent/pom.xml</relativePath>
|
<relativePath>../parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ public Set<String> integerStreamToStringSet(Stream<Integer> integers) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return integers.stream().map( integer -> String.valueOf( integer ) )
|
return integers.map( integer -> String.valueOf( integer ) )
|
||||||
.collect( Collectors.toCollection( HashSet<String>::new ) );
|
.collect( Collectors.toCollection( HashSet<String>::new ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ public List<CarDto> carsToCarDtos(Stream<Car> cars) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return integers.stream().map( car -> carToCarDto( car ) )
|
return cars.map( car -> carToCarDto( car ) )
|
||||||
.collect( Collectors.toCollection( ArrayList<CarDto>::new ) );
|
.collect( Collectors.toCollection( ArrayList<CarDto>::new ) );
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
@ -568,7 +568,7 @@ public interface CarMapper {
|
|||||||
|
|
||||||
The generated code of the `updateCarFromDto()` method will update the passed `Car` instance with the properties from the given `CarDto` object. There may be only one parameter marked as mapping target. Instead of `void` you may also set the method's return type to the type of the target parameter, which will cause the generated implementation to update the passed mapping target and return it as well. This allows for fluent invocations of mapping methods.
|
The generated code of the `updateCarFromDto()` method will update the passed `Car` instance with the properties from the given `CarDto` object. There may be only one parameter marked as mapping target. Instead of `void` you may also set the method's return type to the type of the target parameter, which will cause the generated implementation to update the passed mapping target and return it as well. This allows for fluent invocations of mapping methods.
|
||||||
|
|
||||||
Collection- or map-typed properties of the target bean to be updated will be cleared and then populated with the values from the corresponding source collection or map.
|
For `CollectionMappingStrategy.ACCESSOR_ONLY` Collection- or map-typed properties of the target bean to be updated will be cleared and then populated with the values from the corresponding source collection or map. Otherwise, For `CollectionMappingStrategy.ADDER_PREFERRED` or `CollectionMappingStrategy.TARGET_IMMUTABLE` the target will not be cleared and the values will be populated immediately.
|
||||||
|
|
||||||
[[direct-field-mappings]]
|
[[direct-field-mappings]]
|
||||||
=== Mappings with direct field access
|
=== Mappings with direct field access
|
||||||
@ -656,7 +656,7 @@ project on GitHub.
|
|||||||
MapStruct also supports mapping of immutable types via builders.
|
MapStruct also supports mapping of immutable types via builders.
|
||||||
When performing a mapping MapStruct checks if there is a builder for the type being mapped.
|
When performing a mapping MapStruct checks if there is a builder for the type being mapped.
|
||||||
This is done via the `BuilderProvider` SPI.
|
This is done via the `BuilderProvider` SPI.
|
||||||
If a Builder exists for a certain type, than that builder will be used for the mappings.
|
If a Builder exists for a certain type, then that builder will be used for the mappings.
|
||||||
|
|
||||||
The default implementation of the `BuilderProvider` assumes the following:
|
The default implementation of the `BuilderProvider` assumes the following:
|
||||||
|
|
||||||
@ -677,7 +677,7 @@ To finish the mapping MapStruct generates code that will invoke the build method
|
|||||||
[NOTE]
|
[NOTE]
|
||||||
======
|
======
|
||||||
The <<object-factories>> are also considered for the builder type.
|
The <<object-factories>> are also considered for the builder type.
|
||||||
E.g. If an object factory exists for our `PersonBuilder` than this factory would be used instead of the builder creation method.
|
E.g. If an object factory exists for our `PersonBuilder` then this factory would be used instead of the builder creation method.
|
||||||
======
|
======
|
||||||
|
|
||||||
.Person with Builder example
|
.Person with Builder example
|
||||||
@ -768,9 +768,9 @@ In case you want to disable using builders then you can use the `NoOpBuilderProv
|
|||||||
== Retrieving a mapper
|
== Retrieving a mapper
|
||||||
|
|
||||||
[[mappers-factory]]
|
[[mappers-factory]]
|
||||||
=== The Mappers factory
|
=== The Mappers factory (no dependency injection)
|
||||||
|
|
||||||
Mapper instances can be retrieved via the `org.mapstruct.factory.Mappers` class. Just invoke the `getMapper()` method, passing the interface type of the mapper to return:
|
When not using a DI framework, Mapper instances can be retrieved via the `org.mapstruct.factory.Mappers` class. Just invoke the `getMapper()` method, passing the interface type of the mapper to return:
|
||||||
|
|
||||||
.Using the Mappers factory
|
.Using the Mappers factory
|
||||||
====
|
====
|
||||||
@ -783,7 +783,7 @@ CarMapper mapper = Mappers.getMapper( CarMapper.class );
|
|||||||
|
|
||||||
By convention, a mapper interface should define a member called `INSTANCE` which holds a single instance of the mapper type:
|
By convention, a mapper interface should define a member called `INSTANCE` which holds a single instance of the mapper type:
|
||||||
|
|
||||||
.Declaring an instance of a mapper
|
.Declaring an instance of a mapper (interface)
|
||||||
====
|
====
|
||||||
[source, java, linenums]
|
[source, java, linenums]
|
||||||
[subs="verbatim,attributes"]
|
[subs="verbatim,attributes"]
|
||||||
@ -799,6 +799,22 @@ public interface CarMapper {
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
.Declaring an instance of a mapper (abstract class)
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@Mapper
|
||||||
|
public abstract class CarMapper {
|
||||||
|
|
||||||
|
public static final CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
|
||||||
|
|
||||||
|
CarDto carToCarDto(Car car);
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
This pattern makes it very easy for clients to use mapper objects without repeatedly instantiating new instances:
|
This pattern makes it very easy for clients to use mapper objects without repeatedly instantiating new instances:
|
||||||
|
|
||||||
.Accessing a mapper
|
.Accessing a mapper
|
||||||
@ -811,12 +827,13 @@ CarDto dto = CarMapper.INSTANCE.carToCarDto( car );
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
Note that mappers generated by MapStruct are thread-safe and thus can safely be accessed from several threads at the same time.
|
|
||||||
|
Note that mappers generated by MapStruct are stateless and thread-safe and thus can safely be accessed from several threads at the same time.
|
||||||
|
|
||||||
[[using-dependency-injection]]
|
[[using-dependency-injection]]
|
||||||
=== Using dependency injection
|
=== Using dependency injection
|
||||||
|
|
||||||
If you're working with a dependency injection framework such as http://jcp.org/en/jsr/detail?id=346[CDI] (Contexts and Dependency Injection for Java^TM^ EE) or the http://www.springsource.org/spring-framework[Spring Framework], it is recommended to obtain mapper objects via dependency injection as well. For that purpose you can specify the component model which generated mapper classes should be based on either via `@Mapper#componentModel` or using a processor option as described in <<configuration-options>>.
|
If you're working with a dependency injection framework such as http://jcp.org/en/jsr/detail?id=346[CDI] (Contexts and Dependency Injection for Java^TM^ EE) or the http://www.springsource.org/spring-framework[Spring Framework], it is recommended to obtain mapper objects via dependency injection and *not* via the `Mappers` class as described above. For that purpose you can specify the component model which generated mapper classes should be based on either via `@Mapper#componentModel` or using a processor option as described in <<configuration-options>>.
|
||||||
|
|
||||||
Currently there is support for CDI and Spring (the latter either via its custom annotations or using the JSR 330 annotations). See <<configuration-options>> for the allowed values of the `componentModel` attribute which are the same as for the `mapstruct.defaultComponentModel` processor option. In both cases the required annotations will be added to the generated mapper implementations classes in order to make the same subject to dependency injection. The following shows an example using CDI:
|
Currently there is support for CDI and Spring (the latter either via its custom annotations or using the JSR 330 annotations). See <<configuration-options>> for the allowed values of the `componentModel` attribute which are the same as for the `mapstruct.defaultComponentModel` processor option. In both cases the required annotations will be added to the generated mapper implementations classes in order to make the same subject to dependency injection. The following shows an example using CDI:
|
||||||
|
|
||||||
@ -1312,7 +1329,7 @@ public @interface GermanToEnglish {
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
Please take note of the retention `TitleTranslator` on class level, `EnglishToGerman`, `GermanToEnglish` on method level!
|
Please take note of the target `TitleTranslator` on type level, `EnglishToGerman`, `GermanToEnglish` on method level!
|
||||||
|
|
||||||
Then, using the qualifiers, the mapping could look like this:
|
Then, using the qualifiers, the mapping could look like this:
|
||||||
|
|
||||||
@ -2124,7 +2141,6 @@ MapStruct offers control over the object to create when the source argument of t
|
|||||||
However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT` on `@BeanMapping`, `@IterableMapping`, `@MapMapping`, or globally on `@Mapper` or `@MappingConfig`, the mapping result can be altered to return empty *default* values. This means for:
|
However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT` on `@BeanMapping`, `@IterableMapping`, `@MapMapping`, or globally on `@Mapper` or `@MappingConfig`, the mapping result can be altered to return empty *default* values. This means for:
|
||||||
|
|
||||||
* *Bean mappings*: an 'empty' target bean will be returned, with the exception of constants and expressions, they will be populated when present.
|
* *Bean mappings*: an 'empty' target bean will be returned, with the exception of constants and expressions, they will be populated when present.
|
||||||
* *Primitives*: the default values for primitives will be returned, e.g. `false` for `boolean` or `0` for `int`.
|
|
||||||
* *Iterables / Arrays*: an empty iterable will be returned.
|
* *Iterables / Arrays*: an empty iterable will be returned.
|
||||||
* *Maps*: an empty map will be returned.
|
* *Maps*: an empty map will be returned.
|
||||||
|
|
||||||
@ -2695,6 +2711,7 @@ Within those groups, the method invocations are ordered by their location of def
|
|||||||
|
|
||||||
*Important:* the order of methods declared within one type can not be guaranteed, as it depends on the compiler and the processing environment implementation.
|
*Important:* the order of methods declared within one type can not be guaranteed, as it depends on the compiler and the processing environment implementation.
|
||||||
|
|
||||||
|
*Important:* when using a builder, the `@AfterMapping` annotated method must have the builder as `@MappingTarget` annotated parameter so that the method is able to modify the object going to be build. The `build` method is called when the `@AfterMapping` annotated method scope finishes. MapStruct will not call the `@AfterMapping` annotated method if the real target is used as `@MappingTarget` annotated parameter.
|
||||||
|
|
||||||
[[using-spi]]
|
[[using-spi]]
|
||||||
== Using the MapStruct SPI
|
== Using the MapStruct SPI
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>../parent/pom.xml</relativePath>
|
<relativePath>../parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ public class FullFeatureCompilationTest {
|
|||||||
|
|
||||||
// SPI not working correctly here.. (not picked up)
|
// SPI not working correctly here.. (not picked up)
|
||||||
additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1596/*.java" );
|
additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1596/*.java" );
|
||||||
|
additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1801/*.java" );
|
||||||
|
|
||||||
switch ( processorType ) {
|
switch ( processorType ) {
|
||||||
case ORACLE_JAVA_9:
|
case ORACLE_JAVA_9:
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>MapStruct Parent</name>
|
<name>MapStruct Parent</name>
|
||||||
@ -58,7 +58,7 @@
|
|||||||
<connection>scm:git:git://github.com/mapstruct/mapstruct.git</connection>
|
<connection>scm:git:git://github.com/mapstruct/mapstruct.git</connection>
|
||||||
<developerConnection>scm:git:git@github.com:mapstruct/mapstruct.git</developerConnection>
|
<developerConnection>scm:git:git@github.com:mapstruct/mapstruct.git</developerConnection>
|
||||||
<url>https://github.com/mapstruct/mapstruct/</url>
|
<url>https://github.com/mapstruct/mapstruct/</url>
|
||||||
<tag>HEAD</tag>
|
<tag>1.3.1.Final</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
|
4
pom.xml
4
pom.xml
@ -13,7 +13,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>parent/pom.xml</relativePath>
|
<relativePath>parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@ -54,7 +54,7 @@
|
|||||||
<connection>scm:git:git://github.com/mapstruct/mapstruct.git</connection>
|
<connection>scm:git:git://github.com/mapstruct/mapstruct.git</connection>
|
||||||
<developerConnection>scm:git:git@github.com:mapstruct/mapstruct.git</developerConnection>
|
<developerConnection>scm:git:git@github.com:mapstruct/mapstruct.git</developerConnection>
|
||||||
<url>https://github.com/mapstruct/mapstruct/</url>
|
<url>https://github.com/mapstruct/mapstruct/</url>
|
||||||
<tag>HEAD</tag>
|
<tag>1.3.1.Final</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mapstruct</groupId>
|
<groupId>org.mapstruct</groupId>
|
||||||
<artifactId>mapstruct-parent</artifactId>
|
<artifactId>mapstruct-parent</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>1.3.1.Final</version>
|
||||||
<relativePath>../parent/pom.xml</relativePath>
|
<relativePath>../parent/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.model;
|
|||||||
|
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import org.mapstruct.ap.internal.model.common.Assignment;
|
import org.mapstruct.ap.internal.model.common.Assignment;
|
||||||
|
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||||
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;
|
||||||
@ -73,7 +74,7 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
|
|||||||
*
|
*
|
||||||
* @return See above
|
* @return See above
|
||||||
*/
|
*/
|
||||||
Assignment createForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod) {
|
Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, ForgedMethod forgedMethod) {
|
||||||
|
|
||||||
if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) {
|
if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) {
|
||||||
return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) );
|
return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) );
|
||||||
@ -93,6 +94,7 @@ class AbstractBaseBuilder<B extends AbstractBaseBuilder<B>> {
|
|||||||
else {
|
else {
|
||||||
forgedMappingMethod = new BeanMappingMethod.Builder()
|
forgedMappingMethod = new BeanMappingMethod.Builder()
|
||||||
.forgedMethod( forgedMethod )
|
.forgedMethod( forgedMethod )
|
||||||
|
.returnTypeBuilder( builderType )
|
||||||
.mappingContext( ctx )
|
.mappingContext( ctx )
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.model;
|
|||||||
import org.mapstruct.ap.internal.model.common.Assignment;
|
import org.mapstruct.ap.internal.model.common.Assignment;
|
||||||
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.source.BeanMapping;
|
||||||
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
import org.mapstruct.ap.internal.model.source.ForgedMethod;
|
||||||
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
|
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
@ -63,7 +64,11 @@ public abstract class AbstractMappingMethodBuilder<B extends AbstractMappingMeth
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
return createForgedAssignment( sourceRHS, forgedMethod );
|
return createForgedAssignment(
|
||||||
|
sourceRHS,
|
||||||
|
ctx.getTypeFactory().builderTypeFor( targetType, BeanMapping.builderPrismFor( method ) ),
|
||||||
|
forgedMethod
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getName(Type sourceType, Type targetType) {
|
private String getName(Type sourceType, Type targetType) {
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.model;
|
package org.mapstruct.ap.internal.model;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -20,13 +18,13 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.lang.model.type.DeclaredType;
|
import javax.lang.model.type.DeclaredType;
|
||||||
import javax.tools.Diagnostic;
|
import javax.tools.Diagnostic;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.PropertyMapping.ConstantMappingBuilder;
|
import org.mapstruct.ap.internal.model.PropertyMapping.ConstantMappingBuilder;
|
||||||
import org.mapstruct.ap.internal.model.PropertyMapping.JavaExpressionMappingBuilder;
|
import org.mapstruct.ap.internal.model.PropertyMapping.JavaExpressionMappingBuilder;
|
||||||
import org.mapstruct.ap.internal.model.PropertyMapping.PropertyMappingBuilder;
|
import org.mapstruct.ap.internal.model.PropertyMapping.PropertyMappingBuilder;
|
||||||
|
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer;
|
import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer;
|
||||||
@ -50,7 +48,11 @@ import org.mapstruct.ap.internal.util.MapperConfiguration;
|
|||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
|
||||||
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_ABSTRACT;
|
||||||
|
import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_NOT_ASSIGNABLE;
|
||||||
|
import static org.mapstruct.ap.internal.util.Message.GENERAL_ABSTRACT_RETURN_TYPE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally
|
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally
|
||||||
@ -63,20 +65,23 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
private final List<PropertyMapping> propertyMappings;
|
private final List<PropertyMapping> propertyMappings;
|
||||||
private final Map<String, List<PropertyMapping>> mappingsByParameter;
|
private final Map<String, List<PropertyMapping>> mappingsByParameter;
|
||||||
private final List<PropertyMapping> constantMappings;
|
private final List<PropertyMapping> constantMappings;
|
||||||
private final Type resultType;
|
private final Type returnTypeToConstruct;
|
||||||
|
private final BuilderType returnTypeBuilder;
|
||||||
private final MethodReference finalizerMethod;
|
private final MethodReference finalizerMethod;
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private MappingBuilderContext ctx;
|
private MappingBuilderContext ctx;
|
||||||
private Method method;
|
private Method method;
|
||||||
|
|
||||||
|
/* returnType to construct can have a builder */
|
||||||
|
private BuilderType returnTypeBuilder;
|
||||||
|
|
||||||
private Map<String, Accessor> unprocessedTargetProperties;
|
private Map<String, Accessor> unprocessedTargetProperties;
|
||||||
private Map<String, Accessor> unprocessedSourceProperties;
|
private Map<String, Accessor> unprocessedSourceProperties;
|
||||||
private Set<String> targetProperties;
|
private Set<String> targetProperties;
|
||||||
private final List<PropertyMapping> propertyMappings = new ArrayList<>();
|
private final List<PropertyMapping> propertyMappings = new ArrayList<>();
|
||||||
private final Set<Parameter> unprocessedSourceParameters = new HashSet<>();
|
private final Set<Parameter> unprocessedSourceParameters = new HashSet<>();
|
||||||
private NullValueMappingStrategyPrism nullValueMappingStrategy;
|
|
||||||
private SelectionParameters selectionParameters;
|
|
||||||
private final Set<String> existingVariableNames = new HashSet<>();
|
private final Set<String> existingVariableNames = new HashSet<>();
|
||||||
private Map<String, List<Mapping>> methodMappings;
|
private Map<String, List<Mapping>> methodMappings;
|
||||||
private SingleMappingByTargetPropertyNameFunction singleMapping;
|
private SingleMappingByTargetPropertyNameFunction singleMapping;
|
||||||
@ -87,27 +92,69 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder returnTypeBuilder( BuilderType returnTypeBuilder ) {
|
||||||
|
this.returnTypeBuilder = returnTypeBuilder;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder sourceMethod(SourceMethod sourceMethod) {
|
public Builder sourceMethod(SourceMethod sourceMethod) {
|
||||||
singleMapping = new SourceMethodSingleMapping( sourceMethod );
|
singleMapping = new SourceMethodSingleMapping( sourceMethod );
|
||||||
return setupMethodWithMapping( sourceMethod );
|
this.method = sourceMethod;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder forgedMethod(Method method) {
|
public Builder forgedMethod(Method method) {
|
||||||
singleMapping = new EmptySingleMapping();
|
singleMapping = new EmptySingleMapping();
|
||||||
return setupMethodWithMapping( method );
|
this.method = method;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Builder setupMethodWithMapping(Method sourceMethod) {
|
public BeanMappingMethod build() {
|
||||||
this.method = sourceMethod;
|
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
|
||||||
this.methodMappings = sourceMethod.getMappingOptions().getMappings();
|
SelectionParameters selectionParameters = beanMapping != null ? beanMapping.getSelectionParameters() : null;
|
||||||
CollectionMappingStrategyPrism cms = sourceMethod.getMapperConfiguration().getCollectionMappingStrategy();
|
|
||||||
Type mappingType = method.getResultType();
|
/* the return type that needs to be constructed (new or factorized), so for instance: */
|
||||||
if ( !method.isUpdateMethod() ) {
|
/* 1) the return type of a non-update method */
|
||||||
mappingType = mappingType.getEffectiveType();
|
/* 2) or the implementation type that needs to be used when the return type is abstract */
|
||||||
|
/* 3) or the builder whenever the return type is immutable */
|
||||||
|
Type returnTypeToConstruct = null;
|
||||||
|
|
||||||
|
/* factory or builder method to construct the returnTypeToConstruct */
|
||||||
|
MethodReference factoryMethod = null;
|
||||||
|
|
||||||
|
// determine which return type to construct
|
||||||
|
if ( !method.getReturnType().isVoid() ) {
|
||||||
|
Type returnTypeImpl = getReturnTypeToConstructFromSelectionParameters( selectionParameters );
|
||||||
|
if ( returnTypeImpl != null ) {
|
||||||
|
factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters );
|
||||||
|
if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) {
|
||||||
|
returnTypeToConstruct = returnTypeImpl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isBuilderRequired() ) {
|
||||||
|
returnTypeImpl = returnTypeBuilder.getBuilder();
|
||||||
|
factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters );
|
||||||
|
if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) {
|
||||||
|
returnTypeToConstruct = returnTypeImpl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !method.isUpdateMethod() ) {
|
||||||
|
returnTypeImpl = method.getReturnType();
|
||||||
|
factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters );
|
||||||
|
if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) {
|
||||||
|
returnTypeToConstruct = returnTypeImpl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Accessor> accessors = mappingType
|
/* the type that needs to be used in the mapping process as target */
|
||||||
.getPropertyWriteAccessors( cms );
|
Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct;
|
||||||
|
|
||||||
|
this.methodMappings = this.method.getMappingOptions().getMappings();
|
||||||
|
CollectionMappingStrategyPrism cms = this.method.getMapperConfiguration().getCollectionMappingStrategy();
|
||||||
|
|
||||||
|
// determine accessors
|
||||||
|
Map<String, Accessor> accessors = resultTypeToMap.getPropertyWriteAccessors( cms );
|
||||||
this.targetProperties = accessors.keySet();
|
this.targetProperties = accessors.keySet();
|
||||||
|
|
||||||
this.unprocessedTargetProperties = new LinkedHashMap<>( accessors );
|
this.unprocessedTargetProperties = new LinkedHashMap<>( accessors );
|
||||||
@ -125,17 +172,14 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
}
|
}
|
||||||
existingVariableNames.addAll( method.getParameterNames() );
|
existingVariableNames.addAll( method.getParameterNames() );
|
||||||
|
|
||||||
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
|
// get bean mapping (when specified as annotation )
|
||||||
|
|
||||||
if ( beanMapping != null ) {
|
if ( beanMapping != null ) {
|
||||||
for ( String ignoreUnmapped : beanMapping.getIgnoreUnmappedSourceProperties() ) {
|
for ( String ignoreUnmapped : beanMapping.getIgnoreUnmappedSourceProperties() ) {
|
||||||
unprocessedSourceProperties.remove( ignoreUnmapped );
|
unprocessedSourceProperties.remove( ignoreUnmapped );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BeanMappingMethod build() {
|
|
||||||
// map properties with mapping
|
// map properties with mapping
|
||||||
boolean mappingErrorOccured = handleDefinedMappings();
|
boolean mappingErrorOccured = handleDefinedMappings();
|
||||||
if ( mappingErrorOccured ) {
|
if ( mappingErrorOccured ) {
|
||||||
@ -158,87 +202,30 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
reportErrorForUnmappedTargetPropertiesIfRequired();
|
reportErrorForUnmappedTargetPropertiesIfRequired();
|
||||||
reportErrorForUnmappedSourcePropertiesIfRequired();
|
reportErrorForUnmappedSourcePropertiesIfRequired();
|
||||||
|
|
||||||
// get bean mapping (when specified as annotation )
|
|
||||||
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
|
|
||||||
BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() );
|
|
||||||
|
|
||||||
// mapNullToDefault
|
// mapNullToDefault
|
||||||
NullValueMappingStrategyPrism nullValueMappingStrategy =
|
NullValueMappingStrategyPrism nullValueMappingStrategy =
|
||||||
beanMapping != null ? beanMapping.getNullValueMappingStrategy() : null;
|
beanMapping != null ? beanMapping.getNullValueMappingStrategy() : null;
|
||||||
boolean mapNullToDefault = method.getMapperConfiguration().isMapToDefault( nullValueMappingStrategy );
|
boolean mapNullToDefault = method.getMapperConfiguration().isMapToDefault( nullValueMappingStrategy );
|
||||||
|
|
||||||
|
// sort
|
||||||
// selectionParameters
|
|
||||||
SelectionParameters selectionParameters = beanMapping != null ? beanMapping.getSelectionParameters() : null;
|
|
||||||
|
|
||||||
// check if there's a factory method for the result type
|
|
||||||
MethodReference factoryMethod = null;
|
|
||||||
if ( !method.isUpdateMethod() ) {
|
|
||||||
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod(
|
|
||||||
method,
|
|
||||||
method.getResultType(),
|
|
||||||
selectionParameters,
|
|
||||||
ctx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's no factory method, try the resultType in the @BeanMapping
|
|
||||||
Type resultType = null;
|
|
||||||
if ( factoryMethod == null ) {
|
|
||||||
if ( selectionParameters != null && selectionParameters.getResultType() != null ) {
|
|
||||||
resultType = ctx.getTypeFactory().getType( selectionParameters.getResultType() ).getEffectiveType();
|
|
||||||
if ( resultType.isAbstract() ) {
|
|
||||||
ctx.getMessager().printMessage(
|
|
||||||
method.getExecutable(),
|
|
||||||
beanMappingPrism.mirror,
|
|
||||||
Message.BEANMAPPING_ABSTRACT,
|
|
||||||
resultType,
|
|
||||||
method.getResultType()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ( !resultType.isAssignableTo( method.getResultType() ) ) {
|
|
||||||
ctx.getMessager().printMessage(
|
|
||||||
method.getExecutable(),
|
|
||||||
beanMappingPrism.mirror,
|
|
||||||
Message.BEANMAPPING_NOT_ASSIGNABLE, resultType, method.getResultType()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ( !resultType.hasEmptyAccessibleContructor() ) {
|
|
||||||
ctx.getMessager().printMessage(
|
|
||||||
method.getExecutable(),
|
|
||||||
beanMappingPrism.mirror,
|
|
||||||
Message.GENERAL_NO_SUITABLE_CONSTRUCTOR,
|
|
||||||
resultType
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( !method.isUpdateMethod() && method.getReturnType().getEffectiveType().isAbstract() ) {
|
|
||||||
ctx.getMessager().printMessage(
|
|
||||||
method.getExecutable(),
|
|
||||||
Message.GENERAL_ABSTRACT_RETURN_TYPE,
|
|
||||||
method.getReturnType().getEffectiveType()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ( !method.isUpdateMethod() &&
|
|
||||||
!method.getReturnType().getEffectiveType().hasEmptyAccessibleContructor() ) {
|
|
||||||
ctx.getMessager().printMessage(
|
|
||||||
method.getExecutable(),
|
|
||||||
Message.GENERAL_NO_SUITABLE_CONSTRUCTOR,
|
|
||||||
method.getReturnType().getEffectiveType()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sortPropertyMappingsByDependencies();
|
sortPropertyMappingsByDependencies();
|
||||||
|
|
||||||
|
// before / after mappings
|
||||||
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleMethodResolver.beforeMappingMethods(
|
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleMethodResolver.beforeMappingMethods(
|
||||||
method,
|
method,
|
||||||
|
resultTypeToMap,
|
||||||
selectionParameters,
|
selectionParameters,
|
||||||
ctx,
|
ctx,
|
||||||
existingVariableNames
|
existingVariableNames
|
||||||
);
|
);
|
||||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||||
LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariableNames );
|
LifecycleMethodResolver.afterMappingMethods(
|
||||||
|
method,
|
||||||
|
resultTypeToMap,
|
||||||
|
selectionParameters,
|
||||||
|
ctx,
|
||||||
|
existingVariableNames
|
||||||
|
);
|
||||||
|
|
||||||
if (factoryMethod != null && method instanceof ForgedMethod ) {
|
if (factoryMethod != null && method instanceof ForgedMethod ) {
|
||||||
( (ForgedMethod) method ).addThrownTypes( factoryMethod.getThrownTypes() );
|
( (ForgedMethod) method ).addThrownTypes( factoryMethod.getThrownTypes() );
|
||||||
@ -246,7 +233,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
|
|
||||||
MethodReference finalizeMethod = null;
|
MethodReference finalizeMethod = null;
|
||||||
|
|
||||||
if ( shouldCallFinalizerMethod( resultType == null ? method.getResultType() : resultType ) ) {
|
if ( shouldCallFinalizerMethod( returnTypeToConstruct ) ) {
|
||||||
finalizeMethod = getFinalizerMethod();
|
finalizeMethod = getFinalizerMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,32 +243,41 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
propertyMappings,
|
propertyMappings,
|
||||||
factoryMethod,
|
factoryMethod,
|
||||||
mapNullToDefault,
|
mapNullToDefault,
|
||||||
resultType,
|
returnTypeToConstruct,
|
||||||
|
returnTypeBuilder,
|
||||||
beforeMappingMethods,
|
beforeMappingMethods,
|
||||||
afterMappingMethods,
|
afterMappingMethods,
|
||||||
finalizeMethod
|
finalizeMethod
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldCallFinalizerMethod(Type resultType) {
|
/**
|
||||||
Type returnType = method.getReturnType();
|
* @return builder is required when there is a returnTypeBuilder and the mapping method is not update method.
|
||||||
if ( returnType.isVoid() ) {
|
* However, builder is also required when there is a returnTypeBuilder, the mapping target is the builder and
|
||||||
|
* builder is not assignable to the return type (so without building).
|
||||||
|
*/
|
||||||
|
private boolean isBuilderRequired() {
|
||||||
|
return returnTypeBuilder != null
|
||||||
|
&& ( !method.isUpdateMethod() || !method.isMappingTargetAssignableToReturnType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldCallFinalizerMethod(Type returnTypeToConstruct ) {
|
||||||
|
if ( returnTypeToConstruct == null ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Type mappingType = method.isUpdateMethod() ? resultType : resultType.getEffectiveType();
|
else if ( returnTypeToConstruct.isAssignableTo( method.getReturnType() ) ) {
|
||||||
if ( mappingType.isAssignableTo( returnType ) ) {
|
|
||||||
// If the mapping type can be assigned to the return type then we
|
// If the mapping type can be assigned to the return type then we
|
||||||
// don't need a finalizer method
|
// don't need a finalizer method
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnType.getBuilderType() != null;
|
return returnTypeBuilder != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReference getFinalizerMethod() {
|
private MethodReference getFinalizerMethod() {
|
||||||
return BuilderFinisherMethodResolver.getBuilderFinisherMethod(
|
return BuilderFinisherMethodResolver.getBuilderFinisherMethod(
|
||||||
method,
|
method,
|
||||||
method.getReturnType().getBuilderType(),
|
returnTypeBuilder,
|
||||||
ctx
|
ctx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -372,6 +368,88 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Type getReturnTypeToConstructFromSelectionParameters(SelectionParameters selectionParams) {
|
||||||
|
if ( selectionParams != null && selectionParams.getResultType() != null ) {
|
||||||
|
return ctx.getTypeFactory().getType( selectionParams.getResultType() );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) {
|
||||||
|
|
||||||
|
boolean error = true;
|
||||||
|
if ( resultType.isAbstract() ) {
|
||||||
|
ctx.getMessager().printMessage(
|
||||||
|
method.getExecutable(),
|
||||||
|
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
|
||||||
|
BEANMAPPING_ABSTRACT,
|
||||||
|
resultType,
|
||||||
|
method.getResultType()
|
||||||
|
);
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
else if ( !resultType.isAssignableTo( method.getResultType() ) ) {
|
||||||
|
ctx.getMessager().printMessage(
|
||||||
|
method.getExecutable(),
|
||||||
|
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
|
||||||
|
BEANMAPPING_NOT_ASSIGNABLE,
|
||||||
|
resultType,
|
||||||
|
method.getResultType()
|
||||||
|
);
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
else if ( !resultType.hasEmptyAccessibleContructor() ) {
|
||||||
|
ctx.getMessager().printMessage(
|
||||||
|
method.getExecutable(),
|
||||||
|
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
|
||||||
|
Message.GENERAL_NO_SUITABLE_CONSTRUCTOR,
|
||||||
|
resultType
|
||||||
|
);
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canReturnTypeBeConstructed(Type returnType) {
|
||||||
|
boolean error = true;
|
||||||
|
if ( returnType.isAbstract() ) {
|
||||||
|
ctx.getMessager().printMessage(
|
||||||
|
method.getExecutable(),
|
||||||
|
GENERAL_ABSTRACT_RETURN_TYPE,
|
||||||
|
returnType
|
||||||
|
);
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
else if ( !returnType.hasEmptyAccessibleContructor() ) {
|
||||||
|
ctx.getMessager().printMessage(
|
||||||
|
method.getExecutable(),
|
||||||
|
Message.GENERAL_NO_SUITABLE_CONSTRUCTOR,
|
||||||
|
returnType
|
||||||
|
);
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a factory method for a return type or for a builder.
|
||||||
|
* @param returnTypeImpl the return type implementation to construct
|
||||||
|
* @param selectionParameters
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private MethodReference getFactoryMethod(Type returnTypeImpl, SelectionParameters selectionParameters) {
|
||||||
|
MethodReference factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method,
|
||||||
|
returnTypeImpl,
|
||||||
|
selectionParameters,
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
if ( factoryMethod == null && returnTypeBuilder != null ) {
|
||||||
|
factoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod( method, returnTypeBuilder );
|
||||||
|
}
|
||||||
|
|
||||||
|
return factoryMethod;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over all defined mapping methods ({@code @Mapping(s)}), either directly given or inherited from the
|
* Iterates over all defined mapping methods ({@code @Mapping(s)}), either directly given or inherited from the
|
||||||
* inverse mapping method.
|
* inverse mapping method.
|
||||||
@ -396,23 +474,30 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
for ( Mapping mapping : entry.getValue() ) {
|
for ( Mapping mapping : entry.getValue() ) {
|
||||||
TargetReference targetReference = mapping.getTargetReference();
|
TargetReference targetReference = mapping.getTargetReference();
|
||||||
if ( targetReference.isValid() ) {
|
if ( targetReference.isValid() ) {
|
||||||
if ( !handledTargets.contains( first( targetReference.getPropertyEntries() ).getFullName() ) ) {
|
String target = first( targetReference.getPropertyEntries() ).getFullName();
|
||||||
|
if ( !handledTargets.contains( target ) ) {
|
||||||
if ( handleDefinedMapping( mapping, handledTargets ) ) {
|
if ( handleDefinedMapping( mapping, handledTargets ) ) {
|
||||||
errorOccurred = true;
|
errorOccurred = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) {
|
||||||
|
List<PropertyEntry> sourceEntries = mapping.getSourceReference().getPropertyEntries();
|
||||||
|
if ( !sourceEntries.isEmpty() ) {
|
||||||
|
String source = first( sourceEntries ).getFullName();
|
||||||
|
unprocessedSourceProperties.remove( source );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
errorOccurred = true;
|
errorOccurred = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove the remaining name based properties
|
||||||
for ( String handledTarget : handledTargets ) {
|
for ( String handledTarget : handledTargets ) {
|
||||||
// In order to avoid: "Unknown property foo in return type" in case of duplicate
|
|
||||||
// target mappings
|
|
||||||
unprocessedTargetProperties.remove( handledTarget );
|
unprocessedTargetProperties.remove( handledTarget );
|
||||||
unprocessedDefinedTargets.remove( handledTarget );
|
unprocessedDefinedTargets.remove( handledTarget );
|
||||||
unprocessedSourceProperties.remove( handledTarget );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorOccurred;
|
return errorOccurred;
|
||||||
@ -497,7 +582,6 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
.nullValuePropertyMappingStrategy( mapping.getNullValuePropertyMappingStrategy() )
|
.nullValuePropertyMappingStrategy( mapping.getNullValuePropertyMappingStrategy() )
|
||||||
.build();
|
.build();
|
||||||
handledTargets.add( propertyName );
|
handledTargets.add( propertyName );
|
||||||
unprocessedSourceProperties.remove( mapping.getSourceName() );
|
|
||||||
unprocessedSourceParameters.remove( sourceRef.getParameter() );
|
unprocessedSourceParameters.remove( sourceRef.getParameter() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -584,7 +668,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
Accessor sourceReadAccessor =
|
Accessor sourceReadAccessor =
|
||||||
sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName );
|
sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName );
|
||||||
|
|
||||||
ExecutableElementAccessor sourcePresenceChecker =
|
Accessor sourcePresenceChecker =
|
||||||
sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName );
|
sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName );
|
||||||
|
|
||||||
if ( sourceReadAccessor != null ) {
|
if ( sourceReadAccessor != null ) {
|
||||||
@ -749,7 +833,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
ForgedMethodHistory history = forgedMethod.getHistory();
|
ForgedMethodHistory history = forgedMethod.getHistory();
|
||||||
ctx.getMessager().printMessage(
|
ctx.getMessager().printMessage(
|
||||||
this.method.getExecutable(),
|
this.method.getExecutable(),
|
||||||
Message.PROPERTYMAPPING_MAPPING_NOT_FOUND,
|
Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND,
|
||||||
history.createSourcePropertyErrorMessage(),
|
history.createSourcePropertyErrorMessage(),
|
||||||
history.getTargetType(),
|
history.getTargetType(),
|
||||||
history.createTargetPropertyName(),
|
history.createTargetPropertyName(),
|
||||||
@ -834,7 +918,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
List<PropertyMapping> propertyMappings,
|
List<PropertyMapping> propertyMappings,
|
||||||
MethodReference factoryMethod,
|
MethodReference factoryMethod,
|
||||||
boolean mapNullToDefault,
|
boolean mapNullToDefault,
|
||||||
Type resultType,
|
Type returnTypeToConstruct,
|
||||||
|
BuilderType returnTypeBuilder,
|
||||||
List<LifecycleCallbackMethodReference> beforeMappingReferences,
|
List<LifecycleCallbackMethodReference> beforeMappingReferences,
|
||||||
List<LifecycleCallbackMethodReference> afterMappingReferences,
|
List<LifecycleCallbackMethodReference> afterMappingReferences,
|
||||||
MethodReference finalizerMethod) {
|
MethodReference finalizerMethod) {
|
||||||
@ -848,6 +933,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.propertyMappings = propertyMappings;
|
this.propertyMappings = propertyMappings;
|
||||||
|
this.returnTypeBuilder = returnTypeBuilder;
|
||||||
this.finalizerMethod = finalizerMethod;
|
this.finalizerMethod = finalizerMethod;
|
||||||
|
|
||||||
// intialize constant mappings as all mappings, but take out the ones that can be contributed to a
|
// intialize constant mappings as all mappings, but take out the ones that can be contributed to a
|
||||||
@ -864,11 +950,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.resultType = resultType;
|
this.returnTypeToConstruct = returnTypeToConstruct;
|
||||||
}
|
|
||||||
|
|
||||||
public List<PropertyMapping> getPropertyMappings() {
|
|
||||||
return propertyMappings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PropertyMapping> getConstantMappings() {
|
public List<PropertyMapping> getConstantMappings() {
|
||||||
@ -880,14 +962,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
return mappingsByParameter.get( parameter.getName() );
|
return mappingsByParameter.get( parameter.getName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Type getReturnTypeToConstruct() {
|
||||||
public Type getResultType() {
|
return returnTypeToConstruct;
|
||||||
if ( resultType == null ) {
|
|
||||||
return super.getResultType();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return resultType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodReference getFinalizerMethod() {
|
public MethodReference getFinalizerMethod() {
|
||||||
@ -902,12 +978,12 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
|||||||
types.addAll( propertyMapping.getImportTypes() );
|
types.addAll( propertyMapping.getImportTypes() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !isExistingInstanceMapping() ) {
|
if ( returnTypeToConstruct != null ) {
|
||||||
types.addAll( getResultType().getEffectiveType().getImportTypes() );
|
types.addAll( returnTypeToConstruct.getImportTypes() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( getResultType().getBuilderType() != null ) {
|
if ( returnTypeBuilder != null ) {
|
||||||
types.add( getResultType().getBuilderType().getOwningType() );
|
types.add( returnTypeBuilder.getOwningType() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
|
@ -12,7 +12,6 @@ import org.mapstruct.ap.internal.model.common.BuilderType;
|
|||||||
import org.mapstruct.ap.internal.model.source.BeanMapping;
|
import org.mapstruct.ap.internal.model.source.BeanMapping;
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||||
import org.mapstruct.ap.internal.util.MapperConfiguration;
|
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ public class BuilderFinisherMethodResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuilderPrism builderMapping = builderMappingPrism( method, ctx );
|
BuilderPrism builderMapping = BeanMapping.builderPrismFor( method );
|
||||||
if ( builderMapping == null && buildMethods.size() == 1 ) {
|
if ( builderMapping == null && buildMethods.size() == 1 ) {
|
||||||
return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() );
|
return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() );
|
||||||
}
|
}
|
||||||
@ -77,12 +76,4 @@ public class BuilderFinisherMethodResolver {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BuilderPrism builderMappingPrism(Method method, MappingBuilderContext ctx) {
|
|
||||||
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
|
|
||||||
if ( beanMapping != null && beanMapping.getBuilder() != null ) {
|
|
||||||
return beanMapping.getBuilder();
|
|
||||||
}
|
|
||||||
return MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ).getBuilderPrism();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
|
|||||||
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
|
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
|
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
|
||||||
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
|
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
|
||||||
@ -57,7 +58,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
private Accessor targetReadAccessor;
|
private Accessor targetReadAccessor;
|
||||||
private Type targetType;
|
private Type targetType;
|
||||||
private String targetPropertyName;
|
private String targetPropertyName;
|
||||||
private PropertyMapping.TargetWriteAccessorType targetAccessorType;
|
private AccessorType targetAccessorType;
|
||||||
private Assignment assignment;
|
private Assignment assignment;
|
||||||
private SourceRHS sourceRHS;
|
private SourceRHS sourceRHS;
|
||||||
private NullValueCheckStrategyPrism nvcs;
|
private NullValueCheckStrategyPrism nvcs;
|
||||||
@ -88,7 +89,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CollectionAssignmentBuilder targetAccessorType(PropertyMapping.TargetWriteAccessorType targetAccessorType) {
|
public CollectionAssignmentBuilder targetAccessorType(AccessorType targetAccessorType) {
|
||||||
this.targetAccessorType = targetAccessorType;
|
this.targetAccessorType = targetAccessorType;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -129,8 +130,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy();
|
CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy();
|
||||||
boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE || targetReadAccessor == null;
|
boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE || targetReadAccessor == null;
|
||||||
|
|
||||||
if ( targetAccessorType == PropertyMapping.TargetWriteAccessorType.SETTER ||
|
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) {
|
||||||
targetAccessorType == PropertyMapping.TargetWriteAccessorType.FIELD ) {
|
|
||||||
|
|
||||||
if ( result.isCallingUpdateMethod() && !targetImmutable ) {
|
if ( result.isCallingUpdateMethod() && !targetImmutable ) {
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
result,
|
result,
|
||||||
method.getThrownTypes(),
|
method.getThrownTypes(),
|
||||||
factoryMethod,
|
factoryMethod,
|
||||||
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
|
targetAccessorType == AccessorType.FIELD,
|
||||||
targetType,
|
targetType,
|
||||||
true,
|
true,
|
||||||
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
|
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
|
||||||
@ -165,7 +165,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
nvcs,
|
nvcs,
|
||||||
nvpms,
|
nvpms,
|
||||||
ctx.getTypeFactory(),
|
ctx.getTypeFactory(),
|
||||||
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
|
targetAccessorType == AccessorType.FIELD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
|
else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
|
||||||
@ -176,7 +176,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
method.getThrownTypes(),
|
method.getThrownTypes(),
|
||||||
targetType,
|
targetType,
|
||||||
ctx.getTypeFactory(),
|
ctx.getTypeFactory(),
|
||||||
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
|
targetAccessorType == AccessorType.FIELD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -185,7 +185,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
result,
|
result,
|
||||||
method.getThrownTypes(),
|
method.getThrownTypes(),
|
||||||
targetType,
|
targetType,
|
||||||
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
|
targetAccessorType == AccessorType.FIELD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ public class CollectionAssignmentBuilder {
|
|||||||
result,
|
result,
|
||||||
method.getThrownTypes(),
|
method.getThrownTypes(),
|
||||||
targetType,
|
targetType,
|
||||||
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
|
targetAccessorType == AccessorType.FIELD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
|||||||
|
|
||||||
MethodReference factoryMethod = null;
|
MethodReference factoryMethod = null;
|
||||||
if ( !method.isUpdateMethod() ) {
|
if ( !method.isUpdateMethod() ) {
|
||||||
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, method.getResultType(), null, ctx );
|
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, null, ctx );
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> existingVariables = new HashSet<>( method.getParameterNames() );
|
Set<String> existingVariables = new HashSet<>( method.getParameterNames() );
|
||||||
|
@ -10,7 +10,6 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.common.Accessibility;
|
import org.mapstruct.ap.internal.model.common.Accessibility;
|
||||||
|
@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.model.common.ModelElement;
|
|||||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
|
|
||||||
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model element that can be used to create a type of {@link Iterable} or {@link java.util.Map}. If an implementation
|
* Model element that can be used to create a type of {@link Iterable} or {@link java.util.Map}. If an implementation
|
||||||
* type is used and the target type has a constructor with {@code int} as parameter and the source parameter is of
|
* type is used and the target type has a constructor with {@code int} as parameter and the source parameter is of
|
||||||
@ -69,6 +71,20 @@ public class IterableCreation extends ModelElement {
|
|||||||
if ( factoryMethod == null && resultType.getImplementationType() != null ) {
|
if ( factoryMethod == null && resultType.getImplementationType() != null ) {
|
||||||
types.addAll( resultType.getImplementationType().getImportTypes() );
|
types.addAll( resultType.getImplementationType().getImportTypes() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( isEnumSet() ) {
|
||||||
|
types.add( getEnumSetElementType() );
|
||||||
|
// The result type itself is an EnumSet
|
||||||
|
types.add( resultType );
|
||||||
|
}
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Type getEnumSetElementType() {
|
||||||
|
return first( getResultType().determineTypeArguments( Iterable.class ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnumSet() {
|
||||||
|
return "java.util.EnumSet".equals( resultType.getFullyQualifiedName() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,48 @@ public final class LifecycleMethodResolver {
|
|||||||
private LifecycleMethodResolver() {
|
private LifecycleMethodResolver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param method the method to obtain the beforeMapping methods for
|
||||||
|
* @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract
|
||||||
|
* @param selectionParameters method selectionParameters
|
||||||
|
* @param ctx the builder context
|
||||||
|
* @param existingVariableNames the existing variable names in the mapping method
|
||||||
|
* @return all applicable {@code @BeforeMapping} methods for the given method
|
||||||
|
*/
|
||||||
|
public static List<LifecycleCallbackMethodReference> beforeMappingMethods(Method method,
|
||||||
|
Type alternativeTarget,
|
||||||
|
SelectionParameters selectionParameters,
|
||||||
|
MappingBuilderContext ctx,
|
||||||
|
Set<String> existingVariableNames) {
|
||||||
|
return collectLifecycleCallbackMethods( method,
|
||||||
|
alternativeTarget,
|
||||||
|
selectionParameters,
|
||||||
|
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
||||||
|
ctx,
|
||||||
|
existingVariableNames );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param method the method to obtain the afterMapping methods for
|
||||||
|
* @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract
|
||||||
|
* @param selectionParameters method selectionParameters
|
||||||
|
* @param ctx the builder context
|
||||||
|
* @param existingVariableNames list of already used variable names
|
||||||
|
* @return all applicable {@code @AfterMapping} methods for the given method
|
||||||
|
*/
|
||||||
|
public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method method,
|
||||||
|
Type alternativeTarget,
|
||||||
|
SelectionParameters selectionParameters,
|
||||||
|
MappingBuilderContext ctx,
|
||||||
|
Set<String> existingVariableNames) {
|
||||||
|
return collectLifecycleCallbackMethods( method,
|
||||||
|
alternativeTarget,
|
||||||
|
selectionParameters,
|
||||||
|
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
||||||
|
ctx,
|
||||||
|
existingVariableNames );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param method the method to obtain the beforeMapping methods for
|
* @param method the method to obtain the beforeMapping methods for
|
||||||
* @param selectionParameters method selectionParameters
|
* @param selectionParameters method selectionParameters
|
||||||
@ -42,6 +84,7 @@ public final class LifecycleMethodResolver {
|
|||||||
MappingBuilderContext ctx,
|
MappingBuilderContext ctx,
|
||||||
Set<String> existingVariableNames) {
|
Set<String> existingVariableNames) {
|
||||||
return collectLifecycleCallbackMethods( method,
|
return collectLifecycleCallbackMethods( method,
|
||||||
|
method.getResultType(),
|
||||||
selectionParameters,
|
selectionParameters,
|
||||||
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
||||||
ctx,
|
ctx,
|
||||||
@ -56,10 +99,11 @@ public final class LifecycleMethodResolver {
|
|||||||
* @return all applicable {@code @AfterMapping} methods for the given method
|
* @return all applicable {@code @AfterMapping} methods for the given method
|
||||||
*/
|
*/
|
||||||
public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method method,
|
public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method method,
|
||||||
SelectionParameters selectionParameters,
|
SelectionParameters selectionParameters,
|
||||||
MappingBuilderContext ctx,
|
MappingBuilderContext ctx,
|
||||||
Set<String> existingVariableNames) {
|
Set<String> existingVariableNames) {
|
||||||
return collectLifecycleCallbackMethods( method,
|
return collectLifecycleCallbackMethods( method,
|
||||||
|
method.getResultType(),
|
||||||
selectionParameters,
|
selectionParameters,
|
||||||
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
|
||||||
ctx,
|
ctx,
|
||||||
@ -87,17 +131,11 @@ public final class LifecycleMethodResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static List<LifecycleCallbackMethodReference> collectLifecycleCallbackMethods(
|
private static List<LifecycleCallbackMethodReference> collectLifecycleCallbackMethods(
|
||||||
Method method, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
|
Method method, Type targetType, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
|
||||||
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
||||||
|
|
||||||
MethodSelectors selectors =
|
MethodSelectors selectors =
|
||||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory() );
|
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() );
|
||||||
|
|
||||||
Type targetType = method.getResultType();
|
|
||||||
|
|
||||||
if ( !method.isUpdateMethod() ) {
|
|
||||||
targetType = targetType.getEffectiveType();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
|
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
|
||||||
method,
|
method,
|
||||||
|
@ -181,10 +181,9 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
|
|||||||
MethodReference factoryMethod = null;
|
MethodReference factoryMethod = null;
|
||||||
if ( !method.isUpdateMethod() ) {
|
if ( !method.isUpdateMethod() ) {
|
||||||
factoryMethod = ObjectFactoryMethodResolver
|
factoryMethod = ObjectFactoryMethodResolver
|
||||||
.getFactoryMethod( method, method.getResultType(), null, ctx );
|
.getFactoryMethod( method, null, ctx );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes(), keyTargetType, false );
|
keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes(), keyTargetType, false );
|
||||||
valueAssignment = new LocalVarWrapper( valueAssignment, method.getThrownTypes(), valueTargetType, false );
|
valueAssignment = new LocalVarWrapper( valueAssignment, method.getThrownTypes(), valueTargetType, false );
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSourceReference() {
|
public String getSourceReference() {
|
||||||
return assignment.getSourceReference();
|
return assignment != null ? assignment.getSourceReference() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -160,6 +160,16 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
groupedByTP.singleTargetReferences.get( targetProperty )
|
groupedByTP.singleTargetReferences.get( targetProperty )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// We need an update method in the when one of the following is satisfied:
|
||||||
|
// 1) Multiple source parameters for the target reference
|
||||||
|
// 2) Multiple source references for the target reference
|
||||||
|
// The reason for this is that multiple sources have effect on the target.
|
||||||
|
// See Issue1828Test for more info.
|
||||||
|
boolean forceUpdateMethod =
|
||||||
|
multipleSourceParametersForTP || groupedSourceReferences.groupedBySourceReferences.size() > 1;
|
||||||
|
|
||||||
|
boolean forceUpdateMethodOrNonNestedReferencesPresent =
|
||||||
|
forceUpdateMethod || !groupedSourceReferences.nonNested.isEmpty();
|
||||||
// For all the groupedBySourceReferences we need to create property mappings
|
// For all the groupedBySourceReferences we need to create property mappings
|
||||||
// from the Mappings and not restrict on the defined mappings (allow to forge name based mapping)
|
// from the Mappings and not restrict on the defined mappings (allow to forge name based mapping)
|
||||||
// if we have composite methods i.e. more then 2 parameters then we have to force a creation
|
// if we have composite methods i.e. more then 2 parameters then we have to force a creation
|
||||||
@ -168,8 +178,6 @@ public class NestedTargetPropertyMappingHolder {
|
|||||||
.groupedBySourceReferences
|
.groupedBySourceReferences
|
||||||
.entrySet() ) {
|
.entrySet() ) {
|
||||||
PropertyEntry sourceEntry = entryBySP.getKey();
|
PropertyEntry sourceEntry = entryBySP.getKey();
|
||||||
boolean forceUpdateMethodOrNonNestedReferencesPresent =
|
|
||||||
multipleSourceParametersForTP || !groupedSourceReferences.nonNested.isEmpty();
|
|
||||||
// If there are multiple source parameters that are mapped to the target reference
|
// If there are multiple source parameters that are mapped to the target reference
|
||||||
// then we restrict the mapping only to the defined mappings. And we create MappingOptions
|
// then we restrict the mapping only to the defined mappings. And we create MappingOptions
|
||||||
// for forged methods (which means that any unmapped target properties are ignored)
|
// for forged methods (which means that any unmapped target properties are ignored)
|
||||||
|
@ -5,11 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.model;
|
package org.mapstruct.ap.internal.model;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.lang.model.element.ElementKind;
|
import javax.lang.model.element.ElementKind;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
|
||||||
@ -26,6 +23,8 @@ import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
|||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
|
|
||||||
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sjaak Derksen
|
* @author Sjaak Derksen
|
||||||
@ -39,7 +38,6 @@ public class ObjectFactoryMethodResolver {
|
|||||||
* returns a no arg factory method
|
* returns a no arg factory method
|
||||||
*
|
*
|
||||||
* @param method target mapping method
|
* @param method target mapping method
|
||||||
* @param targetType return type to match
|
|
||||||
* @param selectionParameters parameters used in the selection process
|
* @param selectionParameters parameters used in the selection process
|
||||||
* @param ctx
|
* @param ctx
|
||||||
*
|
*
|
||||||
@ -47,30 +45,49 @@ public class ObjectFactoryMethodResolver {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static MethodReference getFactoryMethod( Method method,
|
public static MethodReference getFactoryMethod( Method method,
|
||||||
Type targetType,
|
|
||||||
SelectionParameters selectionParameters,
|
SelectionParameters selectionParameters,
|
||||||
MappingBuilderContext ctx) {
|
MappingBuilderContext ctx) {
|
||||||
|
return getFactoryMethod( method, method.getResultType(), selectionParameters, ctx );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a no arg factory method
|
||||||
|
*
|
||||||
|
* @param method target mapping method
|
||||||
|
* @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract
|
||||||
|
* @param selectionParameters parameters used in the selection process
|
||||||
|
* @param ctx
|
||||||
|
*
|
||||||
|
* @return a method reference to the factory method, or null if no suitable, or ambiguous method found
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static MethodReference getFactoryMethod( Method method,
|
||||||
|
Type alternativeTarget,
|
||||||
|
SelectionParameters selectionParameters,
|
||||||
|
MappingBuilderContext ctx) {
|
||||||
|
|
||||||
MethodSelectors selectors =
|
MethodSelectors selectors =
|
||||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory() );
|
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() );
|
||||||
|
|
||||||
List<SelectedMethod<SourceMethod>> matchingFactoryMethods =
|
List<SelectedMethod<SourceMethod>> matchingFactoryMethods =
|
||||||
selectors.getMatchingMethods(
|
selectors.getMatchingMethods(
|
||||||
method,
|
method,
|
||||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||||
java.util.Collections.<Type> emptyList(),
|
java.util.Collections.<Type> emptyList(),
|
||||||
targetType.getEffectiveType(),
|
alternativeTarget,
|
||||||
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
||||||
|
|
||||||
if (matchingFactoryMethods.isEmpty()) {
|
if (matchingFactoryMethods.isEmpty()) {
|
||||||
return findBuilderFactoryMethod( targetType );
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( matchingFactoryMethods.size() > 1 ) {
|
if ( matchingFactoryMethods.size() > 1 ) {
|
||||||
ctx.getMessager().printMessage(
|
ctx.getMessager().printMessage(
|
||||||
method.getExecutable(),
|
method.getExecutable(),
|
||||||
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
||||||
targetType.getEffectiveType(),
|
alternativeTarget,
|
||||||
Strings.join( matchingFactoryMethods, ", " ) );
|
Strings.join( matchingFactoryMethods, ", " ) );
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -99,8 +116,7 @@ public class ObjectFactoryMethodResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodReference findBuilderFactoryMethod(Type targetType) {
|
public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder) {
|
||||||
BuilderType builder = targetType.getBuilderType();
|
|
||||||
if ( builder == null ) {
|
if ( builder == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -111,7 +127,7 @@ public class ObjectFactoryMethodResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !builder.getBuildingType().isAssignableTo( targetType ) ) {
|
if ( !builder.getBuildingType().isAssignableTo( method.getReturnType() ) ) {
|
||||||
//TODO print error message
|
//TODO print error message
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.type.DeclaredType;
|
|
||||||
|
|
||||||
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;
|
||||||
@ -22,6 +21,7 @@ import org.mapstruct.ap.internal.model.assignment.SetterWrapper;
|
|||||||
import org.mapstruct.ap.internal.model.assignment.StreamAdderWrapper;
|
import org.mapstruct.ap.internal.model.assignment.StreamAdderWrapper;
|
||||||
import org.mapstruct.ap.internal.model.assignment.UpdateWrapper;
|
import org.mapstruct.ap.internal.model.assignment.UpdateWrapper;
|
||||||
import org.mapstruct.ap.internal.model.common.Assignment;
|
import org.mapstruct.ap.internal.model.common.Assignment;
|
||||||
|
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||||
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
||||||
import org.mapstruct.ap.internal.model.common.ModelElement;
|
import org.mapstruct.ap.internal.model.common.ModelElement;
|
||||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
@ -36,17 +36,17 @@ import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods;
|
|||||||
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
import org.mapstruct.ap.internal.model.source.PropertyEntry;
|
||||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||||
import org.mapstruct.ap.internal.model.source.SourceReference;
|
import org.mapstruct.ap.internal.model.source.SourceReference;
|
||||||
|
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||||
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
|
||||||
import org.mapstruct.ap.internal.util.Executables;
|
|
||||||
import org.mapstruct.ap.internal.util.MapperConfiguration;
|
import org.mapstruct.ap.internal.util.MapperConfiguration;
|
||||||
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;
|
||||||
import org.mapstruct.ap.internal.util.ValueProvider;
|
import org.mapstruct.ap.internal.util.ValueProvider;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
|
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||||
|
|
||||||
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.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
|
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
|
||||||
@ -72,38 +72,13 @@ public class PropertyMapping extends ModelElement {
|
|||||||
private final List<String> dependsOn;
|
private final List<String> dependsOn;
|
||||||
private final Assignment defaultValueAssignment;
|
private final Assignment defaultValueAssignment;
|
||||||
|
|
||||||
public enum TargetWriteAccessorType {
|
|
||||||
FIELD,
|
|
||||||
GETTER,
|
|
||||||
SETTER,
|
|
||||||
ADDER;
|
|
||||||
|
|
||||||
public static TargetWriteAccessorType of(AccessorNamingUtils accessorNaming, Accessor accessor) {
|
|
||||||
if ( accessorNaming.isSetterMethod( accessor ) ) {
|
|
||||||
return TargetWriteAccessorType.SETTER;
|
|
||||||
}
|
|
||||||
else if ( accessorNaming.isAdderMethod( accessor ) ) {
|
|
||||||
return TargetWriteAccessorType.ADDER;
|
|
||||||
}
|
|
||||||
else if ( accessorNaming.isGetterMethod( accessor ) ) {
|
|
||||||
return TargetWriteAccessorType.GETTER;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return TargetWriteAccessorType.FIELD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isFieldAssignment(TargetWriteAccessorType accessorType) {
|
|
||||||
return accessorType == FIELD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static class MappingBuilderBase<T extends MappingBuilderBase<T>> extends AbstractBaseBuilder<T> {
|
private static class MappingBuilderBase<T extends MappingBuilderBase<T>> extends AbstractBaseBuilder<T> {
|
||||||
|
|
||||||
protected Accessor targetWriteAccessor;
|
protected Accessor targetWriteAccessor;
|
||||||
protected TargetWriteAccessorType targetWriteAccessorType;
|
protected AccessorType targetWriteAccessorType;
|
||||||
protected Type targetType;
|
protected Type targetType;
|
||||||
|
protected BuilderType targetBuilderType;
|
||||||
protected Accessor targetReadAccessor;
|
protected Accessor targetReadAccessor;
|
||||||
protected String targetPropertyName;
|
protected String targetPropertyName;
|
||||||
protected String sourcePropertyName;
|
protected String sourcePropertyName;
|
||||||
@ -124,7 +99,8 @@ public class PropertyMapping extends ModelElement {
|
|||||||
this.targetReadAccessor = targetProp.getReadAccessor();
|
this.targetReadAccessor = targetProp.getReadAccessor();
|
||||||
this.targetWriteAccessor = targetProp.getWriteAccessor();
|
this.targetWriteAccessor = targetProp.getWriteAccessor();
|
||||||
this.targetType = targetProp.getType();
|
this.targetType = targetProp.getType();
|
||||||
this.targetWriteAccessorType = TargetWriteAccessorType.of( ctx.getAccessorNaming(), targetWriteAccessor );
|
this.targetBuilderType = targetProp.getBuilderType();
|
||||||
|
this.targetWriteAccessorType = targetWriteAccessor.getAccessorType();
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,8 +111,10 @@ public class PropertyMapping extends ModelElement {
|
|||||||
|
|
||||||
public T targetWriteAccessor(Accessor targetWriteAccessor) {
|
public T targetWriteAccessor(Accessor targetWriteAccessor) {
|
||||||
this.targetWriteAccessor = targetWriteAccessor;
|
this.targetWriteAccessor = targetWriteAccessor;
|
||||||
this.targetWriteAccessorType = TargetWriteAccessorType.of( ctx.getAccessorNaming(), targetWriteAccessor );
|
this.targetType = ctx.getTypeFactory().getType( targetWriteAccessor.getAccessedType() );
|
||||||
this.targetType = determineTargetType();
|
BuilderPrism builderPrism = BeanMapping.builderPrismFor( method );
|
||||||
|
this.targetBuilderType = ctx.getTypeFactory().builderTypeFor( this.targetType, builderPrism );
|
||||||
|
this.targetWriteAccessorType = targetWriteAccessor.getAccessorType();
|
||||||
|
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
@ -146,28 +124,6 @@ public class PropertyMapping extends ModelElement {
|
|||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type determineTargetType() {
|
|
||||||
// This is a bean mapping method, so we know the result is a declared type
|
|
||||||
Type mappingType = method.getResultType();
|
|
||||||
if ( !method.isUpdateMethod() ) {
|
|
||||||
mappingType = mappingType.getEffectiveType();
|
|
||||||
}
|
|
||||||
DeclaredType resultType = (DeclaredType) mappingType.getTypeMirror();
|
|
||||||
|
|
||||||
switch ( targetWriteAccessorType ) {
|
|
||||||
case ADDER:
|
|
||||||
case SETTER:
|
|
||||||
return ctx.getTypeFactory()
|
|
||||||
.getSingleParameter( resultType, targetWriteAccessor )
|
|
||||||
.getType();
|
|
||||||
case GETTER:
|
|
||||||
case FIELD:
|
|
||||||
default:
|
|
||||||
return ctx.getTypeFactory()
|
|
||||||
.getReturnType( resultType, targetWriteAccessor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T targetPropertyName(String targetPropertyName) {
|
public T targetPropertyName(String targetPropertyName) {
|
||||||
this.targetPropertyName = targetPropertyName;
|
this.targetPropertyName = targetPropertyName;
|
||||||
return (T) this;
|
return (T) this;
|
||||||
@ -189,7 +145,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isFieldAssignment() {
|
protected boolean isFieldAssignment() {
|
||||||
return targetWriteAccessorType == TargetWriteAccessorType.FIELD;
|
return targetWriteAccessorType == AccessorType.FIELD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,11 +252,11 @@ public class PropertyMapping extends ModelElement {
|
|||||||
// handle source
|
// handle source
|
||||||
this.rightHandSide = getSourceRHS( sourceReference );
|
this.rightHandSide = getSourceRHS( sourceReference );
|
||||||
rightHandSide.setUseElementAsSourceTypeForMatching(
|
rightHandSide.setUseElementAsSourceTypeForMatching(
|
||||||
targetWriteAccessorType == TargetWriteAccessorType.ADDER );
|
targetWriteAccessorType == AccessorType.ADDER );
|
||||||
|
|
||||||
// all the tricky cases will be excluded for the time being.
|
// all the tricky cases will be excluded for the time being.
|
||||||
boolean preferUpdateMethods;
|
boolean preferUpdateMethods;
|
||||||
if ( targetWriteAccessorType == TargetWriteAccessorType.ADDER ) {
|
if ( targetWriteAccessorType == AccessorType.ADDER ) {
|
||||||
preferUpdateMethods = false;
|
preferUpdateMethods = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -433,13 +389,12 @@ public class PropertyMapping extends ModelElement {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Assignment assignToPlain(Type targetType, TargetWriteAccessorType targetAccessorType,
|
private Assignment assignToPlain(Type targetType, AccessorType targetAccessorType,
|
||||||
Assignment rightHandSide) {
|
Assignment rightHandSide) {
|
||||||
|
|
||||||
Assignment result;
|
Assignment result;
|
||||||
|
|
||||||
if ( targetAccessorType == TargetWriteAccessorType.SETTER ||
|
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) {
|
||||||
targetAccessorType == TargetWriteAccessorType.FIELD ) {
|
|
||||||
result = assignToPlainViaSetter( targetType, rightHandSide );
|
result = assignToPlainViaSetter( targetType, rightHandSide );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -517,7 +472,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Assignment assignToCollection(Type targetType, TargetWriteAccessorType targetAccessorType,
|
private Assignment assignToCollection(Type targetType, AccessorType targetAccessorType,
|
||||||
Assignment rhs) {
|
Assignment rhs) {
|
||||||
return new CollectionAssignmentBuilder()
|
return new CollectionAssignmentBuilder()
|
||||||
.mappingBuilderContext( ctx )
|
.mappingBuilderContext( ctx )
|
||||||
@ -763,7 +718,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
forgeMethodWithMappingOptions,
|
forgeMethodWithMappingOptions,
|
||||||
forgedNamedBased
|
forgedNamedBased
|
||||||
);
|
);
|
||||||
return createForgedAssignment( sourceRHS, forgedMethod );
|
return createForgedAssignment( sourceRHS, targetBuilderType, forgedMethod );
|
||||||
}
|
}
|
||||||
|
|
||||||
private ForgedMethodHistory getForgedMethodHistory(SourceRHS sourceRHS) {
|
private ForgedMethodHistory getForgedMethodHistory(SourceRHS sourceRHS) {
|
||||||
@ -876,8 +831,8 @@ public class PropertyMapping extends ModelElement {
|
|||||||
|
|
||||||
if ( assignment != null ) {
|
if ( assignment != null ) {
|
||||||
|
|
||||||
if ( ctx.getAccessorNaming().isSetterMethod( targetWriteAccessor ) ||
|
if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER ||
|
||||||
Executables.isFieldAccessor( targetWriteAccessor ) ) {
|
targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) {
|
||||||
|
|
||||||
// target accessor is setter, so decorate assignment as setter
|
// target accessor is setter, so decorate assignment as setter
|
||||||
if ( assignment.isCallingUpdateMethod() ) {
|
if ( assignment.isCallingUpdateMethod() ) {
|
||||||
@ -992,8 +947,8 @@ public class PropertyMapping extends ModelElement {
|
|||||||
public PropertyMapping build() {
|
public PropertyMapping build() {
|
||||||
Assignment assignment = new SourceRHS( javaExpression, null, existingVariableNames, "" );
|
Assignment assignment = new SourceRHS( javaExpression, null, existingVariableNames, "" );
|
||||||
|
|
||||||
if ( ctx.getAccessorNaming().isSetterMethod( targetWriteAccessor ) ||
|
if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER ||
|
||||||
Executables.isFieldAccessor( targetWriteAccessor ) ) {
|
targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) {
|
||||||
// setter, so wrap in setter
|
// setter, so wrap in setter
|
||||||
assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() );
|
assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() );
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package org.mapstruct.ap.internal.model.common;
|
package org.mapstruct.ap.internal.model.common;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import javax.lang.model.element.ElementKind;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
@ -82,13 +83,6 @@ public class BuilderType {
|
|||||||
return buildMethods;
|
return buildMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuilderInfo asBuilderInfo() {
|
|
||||||
return new BuilderInfo.Builder()
|
|
||||||
.builderCreationMethod( this.builderCreationMethod )
|
|
||||||
.buildMethod( this.buildMethods )
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BuilderType create(BuilderInfo builderInfo, Type typeToBuild, TypeFactory typeFactory,
|
public static BuilderType create(BuilderInfo builderInfo, Type typeToBuild, TypeFactory typeFactory,
|
||||||
Types typeUtils) {
|
Types typeUtils) {
|
||||||
if ( builderInfo == null ) {
|
if ( builderInfo == null ) {
|
||||||
@ -109,6 +103,11 @@ public class BuilderType {
|
|||||||
owner = typeFactory.getType( builderCreationOwner );
|
owner = typeFactory.getType( builderCreationOwner );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the builderCreationMethod is constructor, its return type is Void. In this case the
|
||||||
|
// builder type should be the owner type.
|
||||||
|
if (builderInfo.getBuilderCreationMethod().getKind() == ElementKind.CONSTRUCTOR) {
|
||||||
|
builder = owner;
|
||||||
|
}
|
||||||
|
|
||||||
return new BuilderType(
|
return new BuilderType(
|
||||||
builder,
|
builder,
|
||||||
|
@ -34,12 +34,12 @@ import javax.lang.model.util.Types;
|
|||||||
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
||||||
import org.mapstruct.ap.internal.util.Executables;
|
import org.mapstruct.ap.internal.util.Executables;
|
||||||
|
import org.mapstruct.ap.internal.util.Fields;
|
||||||
import org.mapstruct.ap.internal.util.Filters;
|
import org.mapstruct.ap.internal.util.Filters;
|
||||||
import org.mapstruct.ap.internal.util.JavaStreamConstants;
|
import org.mapstruct.ap.internal.util.JavaStreamConstants;
|
||||||
import org.mapstruct.ap.internal.util.Nouns;
|
import org.mapstruct.ap.internal.util.Nouns;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||||
import org.mapstruct.ap.spi.BuilderInfo;
|
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
import org.mapstruct.ap.internal.util.NativeTypes;
|
import org.mapstruct.ap.internal.util.NativeTypes;
|
||||||
@ -67,7 +67,6 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
private final ImplementationType implementationType;
|
private final ImplementationType implementationType;
|
||||||
private final Type componentType;
|
private final Type componentType;
|
||||||
private final BuilderType builderType;
|
|
||||||
|
|
||||||
private final String packageName;
|
private final String packageName;
|
||||||
private final String name;
|
private final String name;
|
||||||
@ -89,9 +88,11 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
private Boolean isToBeImported;
|
private Boolean isToBeImported;
|
||||||
|
|
||||||
private Map<String, Accessor> readAccessors = null;
|
private Map<String, Accessor> readAccessors = null;
|
||||||
private Map<String, ExecutableElementAccessor> presenceCheckers = null;
|
private Map<String, Accessor> presenceCheckers = null;
|
||||||
|
|
||||||
|
private List<ExecutableElement> allMethods = null;
|
||||||
|
private List<VariableElement> allFields = null;
|
||||||
|
|
||||||
private List<Accessor> allAccessors = null;
|
|
||||||
private List<Accessor> setters = null;
|
private List<Accessor> setters = null;
|
||||||
private List<Accessor> adders = null;
|
private List<Accessor> adders = null;
|
||||||
private List<Accessor> alternativeTargetAccessors = null;
|
private List<Accessor> alternativeTargetAccessors = null;
|
||||||
@ -100,12 +101,13 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
private Boolean hasEmptyAccessibleContructor;
|
private Boolean hasEmptyAccessibleContructor;
|
||||||
|
|
||||||
|
private final Filters filters;
|
||||||
|
|
||||||
//CHECKSTYLE:OFF
|
//CHECKSTYLE:OFF
|
||||||
public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory,
|
public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory,
|
||||||
AccessorNamingUtils accessorNaming,
|
AccessorNamingUtils accessorNaming,
|
||||||
TypeMirror typeMirror, TypeElement typeElement,
|
TypeMirror typeMirror, TypeElement typeElement,
|
||||||
List<Type> typeParameters, ImplementationType implementationType, Type componentType,
|
List<Type> typeParameters, ImplementationType implementationType, Type componentType,
|
||||||
BuilderInfo builderInfo,
|
|
||||||
String packageName, String name, String qualifiedName,
|
String packageName, String name, String qualifiedName,
|
||||||
boolean isInterface, boolean isEnumType, boolean isIterableType,
|
boolean isInterface, boolean isEnumType, boolean isIterableType,
|
||||||
boolean isCollectionType, boolean isMapType, boolean isStreamType,
|
boolean isCollectionType, boolean isMapType, boolean isStreamType,
|
||||||
@ -157,7 +159,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
this.isToBeImported = isToBeImported;
|
this.isToBeImported = isToBeImported;
|
||||||
this.toBeImportedTypes = toBeImportedTypes;
|
this.toBeImportedTypes = toBeImportedTypes;
|
||||||
this.notToBeImportedTypes = notToBeImportedTypes;
|
this.notToBeImportedTypes = notToBeImportedTypes;
|
||||||
this.builderType = BuilderType.create( builderInfo, this, this.typeFactory, this.typeUtils );
|
this.filters = new Filters( accessorNaming, typeUtils, typeMirror );
|
||||||
}
|
}
|
||||||
//CHECKSTYLE:ON
|
//CHECKSTYLE:ON
|
||||||
|
|
||||||
@ -200,18 +202,6 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
return componentType;
|
return componentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuilderType getBuilderType() {
|
|
||||||
return builderType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The effective type that should be used when searching for getters / setters, creating new types etc
|
|
||||||
* @return the effective type for mappings
|
|
||||||
*/
|
|
||||||
public Type getEffectiveType() {
|
|
||||||
return builderType != null ? builderType.getBuilder() : this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPrimitive() {
|
public boolean isPrimitive() {
|
||||||
return typeMirror.getKind().isPrimitive();
|
return typeMirror.getKind().isPrimitive();
|
||||||
}
|
}
|
||||||
@ -419,7 +409,6 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
typeParameters,
|
typeParameters,
|
||||||
implementationType,
|
implementationType,
|
||||||
componentType,
|
componentType,
|
||||||
builderType == null ? null : builderType.asBuilderInfo(),
|
|
||||||
packageName,
|
packageName,
|
||||||
name,
|
name,
|
||||||
qualifiedName,
|
qualifiedName,
|
||||||
@ -462,7 +451,6 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
bounds,
|
bounds,
|
||||||
implementationType,
|
implementationType,
|
||||||
componentType,
|
componentType,
|
||||||
builderType == null ? null : builderType.asBuilderInfo(),
|
|
||||||
packageName,
|
packageName,
|
||||||
name,
|
name,
|
||||||
qualifiedName,
|
qualifiedName,
|
||||||
@ -505,30 +493,30 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
*/
|
*/
|
||||||
public Map<String, Accessor> getPropertyReadAccessors() {
|
public Map<String, Accessor> getPropertyReadAccessors() {
|
||||||
if ( readAccessors == null ) {
|
if ( readAccessors == null ) {
|
||||||
List<Accessor> getterList = Filters.getterMethodsIn( accessorNaming, getAllAccessors() );
|
List<Accessor> getterList = filters.getterMethodsIn( getAllMethods() );
|
||||||
Map<String, Accessor> modifiableGetters = new LinkedHashMap<>();
|
Map<String, Accessor> modifiableGetters = new LinkedHashMap<>();
|
||||||
for ( Accessor getter : getterList ) {
|
for ( Accessor getter : getterList ) {
|
||||||
String propertyName = accessorNaming.getPropertyName( getter );
|
String propertyName = getPropertyName( getter );
|
||||||
if ( modifiableGetters.containsKey( propertyName ) ) {
|
if ( modifiableGetters.containsKey( propertyName ) ) {
|
||||||
// In the DefaultAccessorNamingStrategy, this can only be the case for Booleans: isFoo() and
|
// In the DefaultAccessorNamingStrategy, this can only be the case for Booleans: isFoo() and
|
||||||
// getFoo(); The latter is preferred.
|
// getFoo(); The latter is preferred.
|
||||||
if ( !getter.getSimpleName().toString().startsWith( "is" ) ) {
|
if ( !getter.getSimpleName().toString().startsWith( "is" ) ) {
|
||||||
modifiableGetters.put( accessorNaming.getPropertyName( getter ), getter );
|
modifiableGetters.put( getPropertyName( getter ), getter );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
modifiableGetters.put( accessorNaming.getPropertyName( getter ), getter );
|
modifiableGetters.put( getPropertyName( getter ), getter );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Accessor> fieldsList = Filters.fieldsIn( getAllAccessors() );
|
List<Accessor> fieldsList = filters.fieldsIn( getAllFields() );
|
||||||
for ( Accessor field : fieldsList ) {
|
for ( Accessor field : fieldsList ) {
|
||||||
String propertyName = accessorNaming.getPropertyName( field );
|
String propertyName = getPropertyName( field );
|
||||||
if ( !modifiableGetters.containsKey( propertyName ) ) {
|
if ( !modifiableGetters.containsKey( propertyName ) ) {
|
||||||
// If there was no getter or is method for booleans, then resort to the field.
|
// If there was no getter or is method for booleans, then resort to the field.
|
||||||
// If a field was already added do not add it again.
|
// If a field was already added do not add it again.
|
||||||
modifiableGetters.put( propertyName, field );
|
modifiableGetters.put( propertyName, field );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
readAccessors = Collections.unmodifiableMap( modifiableGetters );
|
readAccessors = Collections.unmodifiableMap( modifiableGetters );
|
||||||
@ -541,15 +529,12 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
*
|
*
|
||||||
* @return an unmodifiable map of all presence checkers, indexed by property name
|
* @return an unmodifiable map of all presence checkers, indexed by property name
|
||||||
*/
|
*/
|
||||||
public Map<String, ExecutableElementAccessor> getPropertyPresenceCheckers() {
|
public Map<String, Accessor> getPropertyPresenceCheckers() {
|
||||||
if ( presenceCheckers == null ) {
|
if ( presenceCheckers == null ) {
|
||||||
List<ExecutableElementAccessor> checkerList = Filters.presenceCheckMethodsIn(
|
List<Accessor> checkerList = filters.presenceCheckMethodsIn( getAllMethods() );
|
||||||
accessorNaming,
|
Map<String, Accessor> modifiableCheckers = new LinkedHashMap<>();
|
||||||
getAllAccessors()
|
for ( Accessor checker : checkerList ) {
|
||||||
);
|
modifiableCheckers.put( getPropertyName( checker ), checker );
|
||||||
Map<String, ExecutableElementAccessor> modifiableCheckers = new LinkedHashMap<>();
|
|
||||||
for ( ExecutableElementAccessor checker : checkerList ) {
|
|
||||||
modifiableCheckers.put( accessorNaming.getPropertyName( checker ), checker );
|
|
||||||
}
|
}
|
||||||
presenceCheckers = Collections.unmodifiableMap( modifiableCheckers );
|
presenceCheckers = Collections.unmodifiableMap( modifiableCheckers );
|
||||||
}
|
}
|
||||||
@ -577,7 +562,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
Map<String, Accessor> result = new LinkedHashMap<>();
|
Map<String, Accessor> result = new LinkedHashMap<>();
|
||||||
|
|
||||||
for ( Accessor candidate : candidates ) {
|
for ( Accessor candidate : candidates ) {
|
||||||
String targetPropertyName = accessorNaming.getPropertyName( candidate );
|
String targetPropertyName = getPropertyName( candidate );
|
||||||
|
|
||||||
Accessor readAccessor = getPropertyReadAccessors().get( targetPropertyName );
|
Accessor readAccessor = getPropertyReadAccessors().get( targetPropertyName );
|
||||||
|
|
||||||
@ -593,12 +578,12 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
// first check if there's a setter method.
|
// first check if there's a setter method.
|
||||||
Accessor adderMethod = null;
|
Accessor adderMethod = null;
|
||||||
if ( accessorNaming.isSetterMethod( candidate )
|
if ( candidate.getAccessorType() == AccessorType.SETTER
|
||||||
// ok, the current accessor is a setter. So now the strategy determines what to use
|
// ok, the current accessor is a setter. So now the strategy determines what to use
|
||||||
&& cmStrategy == CollectionMappingStrategyPrism.ADDER_PREFERRED ) {
|
&& cmStrategy == CollectionMappingStrategyPrism.ADDER_PREFERRED ) {
|
||||||
adderMethod = getAdderForType( targetType, targetPropertyName );
|
adderMethod = getAdderForType( targetType, targetPropertyName );
|
||||||
}
|
}
|
||||||
else if ( accessorNaming.isGetterMethod( candidate ) ) {
|
else if ( candidate.getAccessorType() == AccessorType.GETTER ) {
|
||||||
// the current accessor is a getter (no setter available). But still, an add method is according
|
// the current accessor is a getter (no setter available). But still, an add method is according
|
||||||
// to the above strategy (SETTER_PREFERRED || ADDER_PREFERRED) preferred over the getter.
|
// to the above strategy (SETTER_PREFERRED || ADDER_PREFERRED) preferred over the getter.
|
||||||
adderMethod = getAdderForType( targetType, targetPropertyName );
|
adderMethod = getAdderForType( targetType, targetPropertyName );
|
||||||
@ -608,7 +593,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
candidate = adderMethod;
|
candidate = adderMethod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( Executables.isFieldAccessor( candidate ) && ( Executables.isFinal( candidate ) ||
|
else if ( candidate.getAccessorType() == AccessorType.FIELD && ( Executables.isFinal( candidate ) ||
|
||||||
result.containsKey( targetPropertyName ) ) ) {
|
result.containsKey( targetPropertyName ) ) ) {
|
||||||
// if the candidate is a field and a mapping already exists, then use that one, skip it.
|
// if the candidate is a field and a mapping already exists, then use that one, skip it.
|
||||||
continue;
|
continue;
|
||||||
@ -636,18 +621,36 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
if ( parameter != null ) {
|
if ( parameter != null ) {
|
||||||
return parameter.getType();
|
return parameter.getType();
|
||||||
}
|
}
|
||||||
else if ( accessorNaming.isGetterMethod( candidate ) || Executables.isFieldAccessor( candidate ) ) {
|
else if ( candidate.getAccessorType() == AccessorType.GETTER ||
|
||||||
|
candidate.getAccessorType() == AccessorType.FIELD ) {
|
||||||
return typeFactory.getReturnType( (DeclaredType) typeMirror, candidate );
|
return typeFactory.getReturnType( (DeclaredType) typeMirror, candidate );
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Accessor> getAllAccessors() {
|
private List<ExecutableElement> getAllMethods() {
|
||||||
if ( allAccessors == null ) {
|
if ( allMethods == null ) {
|
||||||
allAccessors = Executables.getAllEnclosedAccessors( elementUtils, typeElement );
|
allMethods = Executables.getAllEnclosedExecutableElements( elementUtils, typeElement );
|
||||||
}
|
}
|
||||||
|
|
||||||
return allAccessors;
|
return allMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<VariableElement> getAllFields() {
|
||||||
|
if ( allFields == null ) {
|
||||||
|
allFields = Fields.getAllEnclosedFields( elementUtils, typeElement );
|
||||||
|
}
|
||||||
|
|
||||||
|
return allFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPropertyName(Accessor accessor ) {
|
||||||
|
if ( accessor.getAccessorType() == AccessorType.FIELD ) {
|
||||||
|
return accessorNaming.getPropertyName( (VariableElement) accessor.getElement() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return accessorNaming.getPropertyName( (ExecutableElement) accessor.getElement() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -709,19 +712,14 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
* @return accessor candidates
|
* @return accessor candidates
|
||||||
*/
|
*/
|
||||||
private List<Accessor> getAccessorCandidates(Type property, Class<?> superclass) {
|
private List<Accessor> getAccessorCandidates(Type property, Class<?> superclass) {
|
||||||
TypeMirror typeArg = first( property.determineTypeArguments( superclass ) ).getTypeBound()
|
TypeMirror typeArg = first( property.determineTypeArguments( superclass ) ).getTypeBound().getTypeMirror();
|
||||||
.getTypeMirror();
|
|
||||||
// now, look for a method that
|
// now, look for a method that
|
||||||
// 1) starts with add,
|
// 1) starts with add,
|
||||||
// 2) and has typeArg as one and only arg
|
// 2) and has typeArg as one and only arg
|
||||||
List<Accessor> adderList = getAdders();
|
List<Accessor> adderList = getAdders();
|
||||||
List<Accessor> candidateList = new ArrayList<>();
|
List<Accessor> candidateList = new ArrayList<>();
|
||||||
for ( Accessor adder : adderList ) {
|
for ( Accessor adder : adderList ) {
|
||||||
ExecutableElement executable = adder.getExecutable();
|
ExecutableElement executable = (ExecutableElement) adder.getElement();
|
||||||
if ( executable == null ) {
|
|
||||||
// it should not be null, but to be safe
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
VariableElement arg = executable.getParameters().get( 0 );
|
VariableElement arg = executable.getParameters().get( 0 );
|
||||||
if ( typeUtils.isSameType( boxed( arg.asType() ), boxed( typeArg ) ) ) {
|
if ( typeUtils.isSameType( boxed( arg.asType() ), boxed( typeArg ) ) ) {
|
||||||
candidateList.add( adder );
|
candidateList.add( adder );
|
||||||
@ -746,7 +744,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
*/
|
*/
|
||||||
private List<Accessor> getSetters() {
|
private List<Accessor> getSetters() {
|
||||||
if ( setters == null ) {
|
if ( setters == null ) {
|
||||||
setters = Collections.unmodifiableList( Filters.setterMethodsIn( accessorNaming, getAllAccessors() ) );
|
setters = Collections.unmodifiableList( filters.setterMethodsIn( getAllMethods() ) );
|
||||||
}
|
}
|
||||||
return setters;
|
return setters;
|
||||||
}
|
}
|
||||||
@ -761,7 +759,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
*/
|
*/
|
||||||
private List<Accessor> getAdders() {
|
private List<Accessor> getAdders() {
|
||||||
if ( adders == null ) {
|
if ( adders == null ) {
|
||||||
adders = Collections.unmodifiableList( Filters.adderMethodsIn( accessorNaming, getAllAccessors() ) );
|
adders = Collections.unmodifiableList( filters.adderMethodsIn( getAllMethods() ) );
|
||||||
}
|
}
|
||||||
return adders;
|
return adders;
|
||||||
}
|
}
|
||||||
@ -781,10 +779,9 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
List<Accessor> result = new ArrayList<>();
|
List<Accessor> result = new ArrayList<>();
|
||||||
List<Accessor> setterMethods = getSetters();
|
List<Accessor> setterMethods = getSetters();
|
||||||
List<Accessor> readAccessors =
|
List<Accessor> readAccessors = new ArrayList<>( getPropertyReadAccessors().values() );
|
||||||
new ArrayList<>( getPropertyReadAccessors().values() );
|
|
||||||
// All the fields are also alternative accessors
|
// All the fields are also alternative accessors
|
||||||
readAccessors.addAll( Filters.fieldsIn( getAllAccessors() ) );
|
readAccessors.addAll( filters.fieldsIn( getAllFields() ) );
|
||||||
|
|
||||||
// there could be a read accessor (field or method) for a list/map that is not present as setter.
|
// there could be a read accessor (field or method) for a list/map that is not present as setter.
|
||||||
// an accessor could substitute the setter in that case and act as setter.
|
// an accessor could substitute the setter in that case and act as setter.
|
||||||
@ -794,7 +791,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
!correspondingSetterMethodExists( readAccessor, setterMethods ) ) {
|
!correspondingSetterMethodExists( readAccessor, setterMethods ) ) {
|
||||||
result.add( readAccessor );
|
result.add( readAccessor );
|
||||||
}
|
}
|
||||||
else if ( Executables.isFieldAccessor( readAccessor ) &&
|
else if ( readAccessor.getAccessorType() == AccessorType.FIELD &&
|
||||||
!correspondingSetterMethodExists( readAccessor, setterMethods ) ) {
|
!correspondingSetterMethodExists( readAccessor, setterMethods ) ) {
|
||||||
result.add( readAccessor );
|
result.add( readAccessor );
|
||||||
}
|
}
|
||||||
@ -807,10 +804,10 @@ public class Type extends ModelElement implements Comparable<Type> {
|
|||||||
|
|
||||||
private boolean correspondingSetterMethodExists(Accessor getterMethod,
|
private boolean correspondingSetterMethodExists(Accessor getterMethod,
|
||||||
List<Accessor> setterMethods) {
|
List<Accessor> setterMethods) {
|
||||||
String getterPropertyName = accessorNaming.getPropertyName( getterMethod );
|
String getterPropertyName = getPropertyName( getterMethod );
|
||||||
|
|
||||||
for ( Accessor setterMethod : setterMethods ) {
|
for ( Accessor setterMethod : setterMethods ) {
|
||||||
String setterPropertyName = accessorNaming.getPropertyName( setterMethod );
|
String setterPropertyName = getPropertyName( setterMethod );
|
||||||
if ( getterPropertyName.equals( setterPropertyName ) ) {
|
if ( getterPropertyName.equals( setterPropertyName ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import javax.lang.model.type.WildcardType;
|
|||||||
import javax.lang.model.util.Elements;
|
import javax.lang.model.util.Elements;
|
||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||||
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
|
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
|
||||||
import org.mapstruct.ap.internal.util.Collections;
|
import org.mapstruct.ap.internal.util.Collections;
|
||||||
import org.mapstruct.ap.internal.util.Extractor;
|
import org.mapstruct.ap.internal.util.Extractor;
|
||||||
@ -49,6 +50,7 @@ import org.mapstruct.ap.internal.util.NativeTypes;
|
|||||||
import org.mapstruct.ap.internal.util.RoundContext;
|
import org.mapstruct.ap.internal.util.RoundContext;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
|
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||||
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
|
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
|
||||||
import org.mapstruct.ap.spi.BuilderInfo;
|
import org.mapstruct.ap.spi.BuilderInfo;
|
||||||
import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException;
|
import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException;
|
||||||
@ -186,7 +188,6 @@ public class TypeFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImplementationType implementationType = getImplementationType( mirror );
|
ImplementationType implementationType = getImplementationType( mirror );
|
||||||
BuilderInfo builderInfo = findBuilder( mirror );
|
|
||||||
|
|
||||||
boolean isIterableType = typeUtils.isSubtype( mirror, iterableType );
|
boolean isIterableType = typeUtils.isSubtype( mirror, iterableType );
|
||||||
boolean isCollectionType = typeUtils.isSubtype( mirror, collectionType );
|
boolean isCollectionType = typeUtils.isSubtype( mirror, collectionType );
|
||||||
@ -280,7 +281,6 @@ public class TypeFactory {
|
|||||||
getTypeParameters( mirror, false ),
|
getTypeParameters( mirror, false ),
|
||||||
implementationType,
|
implementationType,
|
||||||
componentType,
|
componentType,
|
||||||
builderInfo,
|
|
||||||
packageName,
|
packageName,
|
||||||
name,
|
name,
|
||||||
qualifiedName,
|
qualifiedName,
|
||||||
@ -354,10 +354,10 @@ public class TypeFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Parameter getSingleParameter(DeclaredType includingType, Accessor method) {
|
public Parameter getSingleParameter(DeclaredType includingType, Accessor method) {
|
||||||
ExecutableElement executable = method.getExecutable();
|
if ( method.getAccessorType() == AccessorType.FIELD ) {
|
||||||
if ( executable == null ) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
ExecutableElement executable = (ExecutableElement) method.getElement();
|
||||||
List<? extends VariableElement> parameters = executable.getParameters();
|
List<? extends VariableElement> parameters = executable.getParameters();
|
||||||
|
|
||||||
if ( parameters.size() != 1 ) {
|
if ( parameters.size() != 1 ) {
|
||||||
@ -369,7 +369,7 @@ public class TypeFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Parameter> getParameters(DeclaredType includingType, Accessor accessor) {
|
public List<Parameter> getParameters(DeclaredType includingType, Accessor accessor) {
|
||||||
ExecutableElement method = accessor.getExecutable();
|
ExecutableElement method = (ExecutableElement) accessor.getElement();
|
||||||
TypeMirror methodType = getMethodType( includingType, accessor.getElement() );
|
TypeMirror methodType = getMethodType( includingType, accessor.getElement() );
|
||||||
if ( method == null || methodType.getKind() != TypeKind.EXECUTABLE ) {
|
if ( method == null || methodType.getKind() != TypeKind.EXECUTABLE ) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
@ -427,10 +427,10 @@ public class TypeFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Type> getThrownTypes(Accessor accessor) {
|
public List<Type> getThrownTypes(Accessor accessor) {
|
||||||
if (accessor.getExecutable() == null) {
|
if (accessor.getAccessorType() == AccessorType.FIELD) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return extractTypes( accessor.getExecutable().getThrownTypes() );
|
return extractTypes( ( (ExecutableElement) accessor.getElement() ).getThrownTypes() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Type> extractTypes(List<? extends TypeMirror> typeMirrors) {
|
private List<Type> extractTypes(List<? extends TypeMirror> typeMirrors) {
|
||||||
@ -502,7 +502,6 @@ public class TypeFactory {
|
|||||||
getTypeParameters( mirror, true ),
|
getTypeParameters( mirror, true ),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
implementationType.getPackageName(),
|
implementationType.getPackageName(),
|
||||||
implementationType.getName(),
|
implementationType.getName(),
|
||||||
implementationType.getFullyQualifiedName(),
|
implementationType.getFullyQualifiedName(),
|
||||||
@ -523,19 +522,24 @@ public class TypeFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BuilderInfo findBuilder(TypeMirror type) {
|
private BuilderInfo findBuilder(TypeMirror type, BuilderPrism builderPrism, boolean report) {
|
||||||
|
if ( builderPrism != null && builderPrism.disableBuilder() ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return roundContext.getAnnotationProcessorContext()
|
return roundContext.getAnnotationProcessorContext()
|
||||||
.getBuilderProvider()
|
.getBuilderProvider()
|
||||||
.findBuilderInfo( type );
|
.findBuilderInfo( type );
|
||||||
}
|
}
|
||||||
catch ( MoreThanOneBuilderCreationMethodException ex ) {
|
catch ( MoreThanOneBuilderCreationMethodException ex ) {
|
||||||
messager.printMessage(
|
if ( report ) {
|
||||||
typeUtils.asElement( type ),
|
messager.printMessage(
|
||||||
Message.BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD,
|
typeUtils.asElement( type ),
|
||||||
type,
|
Message.BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD,
|
||||||
Strings.join( ex.getBuilderInfo(), ", ", BUILDER_INFO_CREATION_METHOD_EXTRACTOR )
|
type,
|
||||||
);
|
Strings.join( ex.getBuilderInfo(), ", ", BUILDER_INFO_CREATION_METHOD_EXTRACTOR )
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -663,4 +667,21 @@ public class TypeFactory {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BuilderType builderTypeFor( Type type, BuilderPrism builderPrism ) {
|
||||||
|
if ( type != null ) {
|
||||||
|
BuilderInfo builderInfo = findBuilder( type.getTypeMirror(), builderPrism, true );
|
||||||
|
return BuilderType.create( builderInfo, type, this, this.typeUtils );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type effectiveResultTypeFor( Type type, BuilderPrism builderPrism ) {
|
||||||
|
if ( type != null ) {
|
||||||
|
BuilderInfo builderInfo = findBuilder( type.getTypeMirror(), builderPrism, false );
|
||||||
|
BuilderType builderType = BuilderType.create( builderInfo, type, this, this.typeUtils );
|
||||||
|
return builderType != null ? builderType.getBuilder() : type;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ public class BeanMapping {
|
|||||||
*/
|
*/
|
||||||
public static BeanMapping forInheritance( BeanMapping map ) {
|
public static BeanMapping forInheritance( BeanMapping map ) {
|
||||||
return new BeanMapping(
|
return new BeanMapping(
|
||||||
map.selectionParameters,
|
SelectionParameters.forInheritance( map.selectionParameters ),
|
||||||
map.nullValueMappingStrategy,
|
map.nullValueMappingStrategy,
|
||||||
map.nullValuePropertyMappingStrategy,
|
map.nullValuePropertyMappingStrategy,
|
||||||
map.nullValueCheckStrategy,
|
map.nullValueCheckStrategy,
|
||||||
@ -177,4 +177,21 @@ public class BeanMapping {
|
|||||||
public BuilderPrism getBuilder() {
|
public BuilderPrism getBuilder() {
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* derives the builder prism given the options and configuration
|
||||||
|
* @param method containing mandatory configuration and the mapping options (optionally containing a beanmapping)
|
||||||
|
* @return a BuilderPrism
|
||||||
|
*/
|
||||||
|
public static BuilderPrism builderPrismFor(Method method) {
|
||||||
|
BuilderPrism beanMappingBuilderPrism;
|
||||||
|
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
|
||||||
|
if ( beanMapping != null ) {
|
||||||
|
beanMappingBuilderPrism = beanMapping.builder;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
beanMappingBuilderPrism = null;
|
||||||
|
}
|
||||||
|
return method.getMapperConfiguration().getBuilderPrism( beanMappingBuilderPrism );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,10 @@ public class MappingOptions {
|
|||||||
CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy();
|
CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy();
|
||||||
Type writeType = method.getResultType();
|
Type writeType = method.getResultType();
|
||||||
if ( !method.isUpdateMethod() ) {
|
if ( !method.isUpdateMethod() ) {
|
||||||
writeType = writeType.getEffectiveType();
|
writeType = typeFactory.effectiveResultTypeFor(
|
||||||
|
writeType,
|
||||||
|
BeanMapping.builderPrismFor( method )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Map<String, Accessor> writeAccessors = writeType.getPropertyWriteAccessors( cms );
|
Map<String, Accessor> writeAccessors = writeType.getPropertyWriteAccessors( cms );
|
||||||
List<String> mappedPropertyNames = new ArrayList<>();
|
List<String> mappedPropertyNames = new ArrayList<>();
|
||||||
|
@ -129,7 +129,6 @@ public interface Method {
|
|||||||
*/
|
*/
|
||||||
Type getResultType();
|
Type getResultType();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return the names of the parameters of this mapping method
|
* @return the names of the parameters of this mapping method
|
||||||
@ -187,4 +186,13 @@ public interface Method {
|
|||||||
* @return the mapping options for this method
|
* @return the mapping options for this method
|
||||||
*/
|
*/
|
||||||
MappingOptions getMappingOptions();
|
MappingOptions getMappingOptions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return true when @MappingTarget annotated parameter is the same type as the return type. The method has
|
||||||
|
* to be an update method in order for this to be true.
|
||||||
|
*/
|
||||||
|
default boolean isMappingTargetAssignableToReturnType() {
|
||||||
|
return isUpdateMethod() ? getResultType().isAssignableTo( getReturnType() ) : false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ package org.mapstruct.ap.internal.model.source;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,8 +22,9 @@ public class PropertyEntry {
|
|||||||
private final String[] fullName;
|
private final String[] fullName;
|
||||||
private final Accessor readAccessor;
|
private final Accessor readAccessor;
|
||||||
private final Accessor writeAccessor;
|
private final Accessor writeAccessor;
|
||||||
private final ExecutableElementAccessor presenceChecker;
|
private final Accessor presenceChecker;
|
||||||
private final Type type;
|
private final Type type;
|
||||||
|
private final BuilderType builderType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor used to create {@link TargetReference} property entries from a mapping
|
* Constructor used to create {@link TargetReference} property entries from a mapping
|
||||||
@ -34,12 +35,13 @@ public class PropertyEntry {
|
|||||||
* @param type
|
* @param type
|
||||||
*/
|
*/
|
||||||
private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor writeAccessor,
|
private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor writeAccessor,
|
||||||
ExecutableElementAccessor presenceChecker, Type type) {
|
Accessor presenceChecker, Type type, BuilderType builderType) {
|
||||||
this.fullName = fullName;
|
this.fullName = fullName;
|
||||||
this.readAccessor = readAccessor;
|
this.readAccessor = readAccessor;
|
||||||
this.writeAccessor = writeAccessor;
|
this.writeAccessor = writeAccessor;
|
||||||
this.presenceChecker = presenceChecker;
|
this.presenceChecker = presenceChecker;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.builderType = builderType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,11 +51,12 @@ public class PropertyEntry {
|
|||||||
* @param readAccessor its read accessor
|
* @param readAccessor its read accessor
|
||||||
* @param writeAccessor its write accessor
|
* @param writeAccessor its write accessor
|
||||||
* @param type type of the property
|
* @param type type of the property
|
||||||
|
* @param builderType the builder for the type
|
||||||
* @return the property entry for given parameters.
|
* @return the property entry for given parameters.
|
||||||
*/
|
*/
|
||||||
public static PropertyEntry forTargetReference(String[] fullName, Accessor readAccessor,
|
public static PropertyEntry forTargetReference(String[] fullName, Accessor readAccessor,
|
||||||
Accessor writeAccessor, Type type) {
|
Accessor writeAccessor, Type type, BuilderType builderType) {
|
||||||
return new PropertyEntry( fullName, readAccessor, writeAccessor, null, type );
|
return new PropertyEntry( fullName, readAccessor, writeAccessor, null, type, builderType );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,8 +69,8 @@ public class PropertyEntry {
|
|||||||
* @return the property entry for given parameters.
|
* @return the property entry for given parameters.
|
||||||
*/
|
*/
|
||||||
public static PropertyEntry forSourceReference(String name, Accessor readAccessor,
|
public static PropertyEntry forSourceReference(String name, Accessor readAccessor,
|
||||||
ExecutableElementAccessor presenceChecker, Type type) {
|
Accessor presenceChecker, Type type) {
|
||||||
return new PropertyEntry( new String[]{name}, readAccessor, null, presenceChecker, type );
|
return new PropertyEntry( new String[]{name}, readAccessor, null, presenceChecker, type, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -82,7 +85,7 @@ public class PropertyEntry {
|
|||||||
return writeAccessor;
|
return writeAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExecutableElementAccessor getPresenceChecker() {
|
public Accessor getPresenceChecker() {
|
||||||
return presenceChecker;
|
return presenceChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +93,10 @@ public class PropertyEntry {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BuilderType getBuilderType() {
|
||||||
|
return builderType;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return Strings.join( Arrays.asList( fullName ), "." );
|
return Strings.join( Arrays.asList( fullName ), "." );
|
||||||
}
|
}
|
||||||
@ -97,7 +104,7 @@ public class PropertyEntry {
|
|||||||
public PropertyEntry pop() {
|
public PropertyEntry pop() {
|
||||||
if ( fullName.length > 1 ) {
|
if ( fullName.length > 1 ) {
|
||||||
String[] newFullName = Arrays.copyOfRange( fullName, 1, fullName.length );
|
String[] newFullName = Arrays.copyOfRange( fullName, 1, fullName.length );
|
||||||
return new PropertyEntry(newFullName, readAccessor, writeAccessor, presenceChecker, type );
|
return new PropertyEntry(newFullName, readAccessor, writeAccessor, presenceChecker, type, builderType );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -26,6 +26,23 @@ public class SelectionParameters {
|
|||||||
private final Types typeUtils;
|
private final Types typeUtils;
|
||||||
private final SourceRHS sourceRHS;
|
private final SourceRHS sourceRHS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns new selection parameters
|
||||||
|
*
|
||||||
|
* ResultType is not inherited.
|
||||||
|
*
|
||||||
|
* @param selectionParameters
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static SelectionParameters forInheritance(SelectionParameters selectionParameters) {
|
||||||
|
return new SelectionParameters(
|
||||||
|
selectionParameters.qualifiers,
|
||||||
|
selectionParameters.qualifyingNames,
|
||||||
|
null,
|
||||||
|
selectionParameters.typeUtils
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames, TypeMirror resultType,
|
public SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames, TypeMirror resultType,
|
||||||
Types typeUtils) {
|
Types typeUtils) {
|
||||||
this( qualifiers, qualifyingNames, resultType, typeUtils, null );
|
this( qualifiers, qualifyingNames, resultType, typeUtils, null );
|
||||||
|
@ -19,6 +19,7 @@ import javax.lang.model.element.Modifier;
|
|||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.common.Accessibility;
|
import org.mapstruct.ap.internal.model.common.Accessibility;
|
||||||
|
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
@ -50,6 +51,7 @@ public class SourceMethod implements Method {
|
|||||||
private final Parameter targetTypeParameter;
|
private final Parameter targetTypeParameter;
|
||||||
private final boolean isObjectFactory;
|
private final boolean isObjectFactory;
|
||||||
private final Type returnType;
|
private final Type returnType;
|
||||||
|
private final BuilderType builderType;
|
||||||
private final Accessibility accessibility;
|
private final Accessibility accessibility;
|
||||||
private final List<Type> exceptionTypes;
|
private final List<Type> exceptionTypes;
|
||||||
private final MapperConfiguration config;
|
private final MapperConfiguration config;
|
||||||
@ -80,6 +82,7 @@ public class SourceMethod implements Method {
|
|||||||
private ExecutableElement executable;
|
private ExecutableElement executable;
|
||||||
private List<Parameter> parameters;
|
private List<Parameter> parameters;
|
||||||
private Type returnType = null;
|
private Type returnType = null;
|
||||||
|
private BuilderType builderType = null;
|
||||||
private List<Type> exceptionTypes;
|
private List<Type> exceptionTypes;
|
||||||
private Map<String, List<Mapping>> mappings;
|
private Map<String, List<Mapping>> mappings;
|
||||||
private IterableMapping iterableMapping = null;
|
private IterableMapping iterableMapping = null;
|
||||||
@ -207,6 +210,7 @@ public class SourceMethod implements Method {
|
|||||||
this.executable = builder.executable;
|
this.executable = builder.executable;
|
||||||
this.parameters = builder.parameters;
|
this.parameters = builder.parameters;
|
||||||
this.returnType = builder.returnType;
|
this.returnType = builder.returnType;
|
||||||
|
this.builderType = builder.builderType;
|
||||||
this.exceptionTypes = builder.exceptionTypes;
|
this.exceptionTypes = builder.exceptionTypes;
|
||||||
this.accessibility = Accessibility.fromModifiers( builder.executable.getModifiers() );
|
this.accessibility = Accessibility.fromModifiers( builder.executable.getModifiers() );
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ import org.mapstruct.ap.internal.util.FormattingMessager;
|
|||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class describes the source side of a property mapping.
|
* This class describes the source side of a property mapping.
|
||||||
@ -209,7 +208,7 @@ public class SourceReference {
|
|||||||
for ( String entryName : entryNames ) {
|
for ( String entryName : entryNames ) {
|
||||||
boolean matchFound = false;
|
boolean matchFound = false;
|
||||||
Map<String, Accessor> sourceReadAccessors = newType.getPropertyReadAccessors();
|
Map<String, Accessor> sourceReadAccessors = newType.getPropertyReadAccessors();
|
||||||
Map<String, ExecutableElementAccessor> sourcePresenceCheckers = newType.getPropertyPresenceCheckers();
|
Map<String, Accessor> sourcePresenceCheckers = newType.getPropertyPresenceCheckers();
|
||||||
|
|
||||||
for ( Map.Entry<String, Accessor> getter : sourceReadAccessors.entrySet() ) {
|
for ( Map.Entry<String, Accessor> getter : sourceReadAccessors.entrySet() ) {
|
||||||
if ( getter.getKey().equals( entryName ) ) {
|
if ( getter.getKey().equals( entryName ) ) {
|
||||||
@ -245,7 +244,7 @@ public class SourceReference {
|
|||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private Accessor readAccessor;
|
private Accessor readAccessor;
|
||||||
private ExecutableElementAccessor presenceChecker;
|
private Accessor presenceChecker;
|
||||||
private Type type;
|
private Type type;
|
||||||
private Parameter sourceParameter;
|
private Parameter sourceParameter;
|
||||||
|
|
||||||
@ -259,7 +258,7 @@ public class SourceReference {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuilderFromProperty presenceChecker(ExecutableElementAccessor presenceChecker) {
|
public BuilderFromProperty presenceChecker(Accessor presenceChecker) {
|
||||||
this.presenceChecker = presenceChecker;
|
this.presenceChecker = presenceChecker;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -9,20 +9,21 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.type.DeclaredType;
|
import javax.lang.model.type.DeclaredType;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
|
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||||
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
|
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
|
||||||
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
|
||||||
import org.mapstruct.ap.internal.util.Executables;
|
|
||||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
import org.mapstruct.ap.internal.util.Strings;
|
import org.mapstruct.ap.internal.util.Strings;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
|
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class describes the target side of a property mapping.
|
* This class describes the target side of a property mapping.
|
||||||
@ -146,8 +147,7 @@ public class TargetReference {
|
|||||||
Parameter parameter = method.getMappingTargetParameter();
|
Parameter parameter = method.getMappingTargetParameter();
|
||||||
|
|
||||||
boolean foundEntryMatch;
|
boolean foundEntryMatch;
|
||||||
Type resultType = method.getResultType();
|
Type resultType = typeBasedOnMethod( method.getResultType() );
|
||||||
resultType = typeBasedOnMethod( resultType );
|
|
||||||
|
|
||||||
// there can be 4 situations
|
// there can be 4 situations
|
||||||
// 1. Return type
|
// 1. Return type
|
||||||
@ -198,8 +198,8 @@ public class TargetReference {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isLast || ( accessorNaming.isSetterMethod( targetWriteAccessor )
|
if ( isLast || ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER
|
||||||
|| Executables.isFieldAccessor( targetWriteAccessor ) ) ) {
|
|| targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) ) {
|
||||||
// only intermediate nested properties when they are a true setter or field accessor
|
// only intermediate nested properties when they are a true setter or field accessor
|
||||||
// the last may be other readAccessor (setter / getter / adder).
|
// the last may be other readAccessor (setter / getter / adder).
|
||||||
|
|
||||||
@ -207,8 +207,28 @@ public class TargetReference {
|
|||||||
|
|
||||||
// check if an entry alread exists, otherwise create
|
// check if an entry alread exists, otherwise create
|
||||||
String[] fullName = Arrays.copyOfRange( entryNames, 0, i + 1 );
|
String[] fullName = Arrays.copyOfRange( entryNames, 0, i + 1 );
|
||||||
PropertyEntry propertyEntry = PropertyEntry.forTargetReference( fullName, targetReadAccessor,
|
BuilderType builderType;
|
||||||
targetWriteAccessor, nextType );
|
PropertyEntry propertyEntry = null;
|
||||||
|
if ( method.isUpdateMethod() ) {
|
||||||
|
propertyEntry = PropertyEntry.forTargetReference(
|
||||||
|
fullName,
|
||||||
|
targetReadAccessor,
|
||||||
|
targetWriteAccessor,
|
||||||
|
nextType,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BuilderPrism builderPrism = BeanMapping.builderPrismFor( method );
|
||||||
|
builderType = typeFactory.builderTypeFor( nextType, builderPrism );
|
||||||
|
propertyEntry = PropertyEntry.forTargetReference(
|
||||||
|
fullName,
|
||||||
|
targetReadAccessor,
|
||||||
|
targetWriteAccessor,
|
||||||
|
nextType,
|
||||||
|
builderType
|
||||||
|
);
|
||||||
|
}
|
||||||
targetEntries.add( propertyEntry );
|
targetEntries.add( propertyEntry );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,8 +248,7 @@ public class TargetReference {
|
|||||||
private Type findNextType(Type initial, Accessor targetWriteAccessor, Accessor targetReadAccessor) {
|
private Type findNextType(Type initial, Accessor targetWriteAccessor, Accessor targetReadAccessor) {
|
||||||
Type nextType;
|
Type nextType;
|
||||||
Accessor toUse = targetWriteAccessor != null ? targetWriteAccessor : targetReadAccessor;
|
Accessor toUse = targetWriteAccessor != null ? targetWriteAccessor : targetReadAccessor;
|
||||||
if ( accessorNaming.isGetterMethod( toUse ) ||
|
if ( toUse.getAccessorType() == AccessorType.GETTER || toUse.getAccessorType() == AccessorType.FIELD ) {
|
||||||
Executables.isFieldAccessor( toUse ) ) {
|
|
||||||
nextType = typeFactory.getReturnType(
|
nextType = typeFactory.getReturnType(
|
||||||
(DeclaredType) typeBasedOnMethod( initial ).getTypeMirror(),
|
(DeclaredType) typeBasedOnMethod( initial ).getTypeMirror(),
|
||||||
toUse
|
toUse
|
||||||
@ -268,7 +287,8 @@ public class TargetReference {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return type.getEffectiveType();
|
BuilderPrism builderPrism = BeanMapping.builderPrismFor( method );
|
||||||
|
return typeFactory.effectiveResultTypeFor( type, builderPrism );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ package org.mapstruct.ap.internal.model.source.selector;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.lang.model.util.Elements;
|
import javax.lang.model.util.Elements;
|
||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
|
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies all known {@link MethodSelector}s in order.
|
* Applies all known {@link MethodSelector}s in order.
|
||||||
@ -25,10 +25,11 @@ public class MethodSelectors {
|
|||||||
|
|
||||||
private final List<MethodSelector> selectors;
|
private final List<MethodSelector> selectors;
|
||||||
|
|
||||||
public MethodSelectors(Types typeUtils, Elements elementUtils, TypeFactory typeFactory) {
|
public MethodSelectors(Types typeUtils, Elements elementUtils, TypeFactory typeFactory,
|
||||||
|
FormattingMessager messager) {
|
||||||
selectors = Arrays.asList(
|
selectors = Arrays.asList(
|
||||||
new MethodFamilySelector(),
|
new MethodFamilySelector(),
|
||||||
new TypeSelector( typeFactory ),
|
new TypeSelector( typeFactory, messager ),
|
||||||
new QualifierSelector( typeUtils, elementUtils ),
|
new QualifierSelector( typeUtils, elementUtils ),
|
||||||
new TargetTypeSelector( typeUtils, elementUtils ),
|
new TargetTypeSelector( typeUtils, elementUtils ),
|
||||||
new XmlElementDeclSelector( typeUtils ),
|
new XmlElementDeclSelector( typeUtils ),
|
||||||
|
@ -5,10 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.model.source.selector;
|
package org.mapstruct.ap.internal.model.source.selector;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||||
@ -17,6 +16,10 @@ import org.mapstruct.ap.internal.model.common.Type;
|
|||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
import org.mapstruct.ap.internal.model.source.MethodMatcher;
|
import org.mapstruct.ap.internal.model.source.MethodMatcher;
|
||||||
|
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||||
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
|
|
||||||
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects those methods from the given input set which match the given source and target types (via
|
* Selects those methods from the given input set which match the given source and target types (via
|
||||||
@ -27,9 +30,11 @@ import org.mapstruct.ap.internal.model.source.MethodMatcher;
|
|||||||
public class TypeSelector implements MethodSelector {
|
public class TypeSelector implements MethodSelector {
|
||||||
|
|
||||||
private TypeFactory typeFactory;
|
private TypeFactory typeFactory;
|
||||||
|
private FormattingMessager messager;
|
||||||
|
|
||||||
public TypeSelector(TypeFactory typeFactory) {
|
public TypeSelector(TypeFactory typeFactory, FormattingMessager messager) {
|
||||||
this.typeFactory = typeFactory;
|
this.typeFactory = typeFactory;
|
||||||
|
this.messager = messager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -63,7 +68,7 @@ public class TypeSelector implements MethodSelector {
|
|||||||
|
|
||||||
if ( parameterBindingPermutations != null ) {
|
if ( parameterBindingPermutations != null ) {
|
||||||
SelectedMethod<T> matchingMethod =
|
SelectedMethod<T> matchingMethod =
|
||||||
getFirstMatchingParameterBinding( targetType, method, parameterBindingPermutations );
|
getMatchingParameterBinding( targetType, mappingMethod, method, parameterBindingPermutations );
|
||||||
|
|
||||||
if ( matchingMethod != null ) {
|
if ( matchingMethod != null ) {
|
||||||
result.add( matchingMethod );
|
result.add( matchingMethod );
|
||||||
@ -77,7 +82,6 @@ public class TypeSelector implements MethodSelector {
|
|||||||
SourceRHS sourceRHS) {
|
SourceRHS sourceRHS) {
|
||||||
List<ParameterBinding> availableParams = new ArrayList<>( method.getParameters().size() + 3 );
|
List<ParameterBinding> availableParams = new ArrayList<>( method.getParameters().size() + 3 );
|
||||||
|
|
||||||
addMappingTargetAndTargetTypeBindings( availableParams, targetType );
|
|
||||||
if ( sourceRHS != null ) {
|
if ( sourceRHS != null ) {
|
||||||
availableParams.addAll( ParameterBinding.fromParameters( method.getContextParameters() ) );
|
availableParams.addAll( ParameterBinding.fromParameters( method.getContextParameters() ) );
|
||||||
availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) );
|
availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) );
|
||||||
@ -86,6 +90,8 @@ public class TypeSelector implements MethodSelector {
|
|||||||
availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) );
|
availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addMappingTargetAndTargetTypeBindings( availableParams, targetType );
|
||||||
|
|
||||||
return availableParams;
|
return availableParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +100,6 @@ public class TypeSelector implements MethodSelector {
|
|||||||
|
|
||||||
List<ParameterBinding> availableParams = new ArrayList<>( sourceTypes.size() + 2 );
|
List<ParameterBinding> availableParams = new ArrayList<>( sourceTypes.size() + 2 );
|
||||||
|
|
||||||
addMappingTargetAndTargetTypeBindings( availableParams, targetType );
|
|
||||||
|
|
||||||
for ( Type sourceType : sourceTypes ) {
|
for ( Type sourceType : sourceTypes ) {
|
||||||
availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) );
|
availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) );
|
||||||
}
|
}
|
||||||
@ -106,24 +110,124 @@ public class TypeSelector implements MethodSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addMappingTargetAndTargetTypeBindings( availableParams, targetType );
|
||||||
|
|
||||||
return availableParams;
|
return availableParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds default parameter bindings for the mapping-target and target-type if not already available.
|
||||||
|
*
|
||||||
|
* @param availableParams Already available params, new entries will be added to this list
|
||||||
|
* @param targetType Target type
|
||||||
|
*/
|
||||||
private void addMappingTargetAndTargetTypeBindings(List<ParameterBinding> availableParams, Type targetType) {
|
private void addMappingTargetAndTargetTypeBindings(List<ParameterBinding> availableParams, Type targetType) {
|
||||||
availableParams.add( ParameterBinding.forMappingTargetBinding( targetType ) );
|
boolean mappingTargetAvailable = false;
|
||||||
availableParams.add( ParameterBinding.forTargetTypeBinding( typeFactory.classTypeOf( targetType ) ) );
|
boolean targetTypeAvailable = false;
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends Method> SelectedMethod<T> getFirstMatchingParameterBinding(Type targetType,
|
// search available parameter bindings if mapping-target and/or target-type is available
|
||||||
SelectedMethod<T> method, List<List<ParameterBinding>> parameterAssignmentVariants) {
|
for ( ParameterBinding pb : availableParams ) {
|
||||||
|
if ( pb.isMappingTarget() ) {
|
||||||
for ( List<ParameterBinding> parameterAssignments : parameterAssignmentVariants ) {
|
mappingTargetAvailable = true;
|
||||||
if ( method.getMethod().matches( extractTypes( parameterAssignments ), targetType ) ) {
|
}
|
||||||
method.setParameterBindings( parameterAssignments );
|
else if ( pb.isTargetType() ) {
|
||||||
return method;
|
targetTypeAvailable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
if ( !mappingTargetAvailable ) {
|
||||||
|
availableParams.add( ParameterBinding.forMappingTargetBinding( targetType ) );
|
||||||
|
}
|
||||||
|
if ( !targetTypeAvailable ) {
|
||||||
|
availableParams.add( ParameterBinding.forTargetTypeBinding( typeFactory.classTypeOf( targetType ) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Method> SelectedMethod<T> getMatchingParameterBinding(Type targetType,
|
||||||
|
Method mappingMethod, SelectedMethod<T> selectedMethodInfo,
|
||||||
|
List<List<ParameterBinding>> parameterAssignmentVariants) {
|
||||||
|
|
||||||
|
List<List<ParameterBinding>> matchingParameterAssignmentVariants = new ArrayList<>(
|
||||||
|
parameterAssignmentVariants
|
||||||
|
);
|
||||||
|
|
||||||
|
Method selectedMethod = selectedMethodInfo.getMethod();
|
||||||
|
|
||||||
|
// remove all assignment variants that doesn't match the types from the method
|
||||||
|
matchingParameterAssignmentVariants.removeIf( parameterAssignments ->
|
||||||
|
!selectedMethod.matches( extractTypes( parameterAssignments ), targetType )
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( matchingParameterAssignmentVariants.isEmpty() ) {
|
||||||
|
// no matching variants found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if ( matchingParameterAssignmentVariants.size() == 1 ) {
|
||||||
|
// we found exactly one set of variants, use this
|
||||||
|
selectedMethodInfo.setParameterBindings( first( matchingParameterAssignmentVariants ) );
|
||||||
|
return selectedMethodInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// more than one variant matches, try to find one where also the parameter names are matching
|
||||||
|
// -> remove all variants where the binding var-name doesn't match the var-name of the parameter
|
||||||
|
List<Parameter> methodParameters = selectedMethod.getParameters();
|
||||||
|
|
||||||
|
matchingParameterAssignmentVariants.removeIf( parameterBindings ->
|
||||||
|
parameterBindingNotMatchesParameterVariableNames(
|
||||||
|
parameterBindings,
|
||||||
|
methodParameters
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if ( matchingParameterAssignmentVariants.isEmpty() ) {
|
||||||
|
// we had some matching assignments before, but when checking the parameter names we can't find an
|
||||||
|
// appropriate one, in this case the user must chose identical parameter names for the mapping and lifecycle
|
||||||
|
// method
|
||||||
|
messager.printMessage(
|
||||||
|
selectedMethod.getExecutable(),
|
||||||
|
Message.LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS,
|
||||||
|
mappingMethod
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there should never be more then one assignment left after checking the parameter names as it is not possible
|
||||||
|
// to use the same parameter name more then once
|
||||||
|
// -> we can use the first variant that is left (that should also be the only one)
|
||||||
|
selectedMethodInfo.setParameterBindings( first( matchingParameterAssignmentVariants ) );
|
||||||
|
return selectedMethodInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given parameter-bindings have the same variable name than the given parameters.<br>
|
||||||
|
* The first entry in the parameter-bindings belongs to the first entry in the parameters and so on.<br>
|
||||||
|
*
|
||||||
|
* @param parameterBindings List of parameter bindings
|
||||||
|
* @param parameters List of parameters, must have the same size than the {@code parameterBindings} list
|
||||||
|
*
|
||||||
|
* @return {@code true} as soon as there is a parameter with a different variable name than the binding
|
||||||
|
*/
|
||||||
|
private boolean parameterBindingNotMatchesParameterVariableNames(List<ParameterBinding> parameterBindings,
|
||||||
|
List<Parameter> parameters) {
|
||||||
|
if ( parameterBindings.size() != parameters.size() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for ( ParameterBinding parameterBinding : parameterBindings ) {
|
||||||
|
Parameter parameter = parameters.get( i++ );
|
||||||
|
|
||||||
|
// if the parameterBinding contains a parameter name we must ensure that this matches the name from the
|
||||||
|
// method parameter -> remove all variants where this is not the case
|
||||||
|
if ( parameterBinding.getVariableName() != null &&
|
||||||
|
!parameter.getName().equals( parameterBinding.getVariableName() ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,12 +304,8 @@ public class TypeSelector implements MethodSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static List<Type> extractTypes(List<ParameterBinding> parameters) {
|
private static List<Type> extractTypes(List<ParameterBinding> parameters) {
|
||||||
List<Type> result = new ArrayList<>( parameters.size() );
|
return parameters.stream()
|
||||||
|
.map( ParameterBinding::getType )
|
||||||
for ( ParameterBinding param : parameters ) {
|
.collect( Collectors.toList() );
|
||||||
result.add( param.getType() );
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,13 @@ import org.mapstruct.ap.internal.model.ValueMappingMethod;
|
|||||||
import org.mapstruct.ap.internal.model.common.Type;
|
import org.mapstruct.ap.internal.model.common.Type;
|
||||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||||
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
import org.mapstruct.ap.internal.model.common.FormattingParameters;
|
||||||
|
import org.mapstruct.ap.internal.model.source.BeanMapping;
|
||||||
import org.mapstruct.ap.internal.model.source.MappingOptions;
|
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.SourceMethod;
|
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||||
import org.mapstruct.ap.internal.option.Options;
|
import org.mapstruct.ap.internal.option.Options;
|
||||||
|
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||||
import org.mapstruct.ap.internal.prism.DecoratedWithPrism;
|
import org.mapstruct.ap.internal.prism.DecoratedWithPrism;
|
||||||
import org.mapstruct.ap.internal.prism.InheritConfigurationPrism;
|
import org.mapstruct.ap.internal.prism.InheritConfigurationPrism;
|
||||||
import org.mapstruct.ap.internal.prism.InheritInverseConfigurationPrism;
|
import org.mapstruct.ap.internal.prism.InheritInverseConfigurationPrism;
|
||||||
@ -365,11 +367,12 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
BuilderPrism builderPrism = BeanMapping.builderPrismFor( method );
|
||||||
BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder();
|
BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder();
|
||||||
BeanMappingMethod beanMappingMethod = builder
|
BeanMappingMethod beanMappingMethod = builder
|
||||||
.mappingContext( mappingContext )
|
.mappingContext( mappingContext )
|
||||||
.sourceMethod( method )
|
.sourceMethod( method )
|
||||||
|
.returnTypeBuilder( typeFactory.builderTypeFor( method.getReturnType(), builderPrism ) )
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if ( beanMappingMethod != null ) {
|
if ( beanMappingMethod != null ) {
|
||||||
|
@ -381,8 +381,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
|
|
||||||
private boolean checkParameterAndReturnType(ExecutableElement method, List<Parameter> sourceParameters,
|
private boolean checkParameterAndReturnType(ExecutableElement method, List<Parameter> sourceParameters,
|
||||||
Parameter targetParameter, List<Parameter> contextParameters,
|
Parameter targetParameter, List<Parameter> contextParameters,
|
||||||
Type resultType, Type returnType,
|
Type resultType, Type returnType, boolean containsTargetTypeParameter) {
|
||||||
boolean containsTargetTypeParameter) {
|
|
||||||
if ( sourceParameters.isEmpty() ) {
|
if ( sourceParameters.isEmpty() ) {
|
||||||
messager.printMessage( method, Message.RETRIEVAL_NO_INPUT_ARGS );
|
messager.printMessage( method, Message.RETRIEVAL_NO_INPUT_ARGS );
|
||||||
return false;
|
return false;
|
||||||
@ -401,7 +400,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
|
|
||||||
if ( returnType.getTypeMirror().getKind() != TypeKind.VOID &&
|
if ( returnType.getTypeMirror().getKind() != TypeKind.VOID &&
|
||||||
!resultType.isAssignableTo( returnType ) &&
|
!resultType.isAssignableTo( returnType ) &&
|
||||||
!resultType.isAssignableTo( returnType.getEffectiveType() )) {
|
!resultType.isAssignableTo( typeFactory.effectiveResultTypeFor( returnType, null ) )) {
|
||||||
messager.printMessage( method, Message.RETRIEVAL_NON_ASSIGNABLE_RESULTTYPE );
|
messager.printMessage( method, Message.RETRIEVAL_NON_ASSIGNABLE_RESULTTYPE );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
|
|
||||||
this.conversions = new Conversions( elementUtils, typeFactory );
|
this.conversions = new Conversions( elementUtils, typeFactory );
|
||||||
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
||||||
this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory );
|
this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory, messager );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.util;
|
|||||||
|
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
import javax.lang.model.type.DeclaredType;
|
import javax.lang.model.type.DeclaredType;
|
||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
@ -14,7 +15,7 @@ import javax.lang.model.util.SimpleElementVisitor6;
|
|||||||
import javax.lang.model.util.SimpleTypeVisitor6;
|
import javax.lang.model.util.SimpleTypeVisitor6;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||||
import org.mapstruct.ap.spi.AccessorNamingStrategy;
|
import org.mapstruct.ap.spi.AccessorNamingStrategy;
|
||||||
import org.mapstruct.ap.spi.MethodType;
|
import org.mapstruct.ap.spi.MethodType;
|
||||||
|
|
||||||
@ -33,46 +34,41 @@ public final class AccessorNamingUtils {
|
|||||||
this.accessorNamingStrategy = accessorNamingStrategy;
|
this.accessorNamingStrategy = accessorNamingStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGetterMethod(Accessor method) {
|
public boolean isGetterMethod(ExecutableElement executable) {
|
||||||
ExecutableElement executable = method.getExecutable();
|
return executable != null && isPublicNotStatic( executable ) &&
|
||||||
return executable != null && isPublicNotStatic( method ) &&
|
|
||||||
executable.getParameters().isEmpty() &&
|
executable.getParameters().isEmpty() &&
|
||||||
accessorNamingStrategy.getMethodType( executable ) == MethodType.GETTER;
|
accessorNamingStrategy.getMethodType( executable ) == MethodType.GETTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPresenceCheckMethod(Accessor method) {
|
public boolean isPresenceCheckMethod(ExecutableElement executable) {
|
||||||
if ( !( method instanceof ExecutableElementAccessor ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ExecutableElement executable = method.getExecutable();
|
|
||||||
return executable != null
|
return executable != null
|
||||||
&& isPublicNotStatic( method )
|
&& isPublicNotStatic( executable )
|
||||||
&& executable.getParameters().isEmpty()
|
&& executable.getParameters().isEmpty()
|
||||||
&& ( executable.getReturnType().getKind() == TypeKind.BOOLEAN ||
|
&& ( executable.getReturnType().getKind() == TypeKind.BOOLEAN ||
|
||||||
"java.lang.Boolean".equals( getQualifiedName( executable.getReturnType() ) ) )
|
"java.lang.Boolean".equals( getQualifiedName( executable.getReturnType() ) ) )
|
||||||
&& accessorNamingStrategy.getMethodType( executable ) == MethodType.PRESENCE_CHECKER;
|
&& accessorNamingStrategy.getMethodType( executable ) == MethodType.PRESENCE_CHECKER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSetterMethod(Accessor method) {
|
public boolean isSetterMethod(ExecutableElement executable) {
|
||||||
ExecutableElement executable = method.getExecutable();
|
|
||||||
return executable != null
|
return executable != null
|
||||||
&& isPublicNotStatic( method )
|
&& isPublicNotStatic( executable )
|
||||||
&& executable.getParameters().size() == 1
|
&& executable.getParameters().size() == 1
|
||||||
&& accessorNamingStrategy.getMethodType( executable ) == MethodType.SETTER;
|
&& accessorNamingStrategy.getMethodType( executable ) == MethodType.SETTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdderMethod(Accessor method) {
|
public boolean isAdderMethod(ExecutableElement executable) {
|
||||||
ExecutableElement executable = method.getExecutable();
|
|
||||||
return executable != null
|
return executable != null
|
||||||
&& isPublicNotStatic( method )
|
&& isPublicNotStatic( executable )
|
||||||
&& executable.getParameters().size() == 1
|
&& executable.getParameters().size() == 1
|
||||||
&& accessorNamingStrategy.getMethodType( executable ) == MethodType.ADDER;
|
&& accessorNamingStrategy.getMethodType( executable ) == MethodType.ADDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPropertyName(Accessor accessor) {
|
public String getPropertyName(ExecutableElement executable) {
|
||||||
ExecutableElement executable = accessor.getExecutable();
|
return accessorNamingStrategy.getPropertyName( executable );
|
||||||
return executable != null ? accessorNamingStrategy.getPropertyName( executable ) :
|
}
|
||||||
accessor.getSimpleName().toString();
|
|
||||||
|
public String getPropertyName(VariableElement variable) {
|
||||||
|
return variable.getSimpleName().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,8 +78,12 @@ public final class AccessorNamingUtils {
|
|||||||
* {@code addChild(Child v)}, the element name would be 'Child'.
|
* {@code addChild(Child v)}, the element name would be 'Child'.
|
||||||
*/
|
*/
|
||||||
public String getElementNameForAdder(Accessor adderMethod) {
|
public String getElementNameForAdder(Accessor adderMethod) {
|
||||||
ExecutableElement executable = adderMethod.getExecutable();
|
if ( adderMethod.getAccessorType() == AccessorType.ADDER ) {
|
||||||
return executable != null ? accessorNamingStrategy.getElementName( executable ) : null;
|
return accessorNamingStrategy.getElementName( (ExecutableElement) adderMethod.getElement() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getQualifiedName(TypeMirror type) {
|
private static String getQualifiedName(TypeMirror type) {
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.util;
|
package org.mapstruct.ap.internal.util;
|
||||||
|
|
||||||
import static javax.lang.model.util.ElementFilter.fieldsIn;
|
|
||||||
import static javax.lang.model.util.ElementFilter.methodsIn;
|
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||||
import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
||||||
|
|
||||||
@ -18,7 +17,6 @@ import java.util.ListIterator;
|
|||||||
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 javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.lang.model.element.VariableElement;
|
|
||||||
import javax.lang.model.type.DeclaredType;
|
import javax.lang.model.type.DeclaredType;
|
||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
@ -27,8 +25,6 @@ import javax.lang.model.util.Elements;
|
|||||||
import org.mapstruct.ap.internal.prism.AfterMappingPrism;
|
import org.mapstruct.ap.internal.prism.AfterMappingPrism;
|
||||||
import org.mapstruct.ap.internal.prism.BeforeMappingPrism;
|
import org.mapstruct.ap.internal.prism.BeforeMappingPrism;
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
|
||||||
import org.mapstruct.ap.internal.util.accessor.VariableElementAccessor;
|
|
||||||
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
|
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,28 +50,15 @@ public class Executables {
|
|||||||
private Executables() {
|
private Executables() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static boolean isPublicNotStatic(ExecutableElement accessor) {
|
||||||
* An {@link Accessor} is a field accessor, if it doesn't have an executable element, is public and it is not
|
|
||||||
* static.
|
|
||||||
*
|
|
||||||
* @param accessor the accessor to ber checked
|
|
||||||
*
|
|
||||||
* @return {@code true} if the {@code accessor} is for a {@code public} non {@code static} field.
|
|
||||||
*/
|
|
||||||
public static boolean isFieldAccessor(Accessor accessor) {
|
|
||||||
ExecutableElement executable = accessor.getExecutable();
|
|
||||||
return executable == null && isPublic( accessor ) && isNotStatic( accessor );
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean isPublicNotStatic(Accessor accessor) {
|
|
||||||
return isPublic( accessor ) && isNotStatic( accessor );
|
return isPublic( accessor ) && isNotStatic( accessor );
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isPublic(Accessor method) {
|
static boolean isPublic(ExecutableElement method) {
|
||||||
return method.getModifiers().contains( Modifier.PUBLIC );
|
return method.getModifiers().contains( Modifier.PUBLIC );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isNotStatic(Accessor accessor) {
|
private static boolean isNotStatic(ExecutableElement accessor) {
|
||||||
return !accessor.getModifiers().contains( Modifier.STATIC );
|
return !accessor.getModifiers().contains( Modifier.STATIC );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,8 +86,8 @@ public class Executables {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds all executable elements within the given type element, including executable elements defined in super
|
* Finds all executable elements within the given type element, including executable elements defined in super
|
||||||
* classes and implemented interfaces. Methods defined in {@link java.lang.Object} are ignored, as well as
|
* classes and implemented interfaces. Methods defined in {@link java.lang.Object},
|
||||||
* implementations of {@link java.lang.Object#equals(Object)}.
|
* implementations of {@link java.lang.Object#equals(Object)} and private methods are ignored
|
||||||
*
|
*
|
||||||
* @param elementUtils element helper
|
* @param elementUtils element helper
|
||||||
* @param element the element to inspect
|
* @param element the element to inspect
|
||||||
@ -112,34 +95,14 @@ public class Executables {
|
|||||||
* @return the executable elements usable in the type
|
* @return the executable elements usable in the type
|
||||||
*/
|
*/
|
||||||
public static List<ExecutableElement> getAllEnclosedExecutableElements(Elements elementUtils, TypeElement element) {
|
public static List<ExecutableElement> getAllEnclosedExecutableElements(Elements elementUtils, TypeElement element) {
|
||||||
List<ExecutableElement> executables = new ArrayList<>();
|
List<ExecutableElement> enclosedElements = new ArrayList<>();
|
||||||
for ( Accessor accessor : getAllEnclosedAccessors( elementUtils, element ) ) {
|
|
||||||
if ( accessor.getExecutable() != null ) {
|
|
||||||
executables.add( accessor.getExecutable() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return executables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds all executable elements/variable elements within the given type element, including executable/variable
|
|
||||||
* elements defined in super classes and implemented interfaces and including the fields in the . Methods defined
|
|
||||||
* in {@link java.lang.Object} are ignored, as well as implementations of {@link java.lang.Object#equals(Object)}.
|
|
||||||
*
|
|
||||||
* @param elementUtils element helper
|
|
||||||
* @param element the element to inspect
|
|
||||||
*
|
|
||||||
* @return the executable elements usable in the type
|
|
||||||
*/
|
|
||||||
public static List<Accessor> getAllEnclosedAccessors(Elements elementUtils, TypeElement element) {
|
|
||||||
List<Accessor> enclosedElements = new ArrayList<>();
|
|
||||||
element = replaceTypeElementIfNecessary( elementUtils, element );
|
element = replaceTypeElementIfNecessary( elementUtils, element );
|
||||||
addEnclosedElementsInHierarchy( elementUtils, enclosedElements, element, element );
|
addEnclosedElementsInHierarchy( elementUtils, enclosedElements, element, element );
|
||||||
|
|
||||||
return enclosedElements;
|
return enclosedElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addEnclosedElementsInHierarchy(Elements elementUtils, List<Accessor> alreadyAdded,
|
private static void addEnclosedElementsInHierarchy(Elements elementUtils, List<ExecutableElement> alreadyAdded,
|
||||||
TypeElement element, TypeElement parentType) {
|
TypeElement element, TypeElement parentType) {
|
||||||
if ( element != parentType ) { // otherwise the element was already checked for replacement
|
if ( element != parentType ) { // otherwise the element was already checked for replacement
|
||||||
element = replaceTypeElementIfNecessary( elementUtils, element );
|
element = replaceTypeElementIfNecessary( elementUtils, element );
|
||||||
@ -150,7 +113,6 @@ public class Executables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addNotYetOverridden( elementUtils, alreadyAdded, methodsIn( element.getEnclosedElements() ), parentType );
|
addNotYetOverridden( elementUtils, alreadyAdded, methodsIn( element.getEnclosedElements() ), parentType );
|
||||||
addFields( alreadyAdded, fieldsIn( element.getEnclosedElements() ) );
|
|
||||||
|
|
||||||
if ( hasNonObjectSuperclass( element ) ) {
|
if ( hasNonObjectSuperclass( element ) ) {
|
||||||
addEnclosedElementsInHierarchy(
|
addEnclosedElementsInHierarchy(
|
||||||
@ -178,28 +140,19 @@ public class Executables {
|
|||||||
* @param methodsToAdd methods to add to alreadyAdded, if they are not yet overridden by an element in the list
|
* @param methodsToAdd methods to add to alreadyAdded, if they are not yet overridden by an element in the list
|
||||||
* @param parentType the type for with elements are collected
|
* @param parentType the type for with elements are collected
|
||||||
*/
|
*/
|
||||||
private static void addNotYetOverridden(Elements elementUtils, List<Accessor> alreadyCollected,
|
private static void addNotYetOverridden(Elements elementUtils, List<ExecutableElement> alreadyCollected,
|
||||||
List<ExecutableElement> methodsToAdd, TypeElement parentType) {
|
List<ExecutableElement> methodsToAdd, TypeElement parentType) {
|
||||||
List<Accessor> safeToAdd = new ArrayList<>( methodsToAdd.size() );
|
List<ExecutableElement> safeToAdd = new ArrayList<>( methodsToAdd.size() );
|
||||||
for ( ExecutableElement toAdd : methodsToAdd ) {
|
for ( ExecutableElement toAdd : methodsToAdd ) {
|
||||||
if ( isNotObjectEquals( toAdd )
|
if ( isNotPrivate( toAdd ) && isNotObjectEquals( toAdd )
|
||||||
&& wasNotYetOverridden( elementUtils, alreadyCollected, toAdd, parentType ) ) {
|
&& wasNotYetOverridden( elementUtils, alreadyCollected, toAdd, parentType ) ) {
|
||||||
safeToAdd.add( new ExecutableElementAccessor( toAdd ) );
|
safeToAdd.add( toAdd );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
alreadyCollected.addAll( 0, safeToAdd );
|
alreadyCollected.addAll( 0, safeToAdd );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addFields(List<Accessor> alreadyCollected, List<VariableElement> variablesToAdd) {
|
|
||||||
List<Accessor> safeToAdd = new ArrayList<>( variablesToAdd.size() );
|
|
||||||
for ( VariableElement toAdd : variablesToAdd ) {
|
|
||||||
safeToAdd.add( new VariableElementAccessor( toAdd ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
alreadyCollected.addAll( 0, safeToAdd );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param executable the executable to check
|
* @param executable the executable to check
|
||||||
@ -217,6 +170,15 @@ public class Executables {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param executable the executable to check
|
||||||
|
*
|
||||||
|
* @return {@code true}, iff the executable does not have a private modifier
|
||||||
|
*/
|
||||||
|
private static boolean isNotPrivate(ExecutableElement executable) {
|
||||||
|
return !executable.getModifiers().contains( Modifier.PRIVATE );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param elementUtils the elementUtils
|
* @param elementUtils the elementUtils
|
||||||
* @param alreadyCollected the list of already collected methods of one type hierarchy (order is from sub-types to
|
* @param alreadyCollected the list of already collected methods of one type hierarchy (order is from sub-types to
|
||||||
@ -225,10 +187,10 @@ public class Executables {
|
|||||||
* @param parentType the type for which elements are collected
|
* @param parentType the type for which elements are collected
|
||||||
* @return {@code true}, iff the given executable was not yet overridden by a method in the given list.
|
* @return {@code true}, iff the given executable was not yet overridden by a method in the given list.
|
||||||
*/
|
*/
|
||||||
private static boolean wasNotYetOverridden(Elements elementUtils, List<Accessor> alreadyCollected,
|
private static boolean wasNotYetOverridden(Elements elementUtils, List<ExecutableElement> alreadyCollected,
|
||||||
ExecutableElement executable, TypeElement parentType) {
|
ExecutableElement executable, TypeElement parentType) {
|
||||||
for ( ListIterator<Accessor> it = alreadyCollected.listIterator(); it.hasNext(); ) {
|
for ( ListIterator<ExecutableElement> it = alreadyCollected.listIterator(); it.hasNext(); ) {
|
||||||
ExecutableElement executableInSubtype = it.next().getExecutable();
|
ExecutableElement executableInSubtype = it.next();
|
||||||
if ( executableInSubtype == null ) {
|
if ( executableInSubtype == null ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
|
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.spi.TypeHierarchyErroneousException;
|
||||||
|
|
||||||
|
import static javax.lang.model.util.ElementFilter.fieldsIn;
|
||||||
|
import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides functionality around {@link VariableElement}s.
|
||||||
|
*
|
||||||
|
* @author Sjaak Derksen
|
||||||
|
*/
|
||||||
|
public class Fields {
|
||||||
|
|
||||||
|
private Fields() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isFieldAccessor(VariableElement method) {
|
||||||
|
return isPublic( method ) && isNotStatic( method );
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isPublic(VariableElement method) {
|
||||||
|
return method.getModifiers().contains( Modifier.PUBLIC );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNotStatic(VariableElement method) {
|
||||||
|
return !method.getModifiers().contains( Modifier.STATIC );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all variable elements within the given type element, including variable
|
||||||
|
* elements defined in super classes and implemented interfaces and including the fields in the .
|
||||||
|
*
|
||||||
|
* @param elementUtils element helper
|
||||||
|
* @param element the element to inspect
|
||||||
|
*
|
||||||
|
* @return the executable elements usable in the type
|
||||||
|
*/
|
||||||
|
public static List<VariableElement> getAllEnclosedFields(Elements elementUtils, TypeElement element) {
|
||||||
|
List<VariableElement> enclosedElements = new ArrayList<>();
|
||||||
|
element = replaceTypeElementIfNecessary( elementUtils, element );
|
||||||
|
addEnclosedElementsInHierarchy( elementUtils, enclosedElements, element, element );
|
||||||
|
|
||||||
|
return enclosedElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addEnclosedElementsInHierarchy(Elements elementUtils, List<VariableElement> alreadyAdded,
|
||||||
|
TypeElement element, TypeElement parentType) {
|
||||||
|
if ( element != parentType ) { // otherwise the element was already checked for replacement
|
||||||
|
element = replaceTypeElementIfNecessary( elementUtils, element );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( element.asType().getKind() == TypeKind.ERROR ) {
|
||||||
|
throw new TypeHierarchyErroneousException( element );
|
||||||
|
}
|
||||||
|
|
||||||
|
addFields( alreadyAdded, fieldsIn( element.getEnclosedElements() ) );
|
||||||
|
|
||||||
|
|
||||||
|
if ( hasNonObjectSuperclass( element ) ) {
|
||||||
|
addEnclosedElementsInHierarchy(
|
||||||
|
elementUtils,
|
||||||
|
alreadyAdded,
|
||||||
|
asTypeElement( element.getSuperclass() ),
|
||||||
|
parentType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addFields(List<VariableElement> alreadyCollected, List<VariableElement> variablesToAdd) {
|
||||||
|
List<VariableElement> safeToAdd = new ArrayList<>( variablesToAdd.size() );
|
||||||
|
for ( VariableElement toAdd : variablesToAdd ) {
|
||||||
|
safeToAdd.add( toAdd );
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyCollected.addAll( 0, safeToAdd );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeElement asTypeElement(TypeMirror mirror) {
|
||||||
|
return (TypeElement) ( (DeclaredType) mirror ).asElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasNonObjectSuperclass(TypeElement element) {
|
||||||
|
if ( element.getSuperclass().getKind() == TypeKind.ERROR ) {
|
||||||
|
throw new TypeHierarchyErroneousException( element );
|
||||||
|
}
|
||||||
|
|
||||||
|
return element.getSuperclass().getKind() == TypeKind.DECLARED
|
||||||
|
&& !asTypeElement( element.getSuperclass() ).getQualifiedName().toString().equals( "java.lang.Object" );
|
||||||
|
}
|
||||||
|
}
|
@ -9,9 +9,22 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
|
import javax.lang.model.type.DeclaredType;
|
||||||
|
import javax.lang.model.type.ExecutableType;
|
||||||
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
import javax.lang.model.util.Types;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
||||||
|
import org.mapstruct.ap.internal.util.accessor.VariableElementAccessor;
|
||||||
|
|
||||||
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
import static org.mapstruct.ap.internal.util.accessor.AccessorType.ADDER;
|
||||||
|
import static org.mapstruct.ap.internal.util.accessor.AccessorType.GETTER;
|
||||||
|
import static org.mapstruct.ap.internal.util.accessor.AccessorType.PRESENCE_CHECKER;
|
||||||
|
import static org.mapstruct.ap.internal.util.accessor.AccessorType.SETTER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter methods for working with {@link Element} collections.
|
* Filter methods for working with {@link Element} collections.
|
||||||
@ -20,66 +33,88 @@ import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
|||||||
*/
|
*/
|
||||||
public class Filters {
|
public class Filters {
|
||||||
|
|
||||||
private Filters() {
|
private final AccessorNamingUtils accessorNaming;
|
||||||
|
private final Types typeUtils;
|
||||||
|
private final TypeMirror typeMirror;
|
||||||
|
|
||||||
|
public Filters(AccessorNamingUtils accessorNaming, Types typeUtils, TypeMirror typeMirror) {
|
||||||
|
this.accessorNaming = accessorNaming;
|
||||||
|
this.typeUtils = typeUtils;
|
||||||
|
this.typeMirror = typeMirror;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Accessor> getterMethodsIn(AccessorNamingUtils accessorNaming, List<Accessor> elements) {
|
public List<Accessor> getterMethodsIn(List<ExecutableElement> elements) {
|
||||||
List<Accessor> getterMethods = new LinkedList<>();
|
List<Accessor> getterMethods = new LinkedList<>();
|
||||||
|
|
||||||
for ( Accessor method : elements ) {
|
for ( ExecutableElement method : elements ) {
|
||||||
if ( accessorNaming.isGetterMethod( method ) ) {
|
if ( accessorNaming.isGetterMethod( method ) ) {
|
||||||
getterMethods.add( method );
|
getterMethods.add( new ExecutableElementAccessor( method, getReturnType( method ), GETTER ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getterMethods;
|
return getterMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Accessor> fieldsIn(List<Accessor> accessors) {
|
public List<Accessor> fieldsIn(List<VariableElement> accessors) {
|
||||||
List<Accessor> fieldAccessors = new LinkedList<>();
|
List<Accessor> fieldAccessors = new LinkedList<>();
|
||||||
|
|
||||||
for ( Accessor accessor : accessors ) {
|
for ( VariableElement accessor : accessors ) {
|
||||||
if ( Executables.isFieldAccessor( accessor ) ) {
|
if ( Fields.isFieldAccessor( accessor ) ) {
|
||||||
fieldAccessors.add( accessor );
|
fieldAccessors.add( new VariableElementAccessor( accessor ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fieldAccessors;
|
return fieldAccessors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ExecutableElementAccessor> presenceCheckMethodsIn(AccessorNamingUtils accessorNaming,
|
public List<Accessor> presenceCheckMethodsIn(List<ExecutableElement> elements) {
|
||||||
List<Accessor> elements) {
|
List<Accessor> presenceCheckMethods = new LinkedList<>();
|
||||||
List<ExecutableElementAccessor> presenceCheckMethods = new LinkedList<>();
|
|
||||||
|
|
||||||
for ( Accessor method : elements ) {
|
for ( ExecutableElement method : elements ) {
|
||||||
if ( accessorNaming.isPresenceCheckMethod( method ) ) {
|
if ( accessorNaming.isPresenceCheckMethod( method ) ) {
|
||||||
presenceCheckMethods.add( (ExecutableElementAccessor) method );
|
presenceCheckMethods.add( new ExecutableElementAccessor(
|
||||||
|
method,
|
||||||
|
getReturnType( method ),
|
||||||
|
PRESENCE_CHECKER
|
||||||
|
) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return presenceCheckMethods;
|
return presenceCheckMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Accessor> setterMethodsIn(AccessorNamingUtils accessorNaming, List<Accessor> elements) {
|
public List<Accessor> setterMethodsIn(List<ExecutableElement> elements) {
|
||||||
List<Accessor> setterMethods = new LinkedList<>();
|
List<Accessor> setterMethods = new LinkedList<>();
|
||||||
|
|
||||||
for ( Accessor method : elements ) {
|
for ( ExecutableElement method : elements ) {
|
||||||
if ( accessorNaming.isSetterMethod( method ) ) {
|
if ( accessorNaming.isSetterMethod( method ) ) {
|
||||||
setterMethods.add( method );
|
setterMethods.add( new ExecutableElementAccessor( method, getFirstParameter( method ), SETTER ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return setterMethods;
|
return setterMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Accessor> adderMethodsIn(AccessorNamingUtils accessorNaming, List<Accessor> elements) {
|
public List<Accessor> adderMethodsIn(List<ExecutableElement> elements) {
|
||||||
List<Accessor> adderMethods = new LinkedList<>();
|
List<Accessor> adderMethods = new LinkedList<>();
|
||||||
|
|
||||||
for ( Accessor method : elements ) {
|
for ( ExecutableElement method : elements ) {
|
||||||
if ( accessorNaming.isAdderMethod( method ) ) {
|
if ( accessorNaming.isAdderMethod( method ) ) {
|
||||||
adderMethods.add( method );
|
adderMethods.add( new ExecutableElementAccessor( method, getFirstParameter( method ), ADDER ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return adderMethods;
|
return adderMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TypeMirror getReturnType(ExecutableElement executableElement) {
|
||||||
|
return getWithinContext( executableElement ).getReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeMirror getFirstParameter(ExecutableElement executableElement) {
|
||||||
|
return first( getWithinContext( executableElement ).getParameterTypes() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExecutableType getWithinContext( ExecutableElement executableElement ) {
|
||||||
|
return (ExecutableType) typeUtils.asMemberOf( (DeclaredType) typeMirror, executableElement );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ public class MapperConfiguration {
|
|||||||
else if ( beanPrism != null ) {
|
else if ( beanPrism != null ) {
|
||||||
return beanPrism;
|
return beanPrism;
|
||||||
}
|
}
|
||||||
else if ( mapperConfigPrism != null && mapperPrism.values.nullValueCheckStrategy() == null ) {
|
else if ( mapperConfigPrism != null && mapperPrism.values.nullValuePropertyMappingStrategy() == null ) {
|
||||||
return NullValuePropertyMappingStrategyPrism.valueOf(
|
return NullValuePropertyMappingStrategyPrism.valueOf(
|
||||||
mapperConfigPrism.nullValuePropertyMappingStrategy()
|
mapperConfigPrism.nullValuePropertyMappingStrategy()
|
||||||
);
|
);
|
||||||
@ -265,8 +265,11 @@ public class MapperConfiguration {
|
|||||||
return mapperPrism.disableSubMappingMethodsGeneration(); // fall back to default defined in the annotation
|
return mapperPrism.disableSubMappingMethodsGeneration(); // fall back to default defined in the annotation
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuilderPrism getBuilderPrism() {
|
public BuilderPrism getBuilderPrism(BuilderPrism beanMappingBuilderPrism) {
|
||||||
if ( mapperPrism.values.builder() != null ) {
|
if ( beanMappingBuilderPrism != null ) {
|
||||||
|
return beanMappingBuilderPrism;
|
||||||
|
}
|
||||||
|
else if ( mapperPrism.values.builder() != null ) {
|
||||||
return mapperPrism.builder();
|
return mapperPrism.builder();
|
||||||
}
|
}
|
||||||
else if ( mapperConfigPrism != null && mapperConfigPrism.values.builder() != null ) {
|
else if ( mapperConfigPrism != null && mapperConfigPrism.values.builder() != null ) {
|
||||||
|
@ -32,7 +32,8 @@ public enum Message {
|
|||||||
BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ),
|
BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ),
|
||||||
|
|
||||||
PROPERTYMAPPING_MAPPING_NOT_FOUND( "Can't map %s to \"%s %s\". Consider to declare/implement a mapping method: \"%s map(%s value)\"." ),
|
PROPERTYMAPPING_MAPPING_NOT_FOUND( "Can't map %s to \"%s %s\". Consider to declare/implement a mapping method: \"%s map(%s value)\"." ),
|
||||||
PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND( "Can't map %s to %s. Consider to implement a mapping method: \"%s map(%s value)\"." ),
|
PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND( "No target bean properties found: can't map %s to \"%s %s\". Consider to declare/implement a mapping method: \"%s map(%s value)\"." ),
|
||||||
|
PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND( "No target bean properties found: can't map %s to %s. Consider to implement a mapping method: \"%s map(%s value)\"." ),
|
||||||
PROPERTYMAPPING_DUPLICATE_TARGETS( "Target property \"%s\" must not be mapped more than once." ),
|
PROPERTYMAPPING_DUPLICATE_TARGETS( "Target property \"%s\" must not be mapped more than once." ),
|
||||||
PROPERTYMAPPING_EMPTY_TARGET( "Target must not be empty in @Mapping." ),
|
PROPERTYMAPPING_EMPTY_TARGET( "Target must not be empty in @Mapping." ),
|
||||||
PROPERTYMAPPING_SOURCE_AND_CONSTANT_BOTH_DEFINED( "Source and constant are both defined in @Mapping, either define a source or a constant." ),
|
PROPERTYMAPPING_SOURCE_AND_CONSTANT_BOTH_DEFINED( "Source and constant are both defined in @Mapping, either define a source or a constant." ),
|
||||||
@ -81,6 +82,8 @@ public enum Message {
|
|||||||
ENUMMAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ),
|
ENUMMAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ),
|
||||||
ENUMMAPPING_DEPRECATED( "Mapping of Enums via @Mapping is going to be removed in future versions of MapStruct. Please use @ValueMapping instead!", Diagnostic.Kind.WARNING ),
|
ENUMMAPPING_DEPRECATED( "Mapping of Enums via @Mapping is going to be removed in future versions of MapStruct. Please use @ValueMapping instead!", Diagnostic.Kind.WARNING ),
|
||||||
|
|
||||||
|
LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS( "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to name the parameters in the lifecycle and mapping method identical. This lifecycle method will not be used for the mapping method '%s'.", Diagnostic.Kind.WARNING),
|
||||||
|
|
||||||
DECORATOR_NO_SUBTYPE( "Specified decorator type is no subtype of the annotated mapper type." ),
|
DECORATOR_NO_SUBTYPE( "Specified decorator type is no subtype of the annotated mapper type." ),
|
||||||
DECORATOR_CONSTRUCTOR( "Specified decorator type has no default constructor nor a constructor with a single parameter accepting the decorated mapper type." ),
|
DECORATOR_CONSTRUCTOR( "Specified decorator type has no default constructor nor a constructor with a single parameter accepting the decorated mapper type." ),
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package org.mapstruct.ap.internal.util;
|
package org.mapstruct.ap.internal.util;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||||
|
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This a wrapper class which provides the value that needs to be used in the models.
|
* This a wrapper class which provides the value that needs to be used in the models.
|
||||||
@ -45,7 +46,7 @@ public class ValueProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String value = accessor.getSimpleName().toString();
|
String value = accessor.getSimpleName().toString();
|
||||||
if ( accessor.getExecutable() != null ) {
|
if ( accessor.getAccessorType() != AccessorType.FIELD ) {
|
||||||
value += "()";
|
value += "()";
|
||||||
}
|
}
|
||||||
return new ValueProvider( value );
|
return new ValueProvider( value );
|
||||||
|
@ -38,4 +38,5 @@ abstract class AbstractAccessor<T extends Element> implements Accessor {
|
|||||||
public T getElement() {
|
public T getElement() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.util.accessor;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
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.VariableElement;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
import javax.lang.model.element.Name;
|
import javax.lang.model.element.Name;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
@ -23,7 +24,7 @@ public interface Accessor {
|
|||||||
* This returns the type that this accessor gives as a return.
|
* This returns the type that this accessor gives as a return.
|
||||||
*
|
*
|
||||||
* e.g. The {@link ExecutableElement#getReturnType()} if this is a method accessor,
|
* e.g. The {@link ExecutableElement#getReturnType()} if this is a method accessor,
|
||||||
* or {@link javax.lang.model.element.VariableElement#asType()} for field accessors.
|
* or {@link VariableElement#asType()} for field accessors.
|
||||||
*
|
*
|
||||||
* @return the type that the accessor gives as a return
|
* @return the type that the accessor gives as a return
|
||||||
*/
|
*/
|
||||||
@ -39,13 +40,15 @@ public interface Accessor {
|
|||||||
*/
|
*/
|
||||||
Set<Modifier> getModifiers();
|
Set<Modifier> getModifiers();
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the {@link ExecutableElement}, or {@code null} if the accessor does not have one
|
|
||||||
*/
|
|
||||||
ExecutableElement getExecutable();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the underlying {@link Element}
|
* @return the underlying {@link Element}
|
||||||
*/
|
*/
|
||||||
Element getElement();
|
Element getElement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The accessor type
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
AccessorType getAccessorType();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.accessor;
|
||||||
|
|
||||||
|
public enum AccessorType {
|
||||||
|
|
||||||
|
FIELD,
|
||||||
|
GETTER,
|
||||||
|
SETTER,
|
||||||
|
ADDER,
|
||||||
|
PRESENCE_CHECKER;
|
||||||
|
}
|
@ -15,17 +15,28 @@ import javax.lang.model.type.TypeMirror;
|
|||||||
*/
|
*/
|
||||||
public class ExecutableElementAccessor extends AbstractAccessor<ExecutableElement> {
|
public class ExecutableElementAccessor extends AbstractAccessor<ExecutableElement> {
|
||||||
|
|
||||||
public ExecutableElementAccessor(ExecutableElement element) {
|
private final TypeMirror accessedType;
|
||||||
|
private final AccessorType accessorType;
|
||||||
|
|
||||||
|
public ExecutableElementAccessor(ExecutableElement element, TypeMirror accessedType,
|
||||||
|
AccessorType accessorType) {
|
||||||
super( element );
|
super( element );
|
||||||
|
this.accessedType = accessedType;
|
||||||
|
this.accessorType = accessorType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TypeMirror getAccessedType() {
|
public TypeMirror getAccessedType() {
|
||||||
return element.getReturnType();
|
return accessedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecutableElement getExecutable() {
|
public String toString() {
|
||||||
return element;
|
return element.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessorType getAccessorType() {
|
||||||
|
return accessorType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.util.accessor;
|
package org.mapstruct.ap.internal.util.accessor;
|
||||||
|
|
||||||
import javax.lang.model.element.ExecutableElement;
|
|
||||||
import javax.lang.model.element.VariableElement;
|
import javax.lang.model.element.VariableElement;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
|
||||||
@ -26,7 +25,12 @@ public class VariableElementAccessor extends AbstractAccessor<VariableElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecutableElement getExecutable() {
|
public String toString() {
|
||||||
return null;
|
return element.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessorType getAccessorType() {
|
||||||
|
return AccessorType.FIELD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,8 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} when the {@link ExecutableElement} is a getter method. A method is a getter when it starts
|
* Returns {@code true} when the {@link ExecutableElement} is a getter method. A method is a getter when it
|
||||||
|
* has no parameters, starts
|
||||||
* with 'get' and the return type is any type other than {@code void}, OR the getter starts with 'is' and the type
|
* with 'get' and the return type is any type other than {@code void}, OR the getter starts with 'is' and the type
|
||||||
* returned is a primitive or the wrapper for {@code boolean}. NOTE: the latter does strictly not comply to the bean
|
* returned is a primitive or the wrapper for {@code boolean}. NOTE: the latter does strictly not comply to the bean
|
||||||
* convention. The remainder of the name is supposed to reflect the property name.
|
* convention. The remainder of the name is supposed to reflect the property name.
|
||||||
@ -69,6 +70,10 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
|
|||||||
* @return {@code true} when the method is a getter.
|
* @return {@code true} when the method is a getter.
|
||||||
*/
|
*/
|
||||||
public boolean isGetterMethod(ExecutableElement method) {
|
public boolean isGetterMethod(ExecutableElement method) {
|
||||||
|
if ( !method.getParameters().isEmpty() ) {
|
||||||
|
// If the method has parameters it can't be a getter
|
||||||
|
return false;
|
||||||
|
}
|
||||||
String methodName = method.getSimpleName().toString();
|
String methodName = method.getSimpleName().toString();
|
||||||
|
|
||||||
boolean isNonBooleanGetterName = methodName.startsWith( "get" ) && methodName.length() > 3 &&
|
boolean isNonBooleanGetterName = methodName.startsWith( "get" ) && methodName.length() > 3 &&
|
||||||
@ -166,11 +171,22 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
|
|||||||
@Override
|
@Override
|
||||||
public String getPropertyName(ExecutableElement getterOrSetterMethod) {
|
public String getPropertyName(ExecutableElement getterOrSetterMethod) {
|
||||||
String methodName = getterOrSetterMethod.getSimpleName().toString();
|
String methodName = getterOrSetterMethod.getSimpleName().toString();
|
||||||
if ( methodName.startsWith( "get" ) || methodName.startsWith( "set" ) ) {
|
if ( isFluentSetter( getterOrSetterMethod ) ) {
|
||||||
return IntrospectorUtils.decapitalize( methodName.substring( 3 ) );
|
// If this is a fluent setter that starts with set and the 4th character is an uppercase one
|
||||||
}
|
// then we treat it as a Java Bean style method (we get the property starting from the 4th character).
|
||||||
else if ( isFluentSetter( getterOrSetterMethod ) ) {
|
// Otherwise we treat it as a fluent setter
|
||||||
return methodName;
|
// For example, for the following methods:
|
||||||
|
// * public Builder setSettlementDate(String settlementDate)
|
||||||
|
// * public Builder settlementDate(String settlementDate)
|
||||||
|
// We are going to extract the same property name settlementDate
|
||||||
|
if ( methodName.startsWith( "set" )
|
||||||
|
&& methodName.length() > 3
|
||||||
|
&& Character.isUpperCase( methodName.charAt( 3 ) ) ) {
|
||||||
|
return IntrospectorUtils.decapitalize( methodName.substring( 3 ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return methodName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return IntrospectorUtils.decapitalize( methodName.substring( methodName.startsWith( "is" ) ? 2 : 3 ) );
|
return IntrospectorUtils.decapitalize( methodName.substring( methodName.startsWith( "is" ) ? 2 : 3 ) );
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,14 @@ public class DefaultBuilderProvider implements BuilderProvider {
|
|||||||
return method.getParameters().isEmpty()
|
return method.getParameters().isEmpty()
|
||||||
&& method.getModifiers().contains( Modifier.PUBLIC )
|
&& method.getModifiers().contains( Modifier.PUBLIC )
|
||||||
&& method.getModifiers().contains( Modifier.STATIC )
|
&& method.getModifiers().contains( Modifier.STATIC )
|
||||||
&& !typeUtils.isSameType( method.getReturnType(), typeElement.asType() );
|
&& method.getReturnType().getKind() != TypeKind.VOID
|
||||||
|
// Only compare raw elements
|
||||||
|
// Reason: if the method is a generic method (<T> Holder<T> build()) and the type element is (Holder<T>)
|
||||||
|
// then the return type of the method does not match the type of the type element
|
||||||
|
&& !typeUtils.isSameType(
|
||||||
|
typeUtils.erasure( method.getReturnType() ),
|
||||||
|
typeUtils.erasure( typeElement.asType() )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>)<@throws/> {
|
<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>)<@throws/> {
|
||||||
<#assign targetType = resultType />
|
<#assign targetType = resultType />
|
||||||
<#if !existingInstanceMapping>
|
<#if !existingInstanceMapping>
|
||||||
<#assign targetType = resultType.effectiveType />
|
<#assign targetType = returnTypeToConstruct />
|
||||||
</#if>
|
</#if>
|
||||||
<#list beforeMappingReferencesWithoutMappingTarget as callback>
|
<#list beforeMappingReferencesWithoutMappingTarget as callback>
|
||||||
<@includeModel object=callback targetBeanName=resultName targetType=targetType/>
|
<@includeModel object=callback targetBeanName=resultName targetType=targetType/>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#if !existingInstanceMapping>
|
<#if !existingInstanceMapping>
|
||||||
<@includeModel object=resultType.effectiveType/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType=resultType.effectiveType/><#else>new <@includeModel object=resultType.effectiveType/>()</#if>;
|
<@includeModel object=returnTypeToConstruct/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType=returnTypeToConstruct/><#else>new <@includeModel object=returnTypeToConstruct/>()</#if>;
|
||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
<#list beforeMappingReferencesWithMappingTarget as callback>
|
<#list beforeMappingReferencesWithMappingTarget as callback>
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
<@compress single_line=true>
|
<@compress single_line=true>
|
||||||
<#if factoryMethod??>
|
<#if factoryMethod??>
|
||||||
<@includeModel object=factoryMethod targetType=resultType/>
|
<@includeModel object=factoryMethod targetType=resultType/>
|
||||||
|
<#elseif enumSet>
|
||||||
|
EnumSet.noneOf( <@includeModel object=enumSetElementType raw=true/>.class )
|
||||||
<#else>
|
<#else>
|
||||||
new
|
new
|
||||||
<#if resultType.implementationType??>
|
<#if resultType.implementationType??>
|
||||||
|
@ -6,17 +6,17 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.NestedPropertyMappingMethod" -->
|
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.NestedPropertyMappingMethod" -->
|
||||||
<#lt>private <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>)<@throws/> {
|
<#lt>private <@includeModel object=returnType.typeBound/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>)<@throws/> {
|
||||||
if ( ${sourceParameter.name} == null ) {
|
if ( ${sourceParameter.name} == null ) {
|
||||||
return ${returnType.null};
|
return ${returnType.null};
|
||||||
}
|
}
|
||||||
<#list propertyEntries as entry>
|
<#list propertyEntries as entry>
|
||||||
<#if entry.presenceCheckerName?? >
|
<#if entry.presenceCheckerName?? >
|
||||||
if ( !<@localVarName index=entry_index/>.${entry.presenceCheckerName}() ) {
|
if ( <#if entry_index != 0><@localVarName index=entry_index/> == null || </#if>!<@localVarName index=entry_index/>.${entry.presenceCheckerName}() ) {
|
||||||
return ${returnType.null};
|
return ${returnType.null};
|
||||||
}
|
}
|
||||||
</#if>
|
</#if>
|
||||||
<@includeModel object=entry.type/> ${entry.name} = <@localVarName index=entry_index/>.${entry.accessorName};
|
<@includeModel object=entry.type.typeBound/> ${entry.name} = <@localVarName index=entry_index/>.${entry.accessorName};
|
||||||
<#if !entry.presenceCheckerName?? >
|
<#if !entry.presenceCheckerName?? >
|
||||||
<#if !entry.type.primitive>
|
<#if !entry.type.primitive>
|
||||||
if ( ${entry.name} == null ) {
|
if ( ${entry.name} == null ) {
|
||||||
|
@ -167,7 +167,6 @@ public class DateFormatValidatorFactoryTest {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
fullQualifiedName,
|
fullQualifiedName,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -115,7 +115,6 @@ public class DefaultConversionContextTest {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
fullQualifiedName,
|
fullQualifiedName,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1457;
|
||||||
|
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.ReportingPolicy;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||||
|
public abstract class BookMapper {
|
||||||
|
|
||||||
|
public static final BookMapper INSTANCE = Mappers.getMapper( BookMapper.class );
|
||||||
|
|
||||||
|
public abstract TargetBook mapBook(SourceBook sourceBook, String authorFirstName, String authorLastName);
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
protected void fillAuthor(@MappingTarget TargetBook targetBook, String authorFirstName, String authorLastName) {
|
||||||
|
targetBook.setAuthorFirstName( authorFirstName );
|
||||||
|
targetBook.setAuthorLastName( authorLastName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
protected void withoutAuthorNames(@MappingTarget TargetBook targetBook) {
|
||||||
|
targetBook.setAfterMappingWithoutAuthorName( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
protected void withOnlyFirstName(@MappingTarget TargetBook targetBook, String authorFirstName) {
|
||||||
|
targetBook.setAfterMappingWithOnlyFirstName( authorFirstName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
protected void withOnlyLastName(@MappingTarget TargetBook targetBook, String authorLastName) {
|
||||||
|
targetBook.setAfterMappingWithOnlyLastName( authorLastName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
protected void withDifferentVariableName(@MappingTarget TargetBook targetBook, String author) {
|
||||||
|
targetBook.setAfterMappingWithDifferentVariableName( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1457;
|
||||||
|
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.ReportingPolicy;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||||
|
public abstract class DifferentOrderingBookMapper {
|
||||||
|
|
||||||
|
public static final DifferentOrderingBookMapper INSTANCE = Mappers.getMapper( DifferentOrderingBookMapper.class );
|
||||||
|
|
||||||
|
public abstract TargetBook mapBook(SourceBook sourceBook, String authorFirstName, String authorLastName);
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
protected void fillAuthor(String authorLastName, String authorFirstName, @MappingTarget TargetBook targetBook) {
|
||||||
|
targetBook.setAuthorLastName( authorLastName );
|
||||||
|
targetBook.setAuthorFirstName( authorFirstName );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1457;
|
||||||
|
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.ReportingPolicy;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||||
|
public abstract class ErroneousBookMapper {
|
||||||
|
|
||||||
|
public static final ErroneousBookMapper INSTANCE = Mappers.getMapper( ErroneousBookMapper.class );
|
||||||
|
|
||||||
|
public abstract TargetBook mapBook(SourceBook sourceBook, String authorFirstName, String authorLastName);
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
protected void fillAuthor(@MappingTarget TargetBook targetBook, String authorFirstN, String authorLastN) {
|
||||||
|
targetBook.setAuthorFirstName( authorFirstN );
|
||||||
|
targetBook.setAuthorLastName( authorLastN );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1457;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@WithClasses({
|
||||||
|
SourceBook.class,
|
||||||
|
TargetBook.class
|
||||||
|
})
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@IssueKey("1457")
|
||||||
|
public class Issue1457Test {
|
||||||
|
|
||||||
|
private SourceBook sourceBook;
|
||||||
|
private String authorFirstName;
|
||||||
|
private String authorLastName;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
sourceBook = new SourceBook();
|
||||||
|
sourceBook.setIsbn( "3453146972" );
|
||||||
|
sourceBook.setTitle( "Per Anhalter durch die Galaxis" );
|
||||||
|
|
||||||
|
authorFirstName = "Douglas";
|
||||||
|
authorLastName = "Adams";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
BookMapper.class
|
||||||
|
})
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.SUCCEEDED,
|
||||||
|
diagnostics = @Diagnostic(
|
||||||
|
messageRegExp =
|
||||||
|
"Lifecycle method has multiple matching parameters \\(e\\. g\\. same type\\), in this case " +
|
||||||
|
"please ensure to name the parameters in the lifecycle and mapping method identical\\. This " +
|
||||||
|
"lifecycle method will not be used for the mapping method '.*\\.TargetBook mapBook\\(" +
|
||||||
|
".*\\.SourceBook sourceBook, .*\\.String authorFirstName, .*\\.String authorLastName\\)'\\.",
|
||||||
|
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||||
|
line = 43
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public void testMapperWithMatchingParameterNames() {
|
||||||
|
TargetBook targetBook = BookMapper.INSTANCE.mapBook( sourceBook, authorFirstName, authorLastName );
|
||||||
|
|
||||||
|
assertTargetBookMatchesSourceBook( targetBook );
|
||||||
|
|
||||||
|
assertThat( targetBook.isAfterMappingWithoutAuthorName() ).isTrue();
|
||||||
|
assertThat( targetBook.getAfterMappingWithOnlyFirstName() ).isEqualTo( authorFirstName );
|
||||||
|
assertThat( targetBook.getAfterMappingWithOnlyLastName() ).isEqualTo( authorLastName );
|
||||||
|
assertThat( targetBook.isAfterMappingWithDifferentVariableName() ).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
DifferentOrderingBookMapper.class
|
||||||
|
})
|
||||||
|
public void testMapperWithMatchingParameterNamesAndDifferentOrdering() {
|
||||||
|
TargetBook targetBook = DifferentOrderingBookMapper.INSTANCE.mapBook(
|
||||||
|
sourceBook,
|
||||||
|
authorFirstName,
|
||||||
|
authorLastName
|
||||||
|
);
|
||||||
|
|
||||||
|
assertTargetBookMatchesSourceBook( targetBook );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ObjectFactoryBookMapper.class
|
||||||
|
})
|
||||||
|
public void testMapperWithObjectFactory() {
|
||||||
|
TargetBook targetBook = ObjectFactoryBookMapper.INSTANCE.mapBook(
|
||||||
|
sourceBook,
|
||||||
|
authorFirstName,
|
||||||
|
authorLastName
|
||||||
|
);
|
||||||
|
|
||||||
|
assertTargetBookMatchesSourceBook( targetBook );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTargetBookMatchesSourceBook(TargetBook targetBook) {
|
||||||
|
assertThat( sourceBook.getIsbn() ).isEqualTo( targetBook.getIsbn() );
|
||||||
|
assertThat( sourceBook.getTitle() ).isEqualTo( targetBook.getTitle() );
|
||||||
|
assertThat( authorFirstName ).isEqualTo( targetBook.getAuthorFirstName() );
|
||||||
|
assertThat( authorLastName ).isEqualTo( targetBook.getAuthorLastName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses({
|
||||||
|
ErroneousBookMapper.class
|
||||||
|
})
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.SUCCEEDED,
|
||||||
|
diagnostics = @Diagnostic(
|
||||||
|
messageRegExp =
|
||||||
|
"Lifecycle method has multiple matching parameters \\(e\\. g\\. same type\\), in this case " +
|
||||||
|
"please ensure to name the parameters in the lifecycle and mapping method identical\\. This " +
|
||||||
|
"lifecycle method will not be used for the mapping method '.*\\.TargetBook mapBook\\(" +
|
||||||
|
".*\\.SourceBook sourceBook, .*\\.String authorFirstName, .*\\.String authorLastName\\)'\\.",
|
||||||
|
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||||
|
line = 22
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public void testMapperWithoutMatchingParameterNames() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1457;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.ObjectFactory;
|
||||||
|
import org.mapstruct.ReportingPolicy;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||||
|
public abstract class ObjectFactoryBookMapper {
|
||||||
|
|
||||||
|
public static final ObjectFactoryBookMapper INSTANCE = Mappers.getMapper( ObjectFactoryBookMapper.class );
|
||||||
|
|
||||||
|
public abstract TargetBook mapBook(SourceBook sourceBook, String authorFirstName, String authorLastName);
|
||||||
|
|
||||||
|
@ObjectFactory
|
||||||
|
protected TargetBook createTargetBook(String authorFirstName, String authorLastName) {
|
||||||
|
TargetBook targetBook = new TargetBook();
|
||||||
|
targetBook.setAuthorFirstName( authorFirstName );
|
||||||
|
targetBook.setAuthorLastName( authorLastName );
|
||||||
|
|
||||||
|
return targetBook;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1457;
|
||||||
|
|
||||||
|
public class SourceBook {
|
||||||
|
private String isbn;
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
public String getIsbn() {
|
||||||
|
return isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsbn(String isbn) {
|
||||||
|
this.isbn = isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1457;
|
||||||
|
|
||||||
|
public class TargetBook {
|
||||||
|
|
||||||
|
private String isbn;
|
||||||
|
private String title;
|
||||||
|
private String authorFirstName;
|
||||||
|
private String authorLastName;
|
||||||
|
|
||||||
|
private boolean afterMappingWithoutAuthorName;
|
||||||
|
private String afterMappingWithOnlyFirstName;
|
||||||
|
private String afterMappingWithOnlyLastName;
|
||||||
|
private boolean afterMappingWithDifferentVariableName;
|
||||||
|
|
||||||
|
public String getIsbn() {
|
||||||
|
return isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsbn(String isbn) {
|
||||||
|
this.isbn = isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthorFirstName() {
|
||||||
|
return authorFirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorFirstName(String authorFirstName) {
|
||||||
|
this.authorFirstName = authorFirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthorLastName() {
|
||||||
|
return authorLastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorLastName(String authorLastName) {
|
||||||
|
this.authorLastName = authorLastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAfterMappingWithoutAuthorName() {
|
||||||
|
return afterMappingWithoutAuthorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAfterMappingWithoutAuthorName(boolean afterMappingWithoutAuthorName) {
|
||||||
|
this.afterMappingWithoutAuthorName = afterMappingWithoutAuthorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAfterMappingWithOnlyFirstName() {
|
||||||
|
return afterMappingWithOnlyFirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAfterMappingWithOnlyFirstName(String afterMappingWithOnlyFirstName) {
|
||||||
|
this.afterMappingWithOnlyFirstName = afterMappingWithOnlyFirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAfterMappingWithOnlyLastName() {
|
||||||
|
return afterMappingWithOnlyLastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAfterMappingWithOnlyLastName(String afterMappingWithOnlyLastName) {
|
||||||
|
this.afterMappingWithOnlyLastName = afterMappingWithOnlyLastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAfterMappingWithDifferentVariableName() {
|
||||||
|
return afterMappingWithDifferentVariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAfterMappingWithDifferentVariableName(boolean afterMappingWithDifferentVariableName) {
|
||||||
|
this.afterMappingWithDifferentVariableName = afterMappingWithDifferentVariableName;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1738;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface Issue1738Mapper {
|
||||||
|
|
||||||
|
Issue1738Mapper INSTANCE = Mappers.getMapper( Issue1738Mapper.class );
|
||||||
|
|
||||||
|
@Mapping(target = "value", source = "nested.value")
|
||||||
|
Target map(Source source);
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1738;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@IssueKey("1738")
|
||||||
|
@WithClasses({
|
||||||
|
Issue1738Mapper.class,
|
||||||
|
Source.class,
|
||||||
|
Target.class
|
||||||
|
})
|
||||||
|
public class Issue1738Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGenerateCorrectSourceNestedMapping() {
|
||||||
|
Source source = new Source();
|
||||||
|
Source.Nested<Number> nested = new Source.Nested<>();
|
||||||
|
source.setNested( nested );
|
||||||
|
nested.setValue( 100L );
|
||||||
|
|
||||||
|
Target target = Issue1738Mapper.INSTANCE.map( source );
|
||||||
|
|
||||||
|
assertThat( target.getValue() ).isEqualTo( 100L );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1738;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Source {
|
||||||
|
|
||||||
|
private Nested<? extends Number> nested;
|
||||||
|
|
||||||
|
public Nested<? extends Number> getNested() {
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNested(Nested<? extends Number> nested) {
|
||||||
|
this.nested = nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Nested<T> {
|
||||||
|
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1738;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Target {
|
||||||
|
|
||||||
|
private Number value;
|
||||||
|
|
||||||
|
public Number getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Number value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1742;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface Issue1742Mapper {
|
||||||
|
|
||||||
|
void update(@MappingTarget Target target, Source source);
|
||||||
|
}
|
@ -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.bugs._1742;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("1742")
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@WithClasses( {
|
||||||
|
Issue1742Mapper.class,
|
||||||
|
NestedSource.class,
|
||||||
|
NestedTarget.class,
|
||||||
|
Source.class,
|
||||||
|
Target.class,
|
||||||
|
} )
|
||||||
|
public class Issue1742Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCompile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1742;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class NestedSource {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1742;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class NestedTarget {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public NestedTarget() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public NestedTarget(Builder builder) {
|
||||||
|
this.value = getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public Builder value(String value) {
|
||||||
|
this.value = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NestedTarget create() {
|
||||||
|
return new NestedTarget(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1742;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Source {
|
||||||
|
|
||||||
|
private NestedSource nested;
|
||||||
|
|
||||||
|
public NestedSource getNested() {
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNested(NestedSource nested) {
|
||||||
|
this.nested = nested;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1742;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Target {
|
||||||
|
|
||||||
|
private NestedTarget nested;
|
||||||
|
|
||||||
|
public NestedTarget getNested() {
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNested(NestedTarget nested) {
|
||||||
|
this.nested = nested;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1751;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Holder<T> {
|
||||||
|
|
||||||
|
private final T value;
|
||||||
|
|
||||||
|
public Holder(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If empty is considered as a builder creation method, this method would be the build method and would
|
||||||
|
// lead to a stackoverflow
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public Holder<T> duplicate() {
|
||||||
|
return new Holder<>( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method should not be considered as builder creation method
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static <V> Holder<V> empty() {
|
||||||
|
return new Holder<>( null );
|
||||||
|
}
|
||||||
|
}
|
@ -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.ap.test.bugs._1751;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface Issue1751Mapper {
|
||||||
|
|
||||||
|
Issue1751Mapper INSTANCE = Mappers.getMapper( Issue1751Mapper.class );
|
||||||
|
|
||||||
|
Target map(Source source);
|
||||||
|
|
||||||
|
default Holder<Target> mapToHolder(Source source) {
|
||||||
|
return new Holder<>( this.map( source ) );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1751;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("1772")
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@WithClasses({
|
||||||
|
Holder.class,
|
||||||
|
Issue1751Mapper.class,
|
||||||
|
Source.class,
|
||||||
|
Target.class
|
||||||
|
})
|
||||||
|
public class Issue1751Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void name() {
|
||||||
|
Source source = new Source();
|
||||||
|
source.setValue( "some value" );
|
||||||
|
|
||||||
|
Holder<Target> targetHolder = Issue1751Mapper.INSTANCE.mapToHolder( source );
|
||||||
|
|
||||||
|
assertThat( targetHolder.getValue() )
|
||||||
|
.extracting( Target::getValue )
|
||||||
|
.isEqualTo( "some value" );
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1751;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Source {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1751;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Target {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1772;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.ReportingPolicy;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sjaak Derksen
|
||||||
|
*/
|
||||||
|
@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR)
|
||||||
|
public interface Issue1772Mapper {
|
||||||
|
|
||||||
|
Issue1772Mapper INSTANCE = Mappers.getMapper( Issue1772Mapper.class );
|
||||||
|
|
||||||
|
@Mapping(target = "nestedTarget.doubleNestedTarget", source = "nestedSource.doublyNestedSourceField" )
|
||||||
|
Target map(Source source);
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1772;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sjaak Derksen
|
||||||
|
*/
|
||||||
|
@IssueKey("1772")
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@WithClasses({
|
||||||
|
Issue1772Mapper.class,
|
||||||
|
Source.class,
|
||||||
|
Target.class,
|
||||||
|
})
|
||||||
|
public class Issue1772Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCorrectlyMarkSourceAsUsed() {
|
||||||
|
|
||||||
|
Source source = new Source();
|
||||||
|
source.setNestedSource( new Source.NestedSource() );
|
||||||
|
source.getNestedSource().setDoublyNestedSourceField( 5d );
|
||||||
|
|
||||||
|
Target target = Issue1772Mapper.INSTANCE.map( source );
|
||||||
|
|
||||||
|
assertThat( target.getNestedTarget().getDoubleNestedTarget() ).isEqualTo( 5d );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1772;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sjaak Derksen
|
||||||
|
*/
|
||||||
|
public class Source {
|
||||||
|
|
||||||
|
private NestedSource nestedSource;
|
||||||
|
|
||||||
|
public NestedSource getNestedSource() {
|
||||||
|
return nestedSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNestedSource(NestedSource nestedSource) {
|
||||||
|
this.nestedSource = nestedSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NestedSource {
|
||||||
|
|
||||||
|
private double doublyNestedSourceField;
|
||||||
|
|
||||||
|
public double getDoublyNestedSourceField() {
|
||||||
|
return doublyNestedSourceField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDoublyNestedSourceField(double doublyNestedSourceField) {
|
||||||
|
this.doublyNestedSourceField = doublyNestedSourceField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1772;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sjaak Derksen
|
||||||
|
*/
|
||||||
|
public class Target {
|
||||||
|
|
||||||
|
private NestedTarget nestedTarget;
|
||||||
|
|
||||||
|
public NestedTarget getNestedTarget() {
|
||||||
|
return nestedTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNestedTarget(NestedTarget nestedTarget) {
|
||||||
|
this.nestedTarget = nestedTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NestedTarget {
|
||||||
|
|
||||||
|
private double doubleNestedTarget;
|
||||||
|
|
||||||
|
public double getDoubleNestedTarget() {
|
||||||
|
return doubleNestedTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDoubleNestedTarget(double doubleNestedTarget) {
|
||||||
|
this.doubleNestedTarget = doubleNestedTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1790;
|
||||||
|
|
||||||
|
import org.mapstruct.MapperConfig;
|
||||||
|
import org.mapstruct.NullValueCheckStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@MapperConfig(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
|
||||||
|
public interface Issue1790Config {
|
||||||
|
}
|
@ -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.bugs._1790;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper(config = Issue1790Config.class, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||||
|
public interface Issue1790Mapper {
|
||||||
|
|
||||||
|
Issue1790Mapper INSTANCE = Mappers.getMapper( Issue1790Mapper.class );
|
||||||
|
|
||||||
|
void toExistingCar(@MappingTarget Target target, Source source);
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1790;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("1790")
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@WithClasses({
|
||||||
|
Issue1790Config.class,
|
||||||
|
Issue1790Mapper.class,
|
||||||
|
Source.class,
|
||||||
|
Target.class,
|
||||||
|
})
|
||||||
|
public class Issue1790Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldProperlyApplyNullValuePropertyMappingStrategyWhenInheriting() {
|
||||||
|
Target target = new Target();
|
||||||
|
target.setName( "My name is set" );
|
||||||
|
|
||||||
|
Source source = new Source();
|
||||||
|
|
||||||
|
Issue1790Mapper.INSTANCE.toExistingCar( target, source );
|
||||||
|
|
||||||
|
assertThat( target.getName() ).isEqualTo( "My name is set" );
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1790;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Source {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1790;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Target {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1797;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Customer {
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
ONE, TWO
|
||||||
|
}
|
||||||
|
|
||||||
|
private final EnumSet<Type> types;
|
||||||
|
|
||||||
|
public Customer(EnumSet<Type> types) {
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumSet<Type> getTypes() {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1797;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class CustomerDto {
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
ONE, TWO
|
||||||
|
}
|
||||||
|
|
||||||
|
private EnumSet<Type> types;
|
||||||
|
|
||||||
|
public EnumSet<Type> getTypes() {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTypes(EnumSet<Type> types) {
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1797;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface Issue1797Mapper {
|
||||||
|
|
||||||
|
Issue1797Mapper INSTANCE = Mappers.getMapper( Issue1797Mapper.class );
|
||||||
|
|
||||||
|
CustomerDto map(Customer customer);
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.bugs._1797;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("1797")
|
||||||
|
@RunWith(AnnotationProcessorTestRunner.class)
|
||||||
|
@WithClasses({
|
||||||
|
Customer.class,
|
||||||
|
CustomerDto.class,
|
||||||
|
Issue1797Mapper.class
|
||||||
|
})
|
||||||
|
public class Issue1797Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCorrectlyMapEnumSetToEnumSet() {
|
||||||
|
|
||||||
|
Customer customer = new Customer( EnumSet.of( Customer.Type.ONE ) );
|
||||||
|
|
||||||
|
CustomerDto customerDto = Issue1797Mapper.INSTANCE.map( customer );
|
||||||
|
|
||||||
|
assertThat( customerDto.getTypes() ).containsExactly( CustomerDto.Type.ONE );
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._1799;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface Issue1799Mapper {
|
||||||
|
|
||||||
|
Issue1799Mapper INSTANCE = Mappers.getMapper( Issue1799Mapper.class );
|
||||||
|
|
||||||
|
Target map(Source source);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user