Compare commits

...

38 Commits

Author SHA1 Message Date
Filip Hrisafov
0f92764de9 [maven-release-plugin] prepare release 1.5.5.Final 2023-04-23 22:02:15 +02:00
Johnny Lim
f1edbd25c2 Polish links in docs (#3214) 2023-04-22 19:25:38 +02:00
Filip Hrisafov
75fb02a19f #3248 BeanMapping#ignoreUnmappedSourceProperties should be inherited for @InheritConfiguration 2023-04-22 19:21:02 +02:00
Iaroslav Bogdanchikov
922258fea7 #2730 Add support for Jakarta XML Binding 2023-04-22 18:00:12 +02:00
Filip Hrisafov
b657209b12 #3236 Add missing jakarta-cdi to the documentation 2023-04-22 17:46:48 +02:00
Filip Hrisafov
85b2a18b3c #3112 Add missing brackets 2023-04-22 17:46:05 +02:00
Filip Hrisafov
7a0ed4e409 [maven-release-plugin] prepare for next development iteration 2023-04-22 17:42:16 +02:00
Filip Hrisafov
35228b3cb7 [maven-release-plugin] prepare release 1.5.4.Final 2023-04-13 23:23:46 +02:00
Filip Hrisafov
998b3e96d7 #2950 Disable CDI in the full features tests on Java 8 2023-04-13 22:44:39 +02:00
Filip Hrisafov
b9c6256d3c #2950 Add support for Jakarta CDI 2023-04-13 22:44:39 +02:00
Filip Hrisafov
a5f57a77cf #3142 Nested forged methods should declare throws from lifecycle methods 2023-04-13 22:42:45 +02:00
Orange Add
257796b959 #3040: Allow using only BeanMapping#mappingControl 2023-04-13 22:33:38 +02:00
Filip Hrisafov
9794543946 #3135 BeanMapping#mappingControl should be inherited by forged methods 2023-04-13 22:32:59 +02:00
José Carlos Campanero Ortiz
53baf96126 #3112 Document <THROW_EXCEPTION> in the reference guide 2023-04-13 21:48:53 +02:00
Claudio Nave
a67e4e5f96 #3110 Fix throws declaration for ValueMapping annotated methods (#3122)
#3110 Fix throws declaration for ValueMapping annotated methods
2023-02-05 12:18:08 +01:00
Filip Hrisafov
50d96eb367 #3077 Add test case 2022-11-13 14:21:43 +01:00
Zegveld
429cc3f914 #3057: limit do not allow self to subclassmappings. (#3063)
* #3057: limit do not allow self to subclassmappings.
* #3057: determine method candidates after all other fields are set in the constructor.

Co-authored-by: Ben Zegveld <Ben.Zegveld@gmail.com>
Co-authored-by: Filip Hrisafov <filip.hrisafov@gmail.com>
2022-11-05 13:15:35 +01:00
Filip Hrisafov
60026437e4 [maven-release-plugin] prepare for next development iteration 2022-10-07 20:10:27 +02:00
Filip Hrisafov
c4e3320b98 [maven-release-plugin] prepare release 1.5.3.Final 2022-10-07 20:10:26 +02:00
Filip Hrisafov
6f262ef405 #3036 Fix compile errors when intersection types are used in lifecycle methods 2022-10-03 21:15:43 +02:00
Zegveld
7cfac7c060 #2955 Fix @AfterMapping with return type not called for update mappings 2022-10-02 09:36:06 +02:00
Filip Hrisafov
9d2bed09ca #2743 BeanMappingOptions should not be inherited for forged methods 2022-09-29 22:11:04 +02:00
Zegveld
a98986c08e #3018: Use MappingControl with SubclassMapping 2022-09-29 21:36:39 +02:00
Filip Hrisafov
73f70b1564 #2840, #2913, #2921: MethodMatcher should not match widening methods
In the MethodMatcher we need to do a special check when the target type is primitive.
The reason for that is that a Long is assignable to a primitive double.
However, doing that means that information can be lost and thus we should not pick such methods.
When the target type is primitive, then a method will be matched if and only if boxed equivalent of the target type is assignable to the boxed equivalent of the candidate return type
2022-09-28 18:11:19 +02:00
Johnny Lim
b1eda5a04e Javadoc and documentation polishing (#3026) 2022-09-26 19:02:39 +02:00
Prasanth Omanakuttan
45e01fea91 Update Typos in java-doc
Closes #2989
2022-09-26 18:52:56 +02:00
Orange Add
473d581528 #2825 Fix SubclassMapping stackoverflow exception 2022-09-26 18:52:08 +02:00
Filip Hrisafov
290189652c #2990 Stabilise top level imports 2022-09-26 18:51:20 +02:00
Filip Hrisafov
51e67ebca4 #2925 Fix IllegalArgumentException when resolving generic parameters
When resolving the parameter for a method like:

```
<T> Optional<T> from(T value)
```

There was an exception in javac because getting a DeclaredType from an Optional
with a primitive type argument throws an exception.
Therefore, when assigning the type arguments we get the boxed equivalent.
This problem does not happen in the Eclipse compiler
2022-09-26 18:50:39 +02:00
Filip Hrisafov
1c3c46f1ef #2907 Add test case for nested import of array 2022-09-26 18:50:07 +02:00
Filip Hrisafov
383ed23ed2 #2945 Stabilise top level imports
Make sure that GeneratedType always gets the imported types
from a Type before adding them
2022-09-26 18:49:43 +02:00
Filip Hrisafov
d0e9d69be1 #2937 Fix conditional check for collections with adders 2022-09-26 18:48:54 +02:00
Filip Hrisafov
082b36a50a #2897 Always import types defined in Mapper#imports 2022-09-26 18:46:46 +02:00
Filip Hrisafov
febed7ea1c #2928 Add IntelliJ and Eclipse plugin information 2022-09-26 18:46:24 +02:00
Filip Hrisafov
3a94eb80b0 #2949 Do not inverse inherit BeanMapping#ignoreUnmappedSourceProperties 2022-09-26 18:45:54 +02:00
Filip Hrisafov
d9ad48154a #2974 Fix typos in documentation
Closes #2974
2022-09-26 18:45:29 +02:00
Hakan
e5c7fdb2f6 #2839 Keep thrown types when creating a new ForgedMethod with the same arguments
This fixes a compilation error when mapping fields with the same type due to not wrapping in a `try-catch` block
2022-09-26 18:45:03 +02:00
Prasanth Omanakuttan
e3f9a1ccd5 Update Typos in javadoc (#2958) 2022-09-26 18:43:53 +02:00
168 changed files with 4105 additions and 151 deletions

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

View File

@ -98,7 +98,7 @@ public @interface EnumMapping {
* <ul>
* <li>{@link MappingConstants#SUFFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a
* suffix to the source enum</li>
* <li>{@link MappingConstants#STRIP_SUFFIX_TRANSFORMATION} - strips the the given {@link #configuration()}
* <li>{@link MappingConstants#STRIP_SUFFIX_TRANSFORMATION} - strips the given {@link #configuration()}
* from the end of the source enum</li>
* <li>{@link MappingConstants#PREFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a
* prefix to the source enum</li>

View File

@ -81,8 +81,8 @@ import java.lang.annotation.Target;
public @interface InheritInverseConfiguration {
/**
* The name of the inverse mapping method to inherit the mappings from. Needs only to be specified in case more than
* one inverse method with matching source and target type exists.
* The name of the inverse mapping method to inherit the mappings from. Needs to be specified only in case more than
* one inverse method exists with a matching source and target type exists.
*
* @return The name of the inverse mapping method to inherit the mappings from.
*/

View File

@ -298,7 +298,7 @@ public @interface Mapper {
* Can be configured by the {@link MapperConfig#disableSubMappingMethodsGeneration()} as well.
* <p>
* Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at
* <a href="http://mapstruct.org">mapstruct.org</a> or
* <a href="https://mapstruct.org">mapstruct.org</a> or
* <a href="https://github.com/mapstruct/mapstruct">github.com/mapstruct/mapstruct</a> to share what problem you
* are facing with the automatic sub-mapping generation.
*

View File

@ -269,7 +269,7 @@ public @interface MapperConfig {
* Can be overridden by {@link Mapper#disableSubMappingMethodsGeneration()}
* <p>
* Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at
* <a href="http://mapstruct.org">mapstruct.org</a> or
* <a href="https://mapstruct.org">mapstruct.org</a> or
* <a href="https://github.com/mapstruct/mapstruct">github.com/mapstruct/mapstruct</a> to share what problem you
* are facing with the automatic sub-mapping generation.
*

View File

@ -109,7 +109,12 @@ public final class MappingConstants {
public static final String DEFAULT = "default";
/**
* The generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
* The generated mapper is an application-scoped CDI bean and can be retrieved via @Inject.
* The annotations are either from {@code javax} or {@code jakarta}.
* Priority have the {@code javax} annotations.
* In case you want to only use Jakarta then use {@link #JAKARTA_CDI}.
*
* @see #JAKARTA_CDI
*/
public static final String CDI = "cdi";
@ -138,6 +143,12 @@ public final class MappingConstants {
*/
public static final String JAKARTA = "jakarta";
/**
* The generated mapper is an application-scoped Jakarta CDI bean and can be retrieved via @Inject.
* @see #CDI
*/
public static final String JAKARTA_CDI = "jakarta-cdi";
}
}

View File

@ -8,7 +8,7 @@ package org.mapstruct;
/**
* Strategy for dealing with null source values.
*
* <b>Note:</b> This strategy is not in effect when the a specific source presence check method is defined
* <b>Note:</b> This strategy is not in effect when a specific source presence check method is defined
* in the service provider interface (SPI).
* <p>
* <b>Note</b>: some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder

View File

@ -13,6 +13,6 @@
* This package contains several annotations which allow to configure how mapper interfaces are generated.
* </p>
*
* @see <a href="http://mapstruct.org/">MapStruct reference documentation</a>
* @see <a href="https://mapstruct.org/">MapStruct reference documentation</a>
*/
package org.mapstruct;

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -39,6 +39,13 @@
<groupId>org.mapstruct.tools.gem</groupId>
<artifactId>gem-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
@ -93,7 +100,7 @@
<doctitle>MapStruct ${project.version}</doctitle>
<windowtitle>MapStruct ${project.version}</windowtitle>
<bottom>
<![CDATA[Copyright &copy; ${project.inceptionYear}-{currentYear} <a href="http://mapstruct.org/">MapStruct Authors</a>; All rights reserved. Released under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache Software License 2.0</a>.]]>
<![CDATA[Copyright &copy; ${project.inceptionYear}-{currentYear} <a href="https://mapstruct.org/">MapStruct Authors</a>; All rights reserved. Released under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache Software License 2.0</a>.]]>
</bottom>
<groups>
@ -187,7 +194,6 @@
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

View File

@ -292,7 +292,7 @@ The source presence checker name can be changed in the MapStruct service provide
[NOTE]
====
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor see `CollectionMappingStrategy`, MapStruct will always generate a source property
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor (see `CollectionMappingStrategy`), MapStruct will always generate a source property
null check, regardless the value of the `NullValueCheckStrategy` to avoid addition of `null` to the target collection or map.
====

View File

@ -130,7 +130,7 @@ You can find a complete example in the https://github.com/mapstruct/mapstruct-ex
The MapStruct code generator can be configured using _annotation processor options_.
When invoking javac directly, these options are passed to the compiler in the form _-Akey=value_. When using MapStruct via Maven, any processor options can be passed using an `options` element within the configuration of the Maven processor plug-in like this:
When invoking javac directly, these options are passed to the compiler in the form _-Akey=value_. When using MapStruct via Maven, any processor options can be passed using `compilerArgs` within the configuration of the Maven processor plug-in like this:
.Maven configuration
====
@ -215,10 +215,11 @@ suppressGeneratorVersionInfoComment`
Supported values are:
* `default`: the mapper uses no component model, instances are typically retrieved via `Mappers#getMapper(Class)`
* `cdi`: the generated mapper is an application-scoped CDI bean and can be retrieved via `@Inject`
* `cdi`: the generated mapper is an application-scoped (from javax.enterprise.context or jakarta.enterprise.context, depending on which one is available with javax.inject having priority) CDI bean and can be retrieved via `@Inject`
* `spring`: the generated mapper is a singleton-scoped Spring bean and can be retrieved via `@Autowired`
* `jsr330`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from javax.inject or jakarta.inject, depending which one is available with javax.inject having priority), e.g. using Spring
* `jakarta`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from jakarta.inject), e.g. using Spring
* `jakarta-cdi`: the generated mapper is an application-scoped (from jakarta.enterprise.context) CDI bean and can be retrieved via `@Inject`
If a component model is given for a specific mapper via `@Mapper#componentModel()`, the value from the annotation takes precedence.
|`default`
@ -273,3 +274,28 @@ disableBuilders`
MapStruct can be used with Java 9 and higher versions.
To allow usage of the `@Generated` annotation `java.annotation.processing.Generated` (part of the `java.compiler` module) can be enabled.
=== IDE Integration
There are optional MapStruct plugins for IntelliJ and Eclipse that allow you to have additional completion support (and more) in the annotations.
==== IntelliJ
The https://plugins.jetbrains.com/plugin/10036-mapstruct-support[MapStruct IntelliJ] plugin offers assistance in projects that use MapStruct.
Some features include:
* Code completion in `target`, `source`, `expression`
* Go To Declaration for properties in `target` and `source`
* Find Usages of properties in `target` and `source`
* Refactoring support
* Errors and Quick Fixes
==== Eclipse
The https://marketplace.eclipse.org/content/mapstruct-eclipse-plugin[MapStruct Eclipse] Plugin offers assistance in projects that use MapStruct.
Some features include:
* Code completion in `target` and `source`
* Quick Fixes

View File

@ -65,7 +65,7 @@ public class OrderMapperImpl implements OrderMapper {
----
====
By default an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@ValueMapping`. This ensures that all constants are mapped in a safe and predictable manner. The generated
mapping method will throw an IllegalStateException if for some reason an unrecognized source value occurs.
mapping method will throw an `IllegalStateException` if for some reason an unrecognized source value occurs.
MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings and only applies to the source. It comes in two flavors: `<ANY_REMAINING>` and `<ANY_UNMAPPED>`. They cannot be used at the same time.
@ -75,14 +75,18 @@ MapStruct will *not* attempt such name based mapping for `<ANY_UNMAPPED>` and di
MapStruct is able to handle `null` sources and `null` targets by means of the `<NULL>` keyword.
In addition, the constant value `<THROW_EXCEPTION>` can be used for throwing an exception for particular value mappings. This value is only applicable to `ValueMapping#target()` and not `ValueMapping#source()` since MapStruct can't map from exceptions.
[TIP]
====
Constants for `<ANY_REMAINING>`, `<ANY_UNMAPPED>` and `<NULL>` are available in the `MappingConstants` class.
Constants for `<ANY_REMAINING>`, `<ANY_UNMAPPED>`, `<THROW_EXCEPTION>` and `<NULL>` are available in the `MappingConstants` class.
====
Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. `<ANY_REMAINING>` and `<ANY_UNMAPPED>` will be ignored in that case.
.Enum mapping method, <NULL> and <ANY_REMAINING>
The following code snippets exemplify the use of the aforementioned constants.
.Enum mapping method, `<NULL>` and `<ANY_REMAINING>`
====
[source, java, linenums]
[subs="verbatim,attributes"]
@ -102,7 +106,7 @@ public interface SpecialOrderMapper {
----
====
.Enum mapping method result, <NULL> and <ANY_REMAINING>
.Enum mapping method result, `<NULL>` and `<ANY_REMAINING>`
====
[source, java, linenums]
[subs="verbatim,attributes"]
@ -137,6 +141,55 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper {
*Note:* MapStruct would have refrained from mapping the `RETAIL` and `B2B` when `<ANY_UNMAPPED>` was used instead of `<ANY_REMAINING>`.
.Enum mapping method with `<THROW_EXCEPTION>`
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
@Mapper
public interface SpecialOrderMapper {
SpecialOrderMapper INSTANCE = Mappers.getMapper( SpecialOrderMapper.class );
@ValueMappings({
@ValueMapping( source = "STANDARD", target = "DEFAULT" ),
@ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION )
})
ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
----
====
.Enum mapping method with `<THROW_EXCEPTION>` result
====
[source, java, linenums]
[subs="verbatim,attributes"]
----
// GENERATED CODE
public class SpecialOrderMapperImpl implements SpecialOrderMapper {
@Override
public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType;
switch ( orderType ) {
case STANDARD: externalOrderType = ExternalOrderType.DEFAULT;
break;
case C2C: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType );
default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return externalOrderType;
}
}
----
====
[WARNING]
====
The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead.
@ -152,6 +205,7 @@ MapStruct supports enum to a String mapping along the same lines as is described
2. Similarity: `<ANY_UNMAPPED`> stops after handling defined mapping and proceeds to the switch/default clause value.
3. Difference: `<ANY_REMAINING>` will result in an error. It acts on the premise that there is name similarity between enum constants in source and target which does not make sense for a String type.
4. Difference: Given 1. and 3. there will never be unmapped values.
5. Similarity: `<THROW_EXCEPTION>` can be used for throwing an exception for particular enum values.
*`String` to enum*
@ -159,6 +213,7 @@ MapStruct supports enum to a String mapping along the same lines as is described
2. Similarity: `<ANY_UNMAPPED`> stops after handling defined mapping and proceeds to the switch/default clause value.
3. Similarity: `<ANY_REMAINING>` will create a mapping for each target enum constant and proceed to the switch/default clause value.
4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `<ANY_REMAINING>` or `<ANY_UNMAPPED`> will result in a warning.
5. Similarity: `<THROW_EXCEPTION>` can be used for throwing an exception for any arbitrary `String` value.
=== Custom name transformation

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -133,8 +133,8 @@
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</profile>

View File

@ -28,6 +28,14 @@ public final class FullFeatureCompilationExclusionCliEnhancer implements Process
additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1801/*.java" );
switch ( currentJreVersion ) {
case JAVA_8:
if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) {
additionalExcludes.add(
"org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" );
}
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" );
break;
case JAVA_9:
// TODO find out why this fails:
additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" );

View File

@ -75,6 +75,10 @@ public class MavenIntegrationTest {
void jaxbTest() {
}
@ProcessorTest(baseDir = "jakartaJaxbTest")
void jakartaJaxbTest() {
}
@ProcessorTest(baseDir = "jsr330Test")
void jsr330Test() {
}

View File

@ -64,6 +64,10 @@
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>
<!-- Spring -->
<dependency>
@ -79,6 +83,13 @@
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<profiles>
@ -89,14 +100,16 @@
</activation>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</profile>

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>jakartaJaxbTest</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>xjc</id>
<phase>initialize</phase>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<xjbSources>
<xjbSource>${project.build.resources[0].directory}/binding</xjbSource>
</xjbSources>
<sources>
<source>${project.build.resources[0].directory}/schema</source>
</sources>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,42 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
import java.util.List;
/**
* @author Sjaak Derksen
*/
public class OrderDetailsDto {
private String name;
private List<String> description;
private OrderStatusDto status;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getDescription() {
return description;
}
public void setDescription(List<String> description) {
this.description = description;
}
public OrderStatusDto getStatus() {
return status;
}
public void setStatus(OrderStatusDto status) {
this.status = status;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
import java.util.Date;
/**
* @author Sjaak Derksen
*/
public class OrderDto {
private Long orderNumber;
private Date orderDate;
private OrderDetailsDto orderDetails;
private ShippingAddressDto shippingAddress;
public Long getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(Long orderNumber) {
this.orderNumber = orderNumber;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public OrderDetailsDto getOrderDetails() {
return orderDetails;
}
public void setOrderDetails(OrderDetailsDto orderDetails) {
this.orderDetails = orderDetails;
}
public ShippingAddressDto getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(ShippingAddressDto shippingAddress) {
this.shippingAddress = shippingAddress;
}
}

View File

@ -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.itest.jakarta.jaxb;
/**
* @author Sjaak Derksen
*/
public enum OrderStatusDto {
ORDERED( "small" ),
PROCESSED( "medium" ),
DELIVERED( "large" );
private final String value;
OrderStatusDto(String v) {
value = v;
}
public String value() {
return value;
}
public static OrderStatusDto fromValue(String v) {
for ( OrderStatusDto c : OrderStatusDto.values() ) {
if ( c.value.equals( v ) ) {
return c;
}
}
throw new IllegalArgumentException( v );
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
/**
* @author Sjaak Derksen
*/
public class ShippingAddressDto {
private String street;
private String houseNumber;
private String city;
private String country;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getHouseNumber() {
return houseNumber;
}
public void setHouseNumber(String houseNumber) {
this.houseNumber = houseNumber;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderDetailsType;
import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderType;
import org.mapstruct.itest.jakarta.jaxb.xsd.test2.OrderStatusType;
import org.mapstruct.itest.jakarta.jaxb.xsd.test2.ShippingAddressType;
import org.mapstruct.itest.jakarta.jaxb.xsd.underscores.SubType;
/**
* @author Sjaak Derksen
*/
@Mapper(uses = {
org.mapstruct.itest.jakarta.jaxb.xsd.test1.ObjectFactory.class,
org.mapstruct.itest.jakarta.jaxb.xsd.test2.ObjectFactory.class,
org.mapstruct.itest.jakarta.jaxb.xsd.underscores.ObjectFactory.class
})
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
// source 2 target methods
OrderDto sourceToTarget(OrderType source);
OrderDetailsDto detailsToDto(OrderDetailsType source);
OrderStatusDto statusToDto(OrderStatusType source);
ShippingAddressDto shippingAddressToDto(ShippingAddressType source);
SubTypeDto subTypeToDto(SubType source);
// target 2 source methods
OrderType targetToSource(OrderDto target);
OrderDetailsType dtoToDetails(OrderDetailsDto target);
OrderStatusType dtoToStatus(OrderStatusDto target);
ShippingAddressType dtoToShippingAddress(ShippingAddressDto source);
SubType dtoToSubType(SubTypeDto source);
}

View File

@ -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.itest.jakarta.jaxb;
public class SubTypeDto extends SuperTypeDto {
private String declaredCamelCase;
private String declaredUnderscore;
public String getDeclaredCamelCase() {
return declaredCamelCase;
}
public void setDeclaredCamelCase(String declaredCamelCase) {
this.declaredCamelCase = declaredCamelCase;
}
public String getDeclaredUnderscore() {
return declaredUnderscore;
}
public void setDeclaredUnderscore(String declaredUnderscore) {
this.declaredUnderscore = declaredUnderscore;
}
}

View File

@ -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.itest.jakarta.jaxb;
public class SuperTypeDto {
private String inheritedCamelCase;
private String inheritedUnderscore;
public String getInheritedCamelCase() {
return inheritedCamelCase;
}
public void setInheritedCamelCase(String inheritedCamelCase) {
this.inheritedCamelCase = inheritedCamelCase;
}
public String getInheritedUnderscore() {
return inheritedUnderscore;
}
public void setInheritedUnderscore(String inheritedUnderscore) {
this.inheritedUnderscore = inheritedUnderscore;
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<jaxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc"
version="3.0" >
<jaxb:globalBindings
fixedAttributeAsConstantProperty="true"
typesafeEnumBase="xs:string"
typesafeEnumMemberName="generateName"
generateIsSetMethod="true"
generateElementProperty="true" >
<xjc:noValidator/>
<xjc:noValidatingUnmarshaller/>
</jaxb:globalBindings>
</jaxb:bindings>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:test1="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/test1"
xmlns:test2="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/test2"
targetNamespace="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/test1"
elementFormDefault="qualified" version="1.0.0">
<import namespace="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/test2" schemaLocation="test2.xsd"/>
<element name="Order" type="test1:OrderType" />
<complexType name="OrderType">
<sequence>
<element name="orderNumber" type="long"/>
<element name="orderDate" type="dateTime"/>
<element name="orderDetails" type="test1:OrderDetailsType"/>
<element name="shippingAddress" type="test2:ShippingAddressType"/>
</sequence>
</complexType>
<element name="OrderDetails" type="test1:OrderDetailsType" />
<complexType name="OrderDetailsType">
<sequence>
<element name="name" type="string"/>
<element name="description" minOccurs="1" maxOccurs="5" type="string"/>
<element name="status" type="test2:OrderStatusType"/>
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:test2="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/test2"
targetNamespace="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/test2"
elementFormDefault="qualified" version="1.0.0">
<element name="OrderStatus" type="test2:OrderStatusType" />
<simpleType name="OrderStatusType">
<restriction base="string">
<enumeration value="ordered" />
<enumeration value="processed" />
<enumeration value="delivered" />
</restriction>
</simpleType>
<element name="ShippingAddress" type="test2:ShippingAddressType" />
<complexType name="ShippingAddressType">
<sequence>
<element name="street" type="string"/>
<element name="houseNumber" type="string"/>
<element name="city" type="string"/>
<element name="country" type="string"/>
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:underscores="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/underscores"
targetNamespace="http://www.mapstruct.org/itest/jakarta/jaxb/xsd/underscores"
elementFormDefault="qualified" version="1.0.0">
<element name="Super" type="underscores:SuperType" />
<complexType name="SuperType">
<sequence>
<element name="inheritedCamelCase" type="string"/>
<element name="inherited_underscore" type="string"/>
</sequence>
</complexType>
<element name="Sub" type="underscores:SubType" />
<complexType name="SubType">
<complexContent>
<extension base="underscores:SuperType">
<sequence>
<element name="declaredCamelCase" type="string"/>
<element name="declared_underscore" type="string"/>
</sequence>
</extension>
</complexContent>
</complexType>
</schema>

View File

@ -0,0 +1,118 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.jakarta.jaxb;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.ByteArrayOutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import org.junit.Test;
import org.mapstruct.itest.jakarta.jaxb.xsd.test1.ObjectFactory;
import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderType;
import org.mapstruct.itest.jakarta.jaxb.xsd.underscores.SubType;
/**
* Test for generation of Jakarta JAXB based mapper implementations.
*
* @author Iaroslav Bogdanchikov
*/
public class JakartaJaxbBasedMapperTest {
@Test
public void shouldMapJakartaJaxb() throws ParseException, JAXBException {
SourceTargetMapper mapper = SourceTargetMapper.INSTANCE;
OrderDto source1 = new OrderDto();
source1.setOrderDetails( new OrderDetailsDto() );
source1.setOrderNumber( 11L );
source1.setOrderDate( createDate( "31-08-1982 10:20:56" ) );
source1.setShippingAddress( new ShippingAddressDto() );
source1.getShippingAddress().setCity( "SmallTown" );
source1.getShippingAddress().setHouseNumber( "11a" );
source1.getShippingAddress().setStreet( "Awesome rd" );
source1.getShippingAddress().setCountry( "USA" );
source1.getOrderDetails().setDescription( new ArrayList<String>() );
source1.getOrderDetails().setName( "Shopping list for a Mapper" );
source1.getOrderDetails().getDescription().add( "1 MapStruct" );
source1.getOrderDetails().getDescription().add( "3 Lines of Code" );
source1.getOrderDetails().getDescription().add( "1 Dose of Luck" );
source1.getOrderDetails().setStatus( OrderStatusDto.ORDERED );
// map to JAXB
OrderType target = mapper.targetToSource( source1 );
// do a pretty print
ObjectFactory of = new ObjectFactory();
System.out.println( toXml( of.createOrder( target ) ) );
// map back from JAXB
OrderDto source2 = mapper.sourceToTarget( target );
// verify that source1 and source 2 are equal
assertThat( source2.getOrderNumber() ).isEqualTo( source1.getOrderNumber() );
assertThat( source2.getOrderDate() ).isEqualTo( source1.getOrderDate() );
assertThat( source2.getOrderDetails().getDescription().size() ).isEqualTo(
source1.getOrderDetails().getDescription().size()
);
assertThat( source2.getOrderDetails().getDescription().get( 0 ) ).isEqualTo(
source1.getOrderDetails().getDescription().get( 0 )
);
assertThat( source2.getOrderDetails().getDescription().get( 1 ) ).isEqualTo(
source1.getOrderDetails().getDescription().get( 1 )
);
assertThat( source2.getOrderDetails().getDescription().get( 2 ) ).isEqualTo(
source1.getOrderDetails().getDescription().get( 2 )
);
assertThat( source2.getOrderDetails().getName() ).isEqualTo( source1.getOrderDetails().getName() );
assertThat( source2.getOrderDetails().getStatus() ).isEqualTo( source1.getOrderDetails().getStatus() );
}
@Test
public void underscores() throws ParseException, JAXBException {
SourceTargetMapper mapper = SourceTargetMapper.INSTANCE;
SubTypeDto source1 = new SubTypeDto();
source1.setInheritedCamelCase("InheritedCamelCase");
source1.setInheritedUnderscore("InheritedUnderscore");
source1.setDeclaredCamelCase("DeclaredCamelCase");
source1.setDeclaredUnderscore("DeclaredUnderscore");
SubType target = mapper.dtoToSubType( source1 );
SubTypeDto source2 = mapper.subTypeToDto( target );
assertThat( source2.getInheritedCamelCase() ).isEqualTo( source1.getInheritedCamelCase() );
assertThat( source2.getInheritedUnderscore() ).isEqualTo( source1.getInheritedUnderscore() );
assertThat( source2.getDeclaredCamelCase() ).isEqualTo( source1.getDeclaredCamelCase() );
assertThat( source2.getDeclaredUnderscore() ).isEqualTo( source1.getDeclaredUnderscore() );
}
private Date createDate(String date) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat( "dd-M-yyyy hh:mm:ss" );
return sdf.parse( date );
}
private String toXml(JAXBElement<?> element) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance( element.getValue().getClass() );
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
marshaller.marshal( element, baos );
return baos.toString();
}
}

View File

@ -51,7 +51,7 @@
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
<version>${jaxb-runtime.version}</version>
</dependency>
</dependencies>
</plugin>
@ -66,14 +66,16 @@
</activation>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</profile>

View File

@ -11,12 +11,12 @@
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<packaging>pom</packaging>
<name>MapStruct Parent</name>
<description>An annotation processor for generating type-safe bean mappers</description>
<url>http://mapstruct.org/</url>
<url>https://mapstruct.org/</url>
<inceptionYear>2012</inceptionYear>
<properties>
@ -42,6 +42,7 @@
-->
<minimum.java.version>1.8</minimum.java.version>
<protobuf.version>3.21.2</protobuf.version>
<jaxb-runtime.version>2.3.2</jaxb-runtime.version>
</properties>
<licenses>
@ -70,7 +71,7 @@
<connection>scm:git:git://github.com/mapstruct/mapstruct.git</connection>
<developerConnection>scm:git:git@github.com:mapstruct/mapstruct.git</developerConnection>
<url>https://github.com/mapstruct/mapstruct/</url>
<tag>HEAD</tag>
<tag>1.5.5.Final</tag>
</scm>
<distributionManagement>
@ -160,6 +161,11 @@
<artifactId>cdi-api</artifactId>
<version>2.0.SP1</version>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
@ -250,6 +256,30 @@
<version>2.9</version>
</dependency>
<!-- XML Binding -->
<!-- Old (javax) -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${jaxb-runtime.version}</version>
</dependency>
<!-- New (jakarta) -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>3.0.2</version>
</dependency>
<!-- Plexus Eclipse Compiler -->
<dependency>
<groupId>org.eclipse.tycho</groupId>
@ -483,12 +513,12 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.17</version>
<version>1.20</version>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2.1</version>
<version>7.0</version>
</dependency>
</dependencies>
</plugin>

View File

@ -13,7 +13,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>parent/pom.xml</relativePath>
</parent>
@ -54,7 +54,7 @@
<connection>scm:git:git://github.com/mapstruct/mapstruct.git</connection>
<developerConnection>scm:git:git@github.com:mapstruct/mapstruct.git</developerConnection>
<url>https://github.com/mapstruct/mapstruct/</url>
<tag>HEAD</tag>
<tag>1.5.5.Final</tag>
</scm>
<profiles>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.5.5.Final</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@ -89,6 +89,11 @@
<artifactId>jakarta.inject-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<scope>test</scope>
</dependency>
<!-- plexus-container-default is a runtime-dependency of the tycho-compiler -->
<dependency>
@ -132,6 +137,13 @@
<artifactId>joda-time</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
@ -370,7 +382,6 @@
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

View File

@ -21,15 +21,15 @@ import org.mapstruct.ap.internal.util.Strings;
* </p>
* <p>
* In general each type comes with a "parse" method to convert a string to this particular type.
* For formatting a dedicated instance of {@link java.time.format.DateTimeFormatter} is used.
* For formatting a dedicated instance of {@link DateTimeFormatter} is used.
* </p>
* <p>
* If no date format for mapping is specified predefined ISO* formatters from
* {@link java.time.format.DateTimeFormatter} are used.
* {@link DateTimeFormatter} are used.
* </p>
* <p>
* An overview of date and time types shipped with Java 8 can be found at
* http://docs.oracle.com/javase/tutorial/datetime/iso/index.html.
* An overview of date and time types shipped with Java 8 can be found at the
* <a href="http://docs.oracle.com/javase/tutorial/datetime/iso/index.html">Standard Calendar Tutorial</a>
* </p>
*/
public abstract class AbstractJavaTimeToStringConversion extends SimpleConversion {

View File

@ -51,6 +51,8 @@ public final class MappingConstantsGem {
public static final String JSR330 = "jsr330";
public static final String JAKARTA = "jakarta";
public static final String JAKARTA_CDI = "jakarta-cdi";
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.internal.gem.jakarta;
import jakarta.xml.bind.annotation.XmlElementDecl;
import jakarta.xml.bind.annotation.XmlElementRef;
import org.mapstruct.tools.gem.GemDefinition;
/**
* This class is a temporary solution to an issue in the Gem Tools library.
*
* <p>
* This class can be merged with {@link org.mapstruct.ap.internal.gem.GemGenerator}
* after the mentioned issue is resolved.
* </p>
*
* @see <a href="https://github.com/mapstruct/tools-gem/issues/10">Gem Tools issue #10</a>
* @author Iaroslav Bogdanchikov
*/
@GemDefinition(XmlElementDecl.class)
@GemDefinition(XmlElementRef.class)
class JakartaGemGenerator {
}

View File

@ -340,6 +340,14 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
if ( factoryMethod != null ) {
forgedMethod.addThrownTypes( factoryMethod.getThrownTypes() );
}
for ( LifecycleCallbackMethodReference beforeMappingMethod : beforeMappingMethods ) {
forgedMethod.addThrownTypes( beforeMappingMethod.getThrownTypes() );
}
for ( LifecycleCallbackMethodReference afterMappingMethod : afterMappingMethods ) {
forgedMethod.addThrownTypes( afterMappingMethod.getThrownTypes() );
}
for ( PropertyMapping propertyMapping : propertyMappings ) {
if ( propertyMapping.getAssignment() != null ) {
@ -398,15 +406,13 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
"SubclassMapping for " + sourceType.getFullyQualifiedName() );
SelectionCriteria criteria =
SelectionCriteria
.forMappingMethods(
.forSubclassMappingMethods(
new SelectionParameters(
Collections.emptyList(),
Collections.emptyList(),
subclassMappingOptions.getTarget(),
ctx.getTypeUtils() ).withSourceRHS( rightHandSide ),
null,
null,
false );
subclassMappingOptions.getMappingControl( ctx.getElementUtils() ) );
Assignment assignment = ctx
.getMappingResolver()
.getTargetAssignment(

View File

@ -196,7 +196,7 @@ public class ForgedMethod implements Method {
public ForgedMethod(String name, ForgedMethod forgedMethod) {
this.parameters = forgedMethod.parameters;
this.returnType = forgedMethod.returnType;
this.thrownTypes = new ArrayList<>();
this.thrownTypes = forgedMethod.thrownTypes;
this.history = forgedMethod.history;
this.sourceParameters = Parameter.getSourceParameters( parameters );

View File

@ -257,8 +257,10 @@ public abstract class GeneratedType extends ModelElement {
return;
}
if ( needsImportDeclaration( typeToAdd ) ) {
collection.add( typeToAdd );
for ( Type type : typeToAdd.getImportTypes() ) {
if ( needsImportDeclaration( type ) ) {
collection.add( type );
}
}
}

View File

@ -176,7 +176,7 @@ public abstract class HelperMethod implements Method {
*
* @param parameter source
* @param returnType target
* @return {@code true}, iff the the type variables match
* @return {@code true}, iff the type variables match
*/
public boolean doTypeVarsMatch(Type parameter, Type returnType) {
return true;

View File

@ -141,7 +141,7 @@ public final class LifecycleMethodResolver {
callbackMethods,
Collections.emptyList(),
targetType,
method.getReturnType(),
method.getResultType(),
SelectionCriteria.forLifecycleMethods( selectionParameters ) );
return toLifecycleCallbackMethodRefs(

View File

@ -22,7 +22,7 @@ import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem;
/**
* This wrapper handles the situation where an assignment is done for an update method.
*
* In case of a pre-existing target the wrapper checks if there is an collection or map initialized on the target bean
* In case of a pre-existing target the wrapper checks if there is a collection or map initialized on the target bean
* (not null). If so it uses the addAll (for collections) or putAll (for maps). The collection / map is cleared in case
* of a pre-existing target {@link org.mapstruct.MappingTarget }before adding the source entries.
*

View File

@ -16,7 +16,7 @@ import org.mapstruct.ap.internal.model.common.Type;
* This wrapper handles the situation were an assignment must be done via a target getter method because there
* is no setter available.
*
* The wrapper checks if there is an collection or map initialized on the target bean (not null). If so it uses the
* The wrapper checks if there is a collection or map initialized on the target bean (not null). If so it uses the
* addAll (for collections) or putAll (for maps). The collection / map is cleared in case of a pre-existing target
* {@link org.mapstruct.MappingTarget }before adding the source entries. The goal is that the same collection / map
* is used as target.

View File

@ -54,7 +54,7 @@ public class UpdateWrapper extends AssignmentWrapper {
return targetType.getImplementationType();
}
// no factory method means we create a new instance ourself and thus need to import the type
// no factory method means we create a new instance ourselves and thus need to import the type
return targetType;
}

View File

@ -14,7 +14,7 @@ import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
/**
* A PropertyEntry contains information on the name, readAccessor and presenceCheck (for source)
* and return type of a property.
* and return type of property.
*/
public class PropertyEntry {

View File

@ -143,7 +143,7 @@ public class SourceReference extends AbstractReference {
* the parameter name to avoid ambiguity
*
* consider: {@code Target map( Source1 source1 )}
* entries in an @Mapping#source can be "source1.propx" or just "propx" to be valid
* entries in a @Mapping#source can be "source1.propx" or just "propx" to be valid
*
* @param segments the segments of @Mapping#source
* @param parameter the one and only parameter
@ -213,7 +213,7 @@ public class SourceReference extends AbstractReference {
* needs to match the parameter name to avoid ambiguity
*
* consider: {@code Target map( Source1 source1, Source2 source2 )}
* entries in an @Mapping#source need to be "source1.propx" or "source2.propy.propz" to be valid
* entries in a @Mapping#source need to be "source1.propx" or "source2.propy.propz" to be valid
*
* @param segments the segments of @Mapping#source
* @return parameter that matches with first segment of @Mapping#source

View File

@ -28,6 +28,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
@ -114,6 +115,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private List<Accessor> alternativeTargetAccessors = null;
private Type boundingBase = null;
private List<Type> boundTypes = null;
private Type boxedEquivalent = null;
@ -181,9 +183,16 @@ public class Type extends ModelElement implements Comparable<Type> {
this.loggingVerbose = loggingVerbose;
TypeElement typeElementForTopLevel;
if ( Boolean.TRUE.equals( isToBeImported ) ) {
// If the is to be imported is explicitly set to true then we shouldn't look for the top level type
typeElementForTopLevel = null;
}
else {
// The top level type for an array type is the top level type of the component type
TypeElement typeElementForTopLevel =
typeElementForTopLevel =
this.componentType == null ? this.typeElement : this.componentType.getTypeElement();
}
this.topLevelType = topLevelType( typeElementForTopLevel, this.typeFactory );
this.nameWithTopLevelTypeName = nameWithTopLevelTypeName( typeElementForTopLevel, this.name );
}
@ -347,6 +356,10 @@ public class Type extends ModelElement implements Comparable<Type> {
return (typeMirror.getKind() == TypeKind.TYPEVAR);
}
public boolean isIntersection() {
return typeMirror.getKind() == TypeKind.INTERSECTION;
}
public boolean isJavaLangType() {
return packageName != null && packageName.startsWith( "java." );
}
@ -1257,6 +1270,29 @@ public class Type extends ModelElement implements Comparable<Type> {
return boundingBase;
}
public List<Type> getTypeBounds() {
if ( this.boundTypes != null ) {
return boundTypes;
}
Type bound = getTypeBound();
if ( bound == null ) {
this.boundTypes = Collections.emptyList();
}
else if ( !bound.isIntersection() ) {
this.boundTypes = Collections.singletonList( bound );
}
else {
List<? extends TypeMirror> bounds = ( (IntersectionType) bound.typeMirror ).getBounds();
this.boundTypes = new ArrayList<>( bounds.size() );
for ( TypeMirror mirror : bounds ) {
boundTypes.add( typeFactory.getType( mirror ) );
}
}
return this.boundTypes;
}
public boolean hasAccessibleConstructor() {
if ( hasAccessibleConstructor == null ) {
hasAccessibleConstructor = false;

View File

@ -193,7 +193,24 @@ public class TypeFactory {
return getType( mirror, false );
}
/**
* Return a type that is always going to be imported.
* This is useful when using it in {@code Mapper#imports}
* for types that should be used in expressions.
*
* @param mirror the type mirror for which we need a type
*
* @return the type
*/
public Type getAlwaysImportedType(TypeMirror mirror) {
return getType( mirror, false, true );
}
private Type getType(TypeMirror mirror, boolean isLiteral) {
return getType( mirror, isLiteral, null );
}
private Type getType(TypeMirror mirror, boolean isLiteral, Boolean alwaysImport) {
if ( !canBeProcessed( mirror ) ) {
throw new TypeHierarchyErroneousException( mirror );
}
@ -212,7 +229,7 @@ public class TypeFactory {
String qualifiedName;
TypeElement typeElement;
Type componentType;
Boolean toBeImported = null;
Boolean toBeImported = alwaysImport;
if ( mirror.getKind() == TypeKind.DECLARED ) {
DeclaredType declaredType = (DeclaredType) mirror;

View File

@ -34,31 +34,48 @@ import org.mapstruct.tools.gem.GemValue;
public class BeanMappingOptions extends DelegatingOptions {
private final SelectionParameters selectionParameters;
private final List<String> ignoreUnmappedSourceProperties;
private final BeanMappingGem beanMapping;
/**
* creates a mapping for inheritance. Will set
*
* @param beanMapping the bean mapping options that should be used
* @param isInverse whether the inheritance is inverse
*
* @return new mapping
*/
public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) {
public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping, boolean isInverse) {
BeanMappingOptions options = new BeanMappingOptions(
SelectionParameters.forInheritance( beanMapping.selectionParameters ),
isInverse ? Collections.emptyList() : beanMapping.ignoreUnmappedSourceProperties,
beanMapping.beanMapping,
beanMapping
);
return options;
}
public static BeanMappingOptions forForgedMethods(BeanMappingOptions beanMapping) {
BeanMappingOptions options = new BeanMappingOptions(
beanMapping.selectionParameters != null ?
SelectionParameters.withoutResultType( beanMapping.selectionParameters ) : null,
Collections.emptyList(),
beanMapping.beanMapping,
beanMapping
);
return options;
}
public static BeanMappingOptions empty(DelegatingOptions delegatingOptions) {
return new BeanMappingOptions( null, Collections.emptyList(), null, delegatingOptions );
}
public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, MapperOptions mapperOptions,
ExecutableElement method, FormattingMessager messager,
TypeUtils typeUtils, TypeFactory typeFactory
) {
if ( beanMapping == null || !isConsistent( beanMapping, method, messager ) ) {
BeanMappingOptions options = new BeanMappingOptions( null, null, mapperOptions );
return options;
return empty( mapperOptions );
}
Objects.requireNonNull( method );
@ -77,6 +94,7 @@ public class BeanMappingOptions extends DelegatingOptions {
//TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support?
BeanMappingOptions options = new BeanMappingOptions(
selectionParameters,
beanMapping.ignoreUnmappedSourceProperties().get(),
beanMapping,
mapperOptions
);
@ -86,6 +104,7 @@ public class BeanMappingOptions extends DelegatingOptions {
private static boolean isConsistent(BeanMappingGem gem, ExecutableElement method,
FormattingMessager messager) {
if ( !gem.resultType().hasValue()
&& !gem.mappingControl().hasValue()
&& !gem.qualifiedBy().hasValue()
&& !gem.qualifiedByName().hasValue()
&& !gem.ignoreUnmappedSourceProperties().hasValue()
@ -104,10 +123,12 @@ public class BeanMappingOptions extends DelegatingOptions {
}
private BeanMappingOptions(SelectionParameters selectionParameters,
List<String> ignoreUnmappedSourceProperties,
BeanMappingGem beanMapping,
DelegatingOptions next) {
super( next );
this.selectionParameters = selectionParameters;
this.ignoreUnmappedSourceProperties = ignoreUnmappedSourceProperties;
this.beanMapping = beanMapping;
}
@ -188,9 +209,7 @@ public class BeanMappingOptions extends DelegatingOptions {
}
public List<String> getIgnoreUnmappedSourceProperties() {
return Optional.ofNullable( beanMapping ).map( BeanMappingGem::ignoreUnmappedSourceProperties )
.map( GemValue::get )
.orElse( Collections.emptyList() );
return ignoreUnmappedSourceProperties;
}
public AnnotationMirror getMirror() {

View File

@ -167,7 +167,7 @@ public class MappingMethodOptions {
}
if ( !getBeanMapping().hasAnnotation() && templateOptions.getBeanMapping().hasAnnotation() ) {
setBeanMapping( BeanMappingOptions.forInheritance( templateOptions.getBeanMapping( ) ) );
setBeanMapping( BeanMappingOptions.forInheritance( templateOptions.getBeanMapping( ), isInverse ) );
}
if ( !getEnumMappingOptions().hasAnnotation() && templateOptions.getEnumMappingOptions().hasAnnotation() ) {
@ -365,7 +365,7 @@ public class MappingMethodOptions {
options.mappings,
options.iterableMapping,
options.mapMapping,
options.beanMapping,
BeanMappingOptions.forForgedMethods( options.beanMapping ),
options.enumMappingOptions,
options.valueMappings,
Collections.emptySet(),

View File

@ -82,6 +82,17 @@ public class MethodMatcher {
// (the relation target / target type, target type being a class)
if ( !analyser.candidateReturnType.isVoid() ) {
if ( targetType.isPrimitive() ) {
// If the target type is primitive
// then we are going to check if its boxed equivalent
// is assignable to the candidate return type
// This is done because primitives can be assigned from their own narrower counterparts
// directly without any casting.
// e.g. a Long is assignable to a primitive double
// However, in order not to lose information we are not going to allow this
return targetType.getBoxedEquivalent()
.isAssignableTo( analyser.candidateReturnType.getBoxedEquivalent() );
}
if ( !( analyser.candidateReturnType.isAssignableTo( targetType ) ) ) {
return false;
}
@ -314,8 +325,7 @@ public class MethodMatcher {
*/
private boolean candidatesWithinBounds(Map<Type, TypeVarCandidate> methodParCandidates ) {
for ( Map.Entry<Type, TypeVarCandidate> entry : methodParCandidates.entrySet() ) {
Type bound = entry.getKey().getTypeBound();
if ( bound != null ) {
for ( Type bound : entry.getKey().getTypeBounds() ) {
for ( Type.ResolvedPair pair : entry.getValue().pairs ) {
if ( entry.getKey().hasUpperBound() ) {
if ( !pair.getMatch().asRawType().isAssignableTo( bound.asRawType() ) ) {
@ -374,7 +384,9 @@ public class MethodMatcher {
// something went wrong
return null;
}
typeArgs[i] = matchingType.getTypeMirror();
// Use the boxed equivalent for the type arguments,
// because a primitive type cannot be a type argument
typeArgs[i] = matchingType.getBoxedEquivalent().getTypeMirror();
}
else {
// it is not a type var (e.g. Map<String, T> ), String is not a type var

View File

@ -39,6 +39,10 @@ public class SelectionParameters {
* @return the selection parameters based on the given ones
*/
public static SelectionParameters forInheritance(SelectionParameters selectionParameters) {
return withoutResultType( selectionParameters );
}
public static SelectionParameters withoutResultType(SelectionParameters selectionParameters) {
return new SelectionParameters(
selectionParameters.qualifiers,
selectionParameters.qualifyingNames,

View File

@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.model.source.builtin;
import java.util.ArrayList;
import java.util.List;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.util.JaxbConstants;
import org.mapstruct.ap.internal.util.JodaTimeConstants;
@ -24,7 +25,7 @@ public class BuiltInMappingMethods {
public BuiltInMappingMethods(TypeFactory typeFactory) {
boolean isXmlGregorianCalendarPresent = isXmlGregorianCalendarAvailable( typeFactory );
builtInMethods = new ArrayList<>( 20 );
builtInMethods = new ArrayList<>( 21 );
if ( isXmlGregorianCalendarPresent ) {
builtInMethods.add( new DateToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToDate( typeFactory ) );
@ -39,8 +40,14 @@ public class BuiltInMappingMethods {
builtInMethods.add( new XmlGregorianCalendarToLocalDateTime( typeFactory ) );
}
if ( isJaxbAvailable( typeFactory ) ) {
builtInMethods.add( new JaxbElemToValue( typeFactory ) );
if ( isJavaxJaxbAvailable( typeFactory ) ) {
Type type = typeFactory.getType( JaxbConstants.JAVAX_JAXB_ELEMENT_FQN );
builtInMethods.add( new JaxbElemToValue( type ) );
}
if ( isJakartaJaxbAvailable( typeFactory ) ) {
Type type = typeFactory.getType( JaxbConstants.JAKARTA_JAXB_ELEMENT_FQN );
builtInMethods.add( new JaxbElemToValue( type ) );
}
builtInMethods.add( new ZonedDateTimeToCalendar( typeFactory ) );
@ -58,8 +65,12 @@ public class BuiltInMappingMethods {
}
}
private static boolean isJaxbAvailable(TypeFactory typeFactory) {
return typeFactory.isTypeAvailable( JaxbConstants.JAXB_ELEMENT_FQN );
private static boolean isJavaxJaxbAvailable(TypeFactory typeFactory) {
return typeFactory.isTypeAvailable( JaxbConstants.JAVAX_JAXB_ELEMENT_FQN );
}
private static boolean isJakartaJaxbAvailable(TypeFactory typeFactory) {
return typeFactory.isTypeAvailable( JaxbConstants.JAKARTA_JAXB_ELEMENT_FQN );
}
private static boolean isXmlGregorianCalendarAvailable(TypeFactory typeFactory) {

View File

@ -190,7 +190,7 @@ public abstract class BuiltInMethod implements Method {
*
* @param parameter source
* @param returnType target
* @return {@code true}, iff the the type variables match
* @return {@code true}, iff the type variables match
*/
public boolean doTypeVarsMatch(Type parameter, Type returnType) {
return true;

View File

@ -5,26 +5,23 @@
*/
package org.mapstruct.ap.internal.model.source.builtin;
import static org.mapstruct.ap.internal.util.Collections.asSet;
import java.util.Set;
import org.mapstruct.ap.internal.model.common.Parameter;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.util.JaxbConstants;
import static org.mapstruct.ap.internal.util.Collections.asSet;
/**
* @author Sjaak Derksen
*/
public class JaxbElemToValue extends BuiltInMethod {
class JaxbElemToValue extends BuiltInMethod {
private final Parameter parameter;
private final Type returnType;
private final Set<Type> importTypes;
public JaxbElemToValue(TypeFactory typeFactory) {
Type type = typeFactory.getType( JaxbConstants.JAXB_ELEMENT_FQN );
JaxbElemToValue(Type type) {
this.parameter = new Parameter( "element", type );
this.returnType = type.getTypeParameters().get( 0 );
this.importTypes = asSet( parameter.getType() );

View File

@ -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.internal.model.source.selector;
import javax.lang.model.element.Element;
import org.mapstruct.ap.internal.gem.jakarta.XmlElementDeclGem;
import org.mapstruct.ap.internal.gem.jakarta.XmlElementRefGem;
import org.mapstruct.ap.internal.util.TypeUtils;
/**
* The concrete implementation of the {@link XmlElementDeclSelector} that
* works with {@link jakarta.xml.bind.annotation.XmlElementRef} and
* {@link jakarta.xml.bind.annotation.XmlElementDecl}.
*
* @author Iaroslav Bogdanchikov
*/
class JakartaXmlElementDeclSelector extends XmlElementDeclSelector {
JakartaXmlElementDeclSelector(TypeUtils typeUtils) {
super( typeUtils );
}
@Override
XmlElementDeclInfo getXmlElementDeclInfo(Element element) {
XmlElementDeclGem gem = XmlElementDeclGem.instanceOn( element );
if (gem == null) {
return null;
}
return new XmlElementDeclInfo( gem.name().get(), gem.scope().get() );
}
@Override
XmlElementRefInfo getXmlElementRefInfo(Element element) {
XmlElementRefGem gem = XmlElementRefGem.instanceOn( element );
if (gem == null) {
return null;
}
return new XmlElementRefInfo( gem.name().get(), gem.type().get() );
}
}

View File

@ -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.internal.model.source.selector;
import javax.lang.model.element.Element;
import org.mapstruct.ap.internal.gem.XmlElementDeclGem;
import org.mapstruct.ap.internal.gem.XmlElementRefGem;
import org.mapstruct.ap.internal.util.TypeUtils;
/**
* The concrete implementation of the {@link XmlElementDeclSelector} that
* works with {@link javax.xml.bind.annotation.XmlElementRef} and
* {@link javax.xml.bind.annotation.XmlElementDecl}.
*
* @author Iaroslav Bogdanchikov
*/
class JavaxXmlElementDeclSelector extends XmlElementDeclSelector {
JavaxXmlElementDeclSelector(TypeUtils typeUtils) {
super( typeUtils );
}
@Override
XmlElementDeclInfo getXmlElementDeclInfo(Element element) {
XmlElementDeclGem gem = XmlElementDeclGem.instanceOn( element );
if (gem == null) {
return null;
}
return new XmlElementDeclInfo( gem.name().get(), gem.scope().get() );
}
@Override
XmlElementRefInfo getXmlElementRefInfo(Element element) {
XmlElementRefGem gem = XmlElementRefGem.instanceOn( element );
if (gem == null) {
return null;
}
return new XmlElementRefInfo( gem.name().get(), gem.type().get() );
}
}

View File

@ -32,7 +32,8 @@ public class MethodSelectors {
new TypeSelector( typeFactory, messager ),
new QualifierSelector( typeUtils, elementUtils ),
new TargetTypeSelector( typeUtils ),
new XmlElementDeclSelector( typeUtils ),
new JavaxXmlElementDeclSelector( typeUtils ),
new JakartaXmlElementDeclSelector( typeUtils ),
new InheritanceSelector(),
new CreateOrUpdateSelector(),
new SourceRhsSelector(),

View File

@ -141,6 +141,10 @@ public class SelectionCriteria {
return allow2Steps;
}
public boolean isSelfAllowed() {
return type != Type.SELF_NOT_ALLOWED;
}
public static SelectionCriteria forMappingMethods(SelectionParameters selectionParameters,
MappingControl mappingControl,
String targetPropertyName, boolean preferUpdateMapping) {
@ -165,10 +169,16 @@ public class SelectionCriteria {
return new SelectionCriteria( selectionParameters, null, null, Type.PRESENCE_CHECK );
}
public static SelectionCriteria forSubclassMappingMethods(SelectionParameters selectionParameters,
MappingControl mappingControl) {
return new SelectionCriteria( selectionParameters, mappingControl, null, Type.SELF_NOT_ALLOWED );
}
public enum Type {
PREFER_UPDATE_MAPPING,
OBJECT_FACTORY,
LIFECYCLE_CALLBACK,
PRESENCE_CHECK,
SELF_NOT_ALLOWED,
}
}

View File

@ -11,19 +11,17 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.gem.XmlElementRefGem;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.source.Method;
import org.mapstruct.ap.internal.model.source.SourceMethod;
import org.mapstruct.ap.internal.gem.XmlElementDeclGem;
import org.mapstruct.ap.internal.util.TypeUtils;
/**
* Finds the {@link javax.xml.bind.annotation.XmlElementRef} annotation on a field (of the mapping result type or its
* Finds the {@code XmlElementRef} annotation on a field (of the mapping result type or its
* super types) matching the
* target property name. Then selects those methods with matching {@code name} and {@code scope} attributes of the
* {@link javax.xml.bind.annotation.XmlElementDecl} annotation, if that is present. Matching happens in the following
* {@code XmlElementDecl} annotation, if that is present. Matching happens in the following
* order:
* <ol>
* <li>Name and Scope matches</li>
@ -34,12 +32,15 @@ import org.mapstruct.ap.internal.gem.XmlElementDeclGem;
* the given method is not annotated with {@code XmlElementDecl} it will be considered as matching.
*
* @author Sjaak Derksen
*
* @see JavaxXmlElementDeclSelector
* @see JakartaXmlElementDeclSelector
*/
public class XmlElementDeclSelector implements MethodSelector {
abstract class XmlElementDeclSelector implements MethodSelector {
private final TypeUtils typeUtils;
public XmlElementDeclSelector(TypeUtils typeUtils) {
XmlElementDeclSelector(TypeUtils typeUtils) {
this.typeUtils = typeUtils;
}
@ -63,15 +64,14 @@ public class XmlElementDeclSelector implements MethodSelector {
}
SourceMethod candidateMethod = (SourceMethod) candidate.getMethod();
XmlElementDeclGem xmlElementDecl =
XmlElementDeclGem.instanceOn( candidateMethod.getExecutable() );
XmlElementDeclInfo xmlElementDeclInfo = getXmlElementDeclInfo( candidateMethod.getExecutable() );
if ( xmlElementDecl == null ) {
if ( xmlElementDeclInfo == null ) {
continue;
}
String name = xmlElementDecl.name().get();
TypeMirror scope = xmlElementDecl.scope().getValue();
String name = xmlElementDeclInfo.nameValue();
TypeMirror scope = xmlElementDeclInfo.scopeType();
boolean nameIsSetAndMatches = name != null && name.equals( xmlElementRefInfo.nameValue() );
boolean scopeIsSetAndMatches =
@ -142,9 +142,9 @@ public class XmlElementDeclSelector implements MethodSelector {
for ( Element enclosed : currentElement.getEnclosedElements() ) {
if ( enclosed.getKind().equals( ElementKind.FIELD )
&& enclosed.getSimpleName().contentEquals( targetPropertyName ) ) {
XmlElementRefGem xmlElementRef = XmlElementRefGem.instanceOn( enclosed );
if ( xmlElementRef != null ) {
return new XmlElementRefInfo( xmlElementRef.name().get(), currentMirror );
XmlElementRefInfo xmlElementRefInfo = getXmlElementRefInfo( enclosed );
if ( xmlElementRefInfo != null ) {
return new XmlElementRefInfo( xmlElementRefInfo.nameValue(), currentMirror );
}
}
}
@ -154,7 +154,11 @@ public class XmlElementDeclSelector implements MethodSelector {
return defaultInfo;
}
private static class XmlElementRefInfo {
abstract XmlElementDeclInfo getXmlElementDeclInfo(Element element);
abstract XmlElementRefInfo getXmlElementRefInfo(Element element);
static class XmlElementRefInfo {
private final String nameValue;
private final TypeMirror sourceType;
@ -163,12 +167,37 @@ public class XmlElementDeclSelector implements MethodSelector {
this.sourceType = sourceType;
}
public String nameValue() {
String nameValue() {
return nameValue;
}
public TypeMirror sourceType() {
TypeMirror sourceType() {
return sourceType;
}
}
/**
* A class, whose purpose is to combine the use of
* {@link org.mapstruct.ap.internal.gem.XmlElementDeclGem}
* and
* {@link org.mapstruct.ap.internal.gem.jakarta.XmlElementDeclGem}.
*/
static class XmlElementDeclInfo {
private final String nameValue;
private final TypeMirror scopeType;
XmlElementDeclInfo(String nameValue, TypeMirror scopeType) {
this.nameValue = nameValue;
this.scopeType = scopeType;
}
String nameValue() {
return nameValue;
}
TypeMirror scopeType() {
return scopeType;
}
}
}

View File

@ -12,6 +12,8 @@ import java.util.List;
import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.Mapper;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
/**
* A {@link ModelElementProcessor} which converts the given {@link Mapper}
@ -30,13 +32,13 @@ public class CdiComponentProcessor extends AnnotationBasedComponentModelProcesso
@Override
protected List<Annotation> getTypeAnnotations(Mapper mapper) {
return Collections.singletonList(
new Annotation( getTypeFactory().getType( "javax.enterprise.context.ApplicationScoped" ) )
new Annotation( getType( "ApplicationScoped" ) )
);
}
@Override
protected List<Annotation> getMapperReferenceAnnotations() {
return Arrays.asList( new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) ) );
return Arrays.asList( new Annotation( getType( "Inject" ) ) );
}
@Override
@ -48,4 +50,24 @@ public class CdiComponentProcessor extends AnnotationBasedComponentModelProcesso
protected boolean additionalPublicEmptyConstructor() {
return true;
}
private Type getType(String simpleName) {
String javaxPrefix = "javax.inject.";
String jakartaPrefix = "jakarta.inject.";
if ( "ApplicationScoped".equals( simpleName ) ) {
javaxPrefix = "javax.enterprise.context.";
jakartaPrefix = "jakarta.enterprise.context.";
}
if ( getTypeFactory().isTypeAvailable( javaxPrefix + simpleName ) ) {
return getTypeFactory().getType( javaxPrefix + simpleName );
}
if ( getTypeFactory().isTypeAvailable( jakartaPrefix + simpleName ) ) {
return getTypeFactory().getType( jakartaPrefix + simpleName );
}
throw new AnnotationProcessingException(
"Couldn't find any of the CDI or Jakarta CDI Dependency types." +
" Are you missing a dependency on your classpath?" );
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.internal.processor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.Mapper;
/**
* A {@link ModelElementProcessor} which converts the given {@link Mapper}
* object into an application-scoped Jakarta CDI bean in case Jakarta CDI
* is configured as the target component model for this mapper.
*
* @author Filip Hrisafov
*/
public class JakartaCdiComponentProcessor extends AnnotationBasedComponentModelProcessor {
@Override
protected String getComponentModelIdentifier() {
return MappingConstantsGem.ComponentModelGem.JAKARTA_CDI;
}
@Override
protected List<Annotation> getTypeAnnotations(Mapper mapper) {
return Collections.singletonList(
new Annotation( getTypeFactory().getType( "jakarta.enterprise.context.ApplicationScoped" ) )
);
}
@Override
protected List<Annotation> getMapperReferenceAnnotations() {
return Arrays.asList( new Annotation( getTypeFactory().getType( "jakarta.inject.Inject" ) ) );
}
@Override
protected boolean requiresGenerationOfDecoratorClass() {
return false;
}
@Override
protected boolean additionalPublicEmptyConstructor() {
return true;
}
}

View File

@ -301,7 +301,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
for ( TypeMirror extraImport : mapperOptions.imports() ) {
Type type = typeFactory.getType( extraImport );
Type type = typeFactory.getAlwaysImportedType( extraImport );
extraImports.add( type );
}

View File

@ -196,7 +196,6 @@ public class MappingResolverImpl implements MappingResolver {
this.mappingMethod = mappingMethod;
this.description = description;
this.methods = filterPossibleCandidateMethods( sourceModel );
this.formattingParameters =
formattingParameters == null ? FormattingParameters.EMPTY : formattingParameters;
this.sourceRHS = sourceRHS;
@ -207,13 +206,14 @@ public class MappingResolverImpl implements MappingResolver {
this.builtIns = builtIns;
this.messager = messager;
this.reportingLimitAmbiguous = verboseLogging ? Integer.MAX_VALUE : LIMIT_REPORTING_AMBIGUOUS;
this.methods = filterPossibleCandidateMethods( sourceModel, mappingMethod );
}
// CHECKSTYLE:ON
private <T extends Method> List<T> filterPossibleCandidateMethods(List<T> candidateMethods) {
private <T extends Method> List<T> filterPossibleCandidateMethods(List<T> candidateMethods, T mappingMethod) {
List<T> result = new ArrayList<>( candidateMethods.size() );
for ( T candidate : candidateMethods ) {
if ( isCandidateForMapping( candidate ) ) {
if ( isCandidateForMapping( candidate ) && isNotSelfOrSelfAllowed( mappingMethod, candidate )) {
result.add( candidate );
}
}
@ -221,6 +221,10 @@ public class MappingResolverImpl implements MappingResolver {
return result;
}
private <T extends Method> boolean isNotSelfOrSelfAllowed(T mappingMethod, T candidate) {
return selectionCriteria == null || selectionCriteria.isSelfAllowed() || !candidate.equals( mappingMethod );
}
private Assignment getTargetAssignment(Type sourceType, Type targetType) {
Assignment assignment;

View File

@ -10,7 +10,8 @@ package org.mapstruct.ap.internal.util;
*/
public final class JaxbConstants {
public static final String JAXB_ELEMENT_FQN = "javax.xml.bind.JAXBElement";
public static final String JAVAX_JAXB_ELEMENT_FQN = "javax.xml.bind.JAXBElement";
public static final String JAKARTA_JAXB_ELEMENT_FQN = "jakarta.xml.bind.JAXBElement";
private JaxbConstants() {
}

View File

@ -159,7 +159,7 @@ public enum Message {
RETRIEVAL_NO_INPUT_ARGS( "Can't generate mapping method with no input arguments." ),
RETRIEVAL_DUPLICATE_MAPPING_TARGETS( "Can't generate mapping method with more than one @MappingTarget parameter." ),
RETRIEVAL_VOID_MAPPING_METHOD( "Can't generate mapping method with return type void." ),
RETRIEVAL_NON_ASSIGNABLE_RESULTTYPE( "The result type is not assignable to the the return type." ),
RETRIEVAL_NON_ASSIGNABLE_RESULTTYPE( "The result type is not assignable to the return type." ),
RETRIEVAL_ITERABLE_TO_NON_ITERABLE( "Can't generate mapping method from iterable type from java stdlib to non-iterable type." ),
RETRIEVAL_MAPPING_HAS_TARGET_TYPE_PARAMETER( "Can't generate mapping method that has a parameter annotated with @TargetType." ),
RETRIEVAL_NON_ITERABLE_TO_ITERABLE( "Can't generate mapping method from non-iterable type to iterable type from java stdlib." ),

View File

@ -41,7 +41,7 @@ public class RoundContext {
/**
* Whether the given type has been found to be ready for further processing or not. This is the case if the type's
* hierarchy is complete (no super-types need to be generated by other processors) an no processors have signaled
* hierarchy is complete (no super-types need to be generated by other processors) and no processors have signaled
* the intention to amend the given type.
*
* @param type the typed to be checked for its readiness

View File

@ -15,7 +15,7 @@ import org.mapstruct.util.Experimental;
* <p>
* This contract will be queried by MapStruct when examining types referenced by mappers to be generated, most notably
* the source and target types of mapping methods. If at least one AST-modifying processor announces further changes to
* such type, the generation of the affected mapper(s) will be deferred to a future round in the annnotation processing
* such type, the generation of the affected mapper(s) will be deferred to a future round in the annotation processing
* cycle.
* <p>
* Implementations are discovered via the service loader, i.e. a JAR providing an AST-modifying processor needs to

View File

@ -20,8 +20,8 @@ import org.mapstruct.util.Experimental;
* <li>{@code mergeFrom(Target.Builder)}</li>
* </ul>
* <p>
* When the JavaBean convention is not used with FreeBuilder then the getters are non standard and MapStruct
* won't recognize them. Therefore one needs to use the JavaBean convention in which the fluent setters
* When the JavaBean convention is not used with FreeBuilder then the getters are non-standard and MapStruct
* won't recognize them. Therefore, one needs to use the JavaBean convention in which the fluent setters
* start with {@code set}.
*
* @author Filip Hrisafov

View File

@ -10,9 +10,9 @@ import javax.lang.model.element.ExecutableElement;
import org.mapstruct.util.Experimental;
/**
* Accesor naming strategy for Immutables.
* Accessor naming strategy for Immutables.
* The generated Immutables also have a from that works as a copy. Our default strategy considers this method
* as a setter with a name {@code from}. Therefore we are ignoring it.
* as a setter with a name {@code from}. Therefore, we are ignoring it.
*
* @author Filip Hrisafov
*/

View File

@ -3,6 +3,7 @@
# Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
org.mapstruct.ap.internal.processor.CdiComponentProcessor
org.mapstruct.ap.internal.processor.JakartaCdiComponentProcessor
org.mapstruct.ap.internal.processor.Jsr330ComponentProcessor
org.mapstruct.ap.internal.processor.JakartaComponentProcessor
org.mapstruct.ap.internal.processor.MapperCreationProcessor

View File

@ -62,6 +62,7 @@
-->
<#macro _assignment assignmentToUse>
<@includeModel object=assignmentToUse
presenceCheck=ext.presenceCheck
targetBeanName=ext.targetBeanName
existingInstanceMapping=ext.existingInstanceMapping
targetReadAccessorName=ext.targetReadAccessorName

View File

@ -7,4 +7,5 @@
-->
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" -->
<@includeModel object=methodReference
presenceCheck=true
targetType=ext.targetType/>

View File

@ -111,7 +111,7 @@
<#else>
<#-- Streams are immutable so we can't update them -->
<#if !existingInstanceMapping>
<#--TODO fhr: after the the result is no longer the same instance, how does it affect the
<#--TODO fhr: after the result is no longer the same instance, how does it affect the
Before mapping methods. Does it even make sense to have before mapping on a stream? -->
<#if sourceParameter.type.arrayType>
<@returnLocalVarDefOrUpdate>Stream.of( ${sourceParameter.name} )<@streamMapSupplier />;</@returnLocalVarDefOrUpdate>

View File

@ -7,7 +7,7 @@
-->
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.ValueMappingMethod" -->
<#if overridden>@Override</#if>
<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) {
<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>)<@throws/> {
<#list beforeMappingReferencesWithoutMappingTarget as callback>
<@includeModel object=callback targetBeanName=resultName targetType=resultType/>
<#if !callback_has_next>
@ -66,3 +66,11 @@
</#if>
</@compress>
</#macro>
<#macro throws>
<#if (thrownTypes?size > 0)><#lt> throws </#if><@compress single_line=true>
<#list thrownTypes as exceptionType>
<@includeModel object=exceptionType/>
<#if exceptionType_has_next>, </#if><#t>
</#list>
</@compress>
</#macro>

View File

@ -6,4 +6,4 @@
-->
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.common.SourceRHS" -->
<#if sourceLoopVarName??>${sourceLoopVarName}<#elseif sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference}</#if>
<#if sourceLoopVarName?? && !ext.presenceCheck??>${sourceLoopVarName}<#elseif sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference}</#if>

View File

@ -23,8 +23,8 @@ public class Issue1719Test {
/**
* For adder methods MapStuct cannot generate an update method. MapStruct would cannot know how to remove objects
* from the child-parent relation. It cannot even assume that the the collection can be cleared at forehand.
* Therefore the only sensible choice is for MapStruct to create a create method for the target elements.
* from the child-parent relation. It cannot even assume that the collection can be cleared at forehand.
* Therefore, the only sensible choice is for MapStruct to create a create method for the target elements.
*/
@ProcessorTest
@WithClasses(Issue1719Mapper.class)

View File

@ -0,0 +1,75 @@
/*
* 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._2743;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
/**
* @author Filip Hrisafov
*/
@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR)
public interface Issue2743Mapper {
@BeanMapping(ignoreUnmappedSourceProperties = { "number" })
Target map(Source source);
class Source {
private final int number = 10;
private final NestedSource nested;
public Source(NestedSource nested) {
this.nested = nested;
}
public int getNumber() {
return number;
}
public NestedSource getNested() {
return nested;
}
}
class NestedSource {
private final String value;
public NestedSource(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
class Target {
private final NestedTarget nested;
public Target(NestedTarget nested) {
this.nested = nested;
}
public NestedTarget getNested() {
return nested;
}
}
class NestedTarget {
private final String value;
public NestedTarget(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2743;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
/**
* @author Filip Hrisafov
*/
@IssueKey("2743")
@WithClasses({
Issue2743Mapper.class
})
class Issue2743Test {
@ProcessorTest
void shouldCompile() {
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2825;
/**
* @author orange add
*/
public class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2825;
/**
* @author orange add
*/
public class Cat extends Animal {
private String race;
public String getRace() {
return race;
}
public void setRace(String race) {
this.race = race;
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2825;
/**
* @author orange add
*/
public class Dog extends Animal {
private String race;
public String getRace() {
return race;
}
public void setRace(String race) {
this.race = race;
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2825;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.SubclassMapping;
import org.mapstruct.factory.Mappers;
/**
* @author orange add
*/
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface Issue2825Mapper {
Issue2825Mapper INSTANCE = Mappers.getMapper( Issue2825Mapper.class );
@SubclassMapping(target = TargetAnimal.class, source = Dog.class)
@SubclassMapping(target = TargetAnimal.class, source = Cat.class)
TargetAnimal map(Animal source);
}

View File

@ -0,0 +1,37 @@
/*
* 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._2825;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author orange add
*/
@IssueKey("2825")
@WithClasses({
Animal.class,
Cat.class,
Dog.class,
Issue2825Mapper.class,
TargetAnimal.class,
})
public class Issue2825Test {
@ProcessorTest
public void mappingMethodShouldNotBeReusedForSubclassMappings() {
Dog dog = new Dog();
dog.setName( "Lucky" );
dog.setRace( "Shepherd" );
TargetAnimal target = Issue2825Mapper.INSTANCE.map( dog );
assertThat( target.getName() ).isEqualTo( "Lucky" );
assertThat( target.getRace() ).isEqualTo( "Shepherd" );
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2825;
/**
* @author orange add
*/
public class TargetAnimal {
private String name;
private String race;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRace() {
return race;
}
public void setRace(String race) {
this.race = race;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2839;
import java.util.List;
/**
* @author Hakan Özkan
*/
public final class Car {
private final Id id;
private final List<? extends Id> seatIds;
private final List<? extends Id> tireIds;
public Car(Id id, List<? extends Id> seatIds, List<? extends Id> tireIds) {
this.id = id;
this.seatIds = seatIds;
this.tireIds = tireIds;
}
public Id getId() {
return id;
}
public List<? extends Id> getSeatIds() {
return seatIds;
}
public List<? extends Id> getTireIds() {
return tireIds;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2839;
import java.util.List;
/**
* @author Hakan Özkan
*/
public final class CarDto {
private final String id;
private final List<String> seatIds;
private final List<String> tireIds;
public CarDto(String id, List<String> seatIds, List<String> tireIds) {
this.id = id;
this.seatIds = seatIds;
this.tireIds = tireIds;
}
public String getId() {
return id;
}
public List<String> getSeatIds() {
return seatIds;
}
public List<String> getTireIds() {
return tireIds;
}
}

View File

@ -0,0 +1,25 @@
/*
* 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._2839;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Hakan Özkan
*/
@Mapper
public abstract class CarMapper {
public static final CarMapper MAPPER = Mappers.getMapper( CarMapper.class );
public abstract Car toEntity(CarDto dto);
protected Id mapId(String id) throws Issue2839Exception {
throw new Issue2839Exception("For id " + id);
}
}

View File

@ -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._2839;
import java.util.UUID;
/**
* @author Hakan Özkan
*/
public class Id {
private final UUID id;
public Id() {
this.id = UUID.randomUUID();
}
public Id(UUID id) {
this.id = id;
}
public UUID getId() {
return id;
}
}

View File

@ -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._2839;
/**
* @author Hakan Özkan
*/
public class Issue2839Exception extends Exception {
public Issue2839Exception(String message) {
super( message );
}
}

View File

@ -0,0 +1,56 @@
/*
* 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._2839;
import java.util.Collections;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* @author Hakan Özkan
*/
@IssueKey("2839")
@WithClasses({
Car.class,
CarDto.class,
CarMapper.class,
Id.class,
Issue2839Exception.class,
})
public class Issue2839Test {
@ProcessorTest
void shouldCompile() {
CarDto car1 = new CarDto(
"carId",
Collections.singletonList( "seatId" ),
Collections.singletonList( "tireId" )
);
assertThatThrownBy( () -> CarMapper.MAPPER.toEntity( car1 ) )
.isExactlyInstanceOf( RuntimeException.class )
.getCause()
.isInstanceOf( Issue2839Exception.class )
.hasMessage( "For id seatId" );
CarDto car2 = new CarDto( "carId", Collections.emptyList(), Collections.singletonList( "tireId" ) );
assertThatThrownBy( () -> CarMapper.MAPPER.toEntity( car2 ) )
.isExactlyInstanceOf( RuntimeException.class )
.getCause()
.isInstanceOf( Issue2839Exception.class )
.hasMessage( "For id tireId" );
CarDto car3 = new CarDto( "carId", Collections.emptyList(), Collections.emptyList() );
assertThatThrownBy( () -> CarMapper.MAPPER.toEntity( car3 ) )
.isExactlyInstanceOf( RuntimeException.class )
.getCause()
.isInstanceOf( Issue2839Exception.class )
.hasMessage( "For id carId" );
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2840;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface Issue2840Mapper {
Issue2840Mapper INSTANCE =
Mappers.getMapper( Issue2840Mapper.class );
Issue2840Mapper.Target map(Short shortValue, Integer intValue);
default int toInt(Number number) {
return number.intValue() + 5;
}
default short toShort(Number number) {
return (short) (number.shortValue() + 10);
}
class Target {
private int intValue;
private short shortValue;
public int getIntValue() {
return intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
public short getShortValue() {
return shortValue;
}
public void setShortValue(short shortValue) {
this.shortValue = shortValue;
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2840;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Filip Hrisafov
*/
@IssueKey("2840")
@WithClasses({
Issue2840Mapper.class,
})
class Issue2840Test {
@ProcessorTest
void shouldUseMethodWithMostSpecificReturnType() {
Issue2840Mapper.Target target = Issue2840Mapper.INSTANCE.map( (short) 10, 50 );
assertThat( target.getShortValue() ).isEqualTo( (short) 20 );
assertThat( target.getIntValue() ).isEqualTo( 55 );
}
}

View File

@ -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._2897;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ap.test.bugs._2897.util.Util;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
@Mapper(imports = Util.Factory.class)
public interface Issue2897Mapper {
Issue2897Mapper INSTANCE = Mappers.getMapper( Issue2897Mapper.class );
@Mapping( target = "value", expression = "java(Factory.parse( source ))")
Target map(Source source);
}

View File

@ -0,0 +1,33 @@
/*
* 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._2897;
import org.mapstruct.ap.test.bugs._2897.util.Util;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Filip Hrisafov
*/
@IssueKey("2897")
@WithClasses({
Util.class,
Issue2897Mapper.class,
Source.class,
Target.class,
})
class Issue2897Test {
@ProcessorTest
void shouldImportNestedClassInMapperImports() {
Target target = Issue2897Mapper.INSTANCE.map( new Source( "test" ) );
assertThat( target.getValue() ).isEqualTo( "parsed(test)" );
}
}

View File

@ -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._2897;
/**
* @author Filip Hrisafov
*/
public class Source {
private final String value;
public Source(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}

View File

@ -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._2897;
/**
* @author Filip Hrisafov
*/
public class Target {
private final String value;
public Target(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}

Some files were not shown because too many files have changed in this diff Show More