From bbf63ae1777981d1d89693d6f9d980064997cf28 Mon Sep 17 00:00:00 2001 From: Iaroslav Bogdanchikov <6546969+ibogdanchikov@users.noreply.github.com> Date: Fri, 2 Sep 2022 22:04:01 +0200 Subject: [PATCH] #2730 Add support for Jakarta XML Binding --- distribution/pom.xml | 8 +- integrationtest/pom.xml | 2 +- .../itest/tests/MavenIntegrationTest.java | 4 + .../test/resources/fullFeatureTest/pom.xml | 17 ++- .../test/resources/jakartaJaxbTest/pom.xml | 64 ++++++++++ .../itest/jakarta/jaxb/OrderDetailsDto.java | 42 +++++++ .../itest/jakarta/jaxb/OrderDto.java | 52 ++++++++ .../itest/jakarta/jaxb/OrderStatusDto.java | 35 ++++++ .../jakarta/jaxb/ShippingAddressDto.java | 50 ++++++++ .../jakarta/jaxb/SourceTargetMapper.java | 50 ++++++++ .../itest/jakarta/jaxb/SubTypeDto.java | 27 ++++ .../itest/jakarta/jaxb/SuperTypeDto.java | 27 ++++ .../src/main/resources/binding/binding.xjb | 28 +++++ .../src/main/resources/schema/test1.xsd | 36 ++++++ .../src/main/resources/schema/test2.xsd | 33 +++++ .../src/main/resources/schema/underscores.xsd | 34 +++++ .../jaxb/JakartaJaxbBasedMapperTest.java | 118 ++++++++++++++++++ .../src/test/resources/jaxbTest/pom.xml | 12 +- parent/pom.xml | 29 ++++- processor/pom.xml | 8 +- .../gem/jakarta/JakartaGemGenerator.java | 26 ++++ .../source/builtin/BuiltInMappingMethods.java | 21 +++- .../model/source/builtin/JaxbElemToValue.java | 11 +- .../JakartaXmlElementDeclSelector.java | 48 +++++++ .../selector/JavaxXmlElementDeclSelector.java | 48 +++++++ .../source/selector/MethodSelectors.java | 3 +- .../selector/XmlElementDeclSelector.java | 65 +++++++--- .../ap/internal/util/JaxbConstants.java | 3 +- .../ap/test/builtin/BuiltInTest.java | 77 ++++++++++++ .../bean/JakartaJaxbElementListProperty.java | 28 +++++ .../bean/JakartaJaxbElementProperty.java | 25 ++++ .../builtin/mapper/JakartaJaxbListMapper.java | 19 +++ .../builtin/mapper/JakartaJaxbMapper.java | 31 +++++ .../ap/testutil/WithJakartaJaxb.java | 27 ++++ 34 files changed, 1062 insertions(+), 46 deletions(-) create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/pom.xml create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java diff --git a/distribution/pom.xml b/distribution/pom.xml index 9ceeb505e..11fb7c9f3 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -39,6 +39,13 @@ org.mapstruct.tools.gem gem-api + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + @@ -187,7 +194,6 @@ javax.xml.bind jaxb-api - 2.3.1 provided true diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 364faae64..f888e226f 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -133,8 +133,8 @@ javax.xml.bind jaxb-api - 2.3.1 provided + true diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 7d63ccf77..75513dd6c 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -75,6 +75,10 @@ public class MavenIntegrationTest { void jaxbTest() { } + @ProcessorTest(baseDir = "jakartaJaxbTest") + void jakartaJaxbTest() { + } + @ProcessorTest(baseDir = "jsr330Test") void jsr330Test() { } diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 62f7986e9..ac69114bd 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -83,6 +83,13 @@ joda-time joda-time + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + @@ -93,14 +100,16 @@ - jakarta.xml.bind - jakarta.xml.bind-api - 2.3.2 + javax.xml.bind + jaxb-api + provided + true org.glassfish.jaxb jaxb-runtime - 2.3.2 + provided + true diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml b/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml new file mode 100644 index 000000000..3aabc1c7a --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + jakartaJaxbTest + jar + + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + + + com.sun.xml.bind + jaxb-impl + provided + true + + + + + + + org.codehaus.mojo + jaxb2-maven-plugin + 3.1.0 + + + xjc + initialize + + xjc + + + + + + ${project.build.resources[0].directory}/binding + + + ${project.build.resources[0].directory}/schema + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java new file mode 100644 index 000000000..e8500633e --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java @@ -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 description; + private OrderStatusDto status; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getDescription() { + return description; + } + + public void setDescription(List description) { + this.description = description; + } + + public OrderStatusDto getStatus() { + return status; + } + + public void setStatus(OrderStatusDto status) { + this.status = status; + } +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java new file mode 100644 index 000000000..f94d5362e --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java @@ -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; + } + +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java new file mode 100644 index 000000000..5da5d45c9 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java @@ -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 ); + } + +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java new file mode 100644 index 000000000..6bc40a19b --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java @@ -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; + } + +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java new file mode 100644 index 000000000..3b76aad43 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java @@ -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); +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java new file mode 100644 index 000000000..88218c277 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java @@ -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; + } +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java new file mode 100644 index 000000000..cd0c6e22e --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java @@ -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; + } +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb new file mode 100644 index 000000000..8f26b1a1e --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd new file mode 100644 index 000000000..3433b0146 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd new file mode 100644 index 000000000..f3b564a48 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd new file mode 100644 index 000000000..b7f590465 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java new file mode 100644 index 000000000..b81c946d9 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java @@ -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() ); + 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(); + } +} diff --git a/integrationtest/src/test/resources/jaxbTest/pom.xml b/integrationtest/src/test/resources/jaxbTest/pom.xml index 000e7cf59..0e69e23e0 100644 --- a/integrationtest/src/test/resources/jaxbTest/pom.xml +++ b/integrationtest/src/test/resources/jaxbTest/pom.xml @@ -51,7 +51,7 @@ org.glassfish.jaxb jaxb-runtime - 2.3.2 + ${jaxb-runtime.version} @@ -66,14 +66,16 @@ - jakarta.xml.bind - jakarta.xml.bind-api - 2.3.2 + javax.xml.bind + jaxb-api + provided + true org.glassfish.jaxb jaxb-runtime - 2.3.2 + provided + true diff --git a/parent/pom.xml b/parent/pom.xml index 426405b4b..3f536034c 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -42,6 +42,7 @@ --> 1.8 3.21.2 + 2.3.2 @@ -255,6 +256,30 @@ 2.9 + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + org.glassfish.jaxb + jaxb-runtime + ${jaxb-runtime.version} + + + + jakarta.xml.bind + jakarta.xml.bind-api + 3.0.1 + + + com.sun.xml.bind + jaxb-impl + 3.0.2 + + org.eclipse.tycho @@ -488,12 +513,12 @@ org.codehaus.mojo animal-sniffer-maven-plugin - 1.17 + 1.20 org.ow2.asm asm - 6.2.1 + 7.0 diff --git a/processor/pom.xml b/processor/pom.xml index ff7864f32..12fc615f5 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -137,6 +137,13 @@ joda-time test + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + @@ -375,7 +382,6 @@ javax.xml.bind jaxb-api - 2.3.1 provided true diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java new file mode 100644 index 000000000..93bdebeae --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java @@ -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. + * + *

+ * This class can be merged with {@link org.mapstruct.ap.internal.gem.GemGenerator} + * after the mentioned issue is resolved. + *

+ * + * @see Gem Tools issue #10 + * @author Iaroslav Bogdanchikov + */ +@GemDefinition(XmlElementDecl.class) +@GemDefinition(XmlElementRef.class) +class JakartaGemGenerator { +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java index 0edae7f10..6cd1605b2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java @@ -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) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java index 0d3d4c30a..2a5d1639e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java @@ -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 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() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java new file mode 100644 index 000000000..df5cd848a --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java @@ -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() ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java new file mode 100644 index 000000000..1d02e97e9 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java @@ -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() ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index de429174d..519e1c3d6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -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(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java index 7bdae0b77..91b4b5ca1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java @@ -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: *
    *
  1. Name and Scope matches
  2. @@ -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; + } + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java index db18673ba..c89877062 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java @@ -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() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java index 2dc41b822..c8ee922af 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java @@ -29,6 +29,8 @@ import org.mapstruct.ap.test.builtin._target.MapTarget; import org.mapstruct.ap.test.builtin.bean.BigDecimalProperty; import org.mapstruct.ap.test.builtin.bean.CalendarProperty; import org.mapstruct.ap.test.builtin.bean.DateProperty; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementListProperty; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementProperty; import org.mapstruct.ap.test.builtin.bean.JaxbElementListProperty; import org.mapstruct.ap.test.builtin.bean.JaxbElementProperty; import org.mapstruct.ap.test.builtin.bean.SomeType; @@ -45,6 +47,8 @@ import org.mapstruct.ap.test.builtin.mapper.CalendarToXmlGregCalMapper; import org.mapstruct.ap.test.builtin.mapper.DateToCalendarMapper; import org.mapstruct.ap.test.builtin.mapper.DateToXmlGregCalMapper; import org.mapstruct.ap.test.builtin.mapper.IterableSourceTargetMapper; +import org.mapstruct.ap.test.builtin.mapper.JakartaJaxbListMapper; +import org.mapstruct.ap.test.builtin.mapper.JakartaJaxbMapper; import org.mapstruct.ap.test.builtin.mapper.JaxbListMapper; import org.mapstruct.ap.test.builtin.mapper.JaxbMapper; import org.mapstruct.ap.test.builtin.mapper.MapSourceTargetMapper; @@ -58,6 +62,7 @@ import org.mapstruct.ap.test.builtin.source.MapSource; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaJaxb; import org.mapstruct.ap.testutil.WithJavaxJaxb; import static org.assertj.core.api.Assertions.assertThat; @@ -101,6 +106,23 @@ public class BuiltInTest { assertThat( target.publicProp ).isEqualTo( "PUBLIC TEST" ); } + @ProcessorTest + @WithClasses( { + JakartaJaxbMapper.class, + JakartaJaxbElementProperty.class, + } ) + @WithJakartaJaxb + public void shouldApplyBuiltInOnJakartaJaxbElement() { + JakartaJaxbElementProperty source = new JakartaJaxbElementProperty(); + source.setProp( createJakartaJaxb( "TEST" ) ); + source.publicProp = createJakartaJaxb( "PUBLIC TEST" ); + + StringProperty target = JakartaJaxbMapper.INSTANCE.map( source ); + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( "TEST" ); + assertThat( target.publicProp ).isEqualTo( "PUBLIC TEST" ); + } + @ProcessorTest @WithClasses( { JaxbMapper.class, @@ -128,6 +150,33 @@ public class BuiltInTest { assertThat( target2.getProp() ).isNotNull(); } + @ProcessorTest + @WithClasses( { + JakartaJaxbMapper.class, + JakartaJaxbElementProperty.class, + } ) + @WithJakartaJaxb + @IssueKey( "1698" ) + public void shouldApplyBuiltInOnJakartaJAXBElementExtra() { + JakartaJaxbElementProperty source = new JakartaJaxbElementProperty(); + source.setProp( createJakartaJaxb( "5" ) ); + source.publicProp = createJakartaJaxb( "5" ); + + BigDecimalProperty target = JakartaJaxbMapper.INSTANCE.mapBD( source ); + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( new BigDecimal( "5" ) ); + assertThat( target.publicProp ).isEqualTo( new BigDecimal( "5" ) ); + + JakartaJaxbElementProperty source2 = new JakartaJaxbElementProperty(); + source2.setProp( createJakartaJaxb( "5" ) ); + source2.publicProp = createJakartaJaxb( "5" ); + + SomeTypeProperty target2 = JakartaJaxbMapper.INSTANCE.mapSomeType( source2 ); + assertThat( target2 ).isNotNull(); + assertThat( target2.publicProp ).isNotNull(); + assertThat( target2.getProp() ).isNotNull(); + } + @ProcessorTest @WithClasses( { JaxbListMapper.class, @@ -147,6 +196,24 @@ public class BuiltInTest { assertThat( target.publicProp.get( 0 ) ).isEqualTo( "PUBLIC TEST2" ); } + @ProcessorTest + @WithClasses( { + JakartaJaxbListMapper.class, + JakartaJaxbElementListProperty.class, + } ) + @WithJakartaJaxb + @IssueKey( "141" ) + public void shouldApplyBuiltInOnJakartaJAXBElementList() { + JakartaJaxbElementListProperty source = new JakartaJaxbElementListProperty(); + source.setProp( createJakartaJaxbList( "TEST2" ) ); + source.publicProp = createJakartaJaxbList( "PUBLIC TEST2" ); + + StringListProperty target = JakartaJaxbListMapper.INSTANCE.map( source ); + assertThat( target ).isNotNull(); + assertThat( target.getProp().get( 0 ) ).isEqualTo( "TEST2" ); + assertThat( target.publicProp.get( 0 ) ).isEqualTo( "PUBLIC TEST2" ); + } + @ProcessorTest @WithClasses( DateToXmlGregCalMapper.class ) public void shouldApplyBuiltInOnDateToXmlGregCal() throws ParseException { @@ -414,12 +481,22 @@ public class BuiltInTest { return new JAXBElement<>( new QName( "www.mapstruct.org", "test" ), String.class, test ); } + private jakarta.xml.bind.JAXBElement createJakartaJaxb(String test) { + return new jakarta.xml.bind.JAXBElement<>( new QName( "www.mapstruct.org", "test" ), String.class, test ); + } + private List> createJaxbList(String test) { List> result = new ArrayList<>(); result.add( createJaxb( test ) ); return result; } + private List> createJakartaJaxbList(String test) { + List> result = new ArrayList<>(); + result.add( createJakartaJaxb( test ) ); + return result; + } + private Date createDate(String date) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "dd-M-yyyy hh:mm:ss" ); return sdf.parse( date ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java new file mode 100644 index 000000000..f6ab53725 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java @@ -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.builtin.bean; + +import java.util.List; + +import jakarta.xml.bind.JAXBElement; + +public class JakartaJaxbElementListProperty { + + // CHECKSTYLE:OFF + public List> publicProp; + // CHECKSTYLE:ON + + private List> prop; + + public List> getProp() { + return prop; + } + + public void setProp( List> prop ) { + this.prop = prop; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.java new file mode 100644 index 000000000..0336afe81 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.java @@ -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.builtin.bean; + +import jakarta.xml.bind.JAXBElement; + +public class JakartaJaxbElementProperty { + + // CHECKSTYLE:OFF + public JAXBElement publicProp; + // CHECKSTYLE:ON + + private JAXBElement prop; + + public JAXBElement getProp() { + return prop; + } + + public void setProp( JAXBElement prop ) { + this.prop = prop; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java new file mode 100644 index 000000000..602e2180f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java @@ -0,0 +1,19 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.builtin.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementListProperty; +import org.mapstruct.ap.test.builtin.bean.StringListProperty; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface JakartaJaxbListMapper { + + JakartaJaxbListMapper INSTANCE = Mappers.getMapper( JakartaJaxbListMapper.class ); + + StringListProperty map(JakartaJaxbElementListProperty source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java new file mode 100644 index 000000000..933479257 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java @@ -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.builtin.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.builtin.bean.BigDecimalProperty; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementProperty; +import org.mapstruct.ap.test.builtin.bean.SomeType; +import org.mapstruct.ap.test.builtin.bean.SomeTypeProperty; +import org.mapstruct.ap.test.builtin.bean.StringProperty; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface JakartaJaxbMapper { + + JakartaJaxbMapper INSTANCE = Mappers.getMapper( JakartaJaxbMapper.class ); + + StringProperty map(JakartaJaxbElementProperty source); + + BigDecimalProperty mapBD(JakartaJaxbElementProperty source); + + SomeTypeProperty mapSomeType(JakartaJaxbElementProperty source); + + @SuppressWarnings( "unused" ) + default SomeType map( String in ) { + return new SomeType(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java new file mode 100644 index 000000000..86c9d83a5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java @@ -0,0 +1,27 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Jakarta XML Binding dependencies. + * + * @author Iaroslav Bogdanchikov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "jakarta.xml.bind", +}) +public @interface WithJakartaJaxb { + +}