Skip to content

Commit f4c9313

Browse files
authored
mapstruct#1791 Support for conversion between java.time.LocalDateTime and javax.xml.datatype.XMLGregorianCalendar (mapstruct#1894)
1 parent fcdf852 commit f4c9313

File tree

11 files changed

+379
-5
lines changed

11 files changed

+379
-5
lines changed

documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ public interface CarMapper {
9090

9191
* Between Jodas `org.joda.time.LocalDateTime`, `org.joda.time.LocalDate` and `javax.xml.datatype.XMLGregorianCalendar`, `java.util.Date`.
9292

93+
* Between `java.time.LocalDate`, `java.time.LocalDateTime` and `javax.xml.datatype.XMLGregorianCalendar`.
94+
9395
* Between `java.time.ZonedDateTime`, `java.time.LocalDateTime`, `java.time.LocalDate`, `java.time.LocalTime` from Java 8 Date-Time package and `String`. A format string as understood by `java.text.SimpleDateFormat` can be specified via the `dateFormat` option (see above).
9496

9597
* Between `java.time.Instant`, `java.time.Duration`, `java.time.Period` from Java 8 Date-Time package and `String` using the `parse` method in each class to map from `String` and using `toString` to map into `String`.

processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public BuiltInMappingMethods(TypeFactory typeFactory) {
3333
builtInMethods.add( new CalendarToXmlGregorianCalendar( typeFactory ) );
3434
builtInMethods.add( new XmlGregorianCalendarToCalendar( typeFactory ) );
3535
builtInMethods.add( new ZonedDateTimeToXmlGregorianCalendar( typeFactory ) );
36+
builtInMethods.add( new XmlGregorianCalendarToLocalDate( typeFactory ) );
37+
builtInMethods.add( new LocalDateToXmlGregorianCalendar( typeFactory ) );
38+
builtInMethods.add( new LocalDateTimeToXmlGregorianCalendar( typeFactory ) );
39+
builtInMethods.add( new XmlGregorianCalendarToLocalDateTime( typeFactory ) );
3640
}
3741

3842
if ( isJaxbAvailable( typeFactory ) ) {
@@ -41,10 +45,6 @@ public BuiltInMappingMethods(TypeFactory typeFactory) {
4145

4246
builtInMethods.add( new ZonedDateTimeToCalendar( typeFactory ) );
4347
builtInMethods.add( new CalendarToZonedDateTime( typeFactory ) );
44-
if ( isXmlGregorianCalendarPresent ) {
45-
builtInMethods.add( new XmlGregorianCalendarToLocalDate( typeFactory ) );
46-
builtInMethods.add( new LocalDateToXmlGregorianCalendar( typeFactory ) );
47-
}
4848

4949
if ( isJodaTimeAvailable( typeFactory ) && isXmlGregorianCalendarPresent ) {
5050
builtInMethods.add( new JodaDateTimeToXmlGregorianCalendar( typeFactory ) );

processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public String getName() {
5151
* @return the types used by this method for which import statements need to be generated
5252
*/
5353
public Set<Type> getImportTypes() {
54-
return Collections.<Type>emptySet();
54+
return Collections.emptySet();
5555
}
5656

5757
/**
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.internal.model.source.builtin;
7+
8+
import java.time.LocalDateTime;
9+
import java.time.temporal.ChronoField;
10+
import java.util.Set;
11+
12+
import javax.xml.datatype.DatatypeConstants;
13+
14+
import org.mapstruct.ap.internal.model.common.Parameter;
15+
import org.mapstruct.ap.internal.model.common.Type;
16+
import org.mapstruct.ap.internal.model.common.TypeFactory;
17+
18+
import static org.mapstruct.ap.internal.util.Collections.asSet;
19+
20+
/**
21+
* @author Andrei Arlou
22+
*/
23+
public class LocalDateTimeToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar {
24+
25+
private final Parameter parameter;
26+
private final Set<Type> importTypes;
27+
28+
public LocalDateTimeToXmlGregorianCalendar(TypeFactory typeFactory) {
29+
super( typeFactory );
30+
this.parameter = new Parameter( "localDateTime", typeFactory.getType( LocalDateTime.class ) );
31+
this.importTypes = asSet(
32+
parameter.getType(),
33+
typeFactory.getType( DatatypeConstants.class ),
34+
typeFactory.getType( ChronoField.class )
35+
);
36+
}
37+
38+
@Override
39+
public Set<Type> getImportTypes() {
40+
Set<Type> result = super.getImportTypes();
41+
result.addAll( importTypes );
42+
return result;
43+
}
44+
45+
@Override
46+
public Parameter getParameter() {
47+
return parameter;
48+
}
49+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.internal.model.source.builtin;
7+
8+
import java.time.Duration;
9+
import java.time.LocalDateTime;
10+
import java.util.Set;
11+
12+
import javax.xml.datatype.DatatypeConstants;
13+
import javax.xml.datatype.XMLGregorianCalendar;
14+
15+
import org.mapstruct.ap.internal.model.common.Parameter;
16+
import org.mapstruct.ap.internal.model.common.Type;
17+
import org.mapstruct.ap.internal.model.common.TypeFactory;
18+
19+
import static org.mapstruct.ap.internal.util.Collections.asSet;
20+
21+
/**
22+
* @author Andrei Arlou
23+
*/
24+
public class XmlGregorianCalendarToLocalDateTime extends BuiltInMethod {
25+
26+
private final Parameter parameter;
27+
private final Type returnType;
28+
private final Set<Type> importTypes;
29+
30+
public XmlGregorianCalendarToLocalDateTime(TypeFactory typeFactory) {
31+
this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) );
32+
this.returnType = typeFactory.getType( LocalDateTime.class );
33+
this.importTypes = asSet(
34+
returnType,
35+
parameter.getType(),
36+
typeFactory.getType( DatatypeConstants.class ),
37+
typeFactory.getType( Duration.class )
38+
);
39+
}
40+
41+
@Override
42+
public Parameter getParameter() {
43+
return parameter;
44+
}
45+
46+
@Override
47+
public Type getReturnType() {
48+
return returnType;
49+
}
50+
51+
@Override
52+
public Set<Type> getImportTypes() {
53+
return importTypes;
54+
}
55+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<#--
2+
3+
Copyright MapStruct Authors.
4+
5+
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
-->
8+
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.SupportingMappingMethod" -->
9+
private <@includeModel object=findType("XMLGregorianCalendar")/> ${name}( <@includeModel object=findType("java.time.LocalDateTime")/> localDateTime ) {
10+
if ( localDateTime == null ) {
11+
return null;
12+
}
13+
14+
return ${supportingField.variableName}.newXMLGregorianCalendar(
15+
localDateTime.getYear(),
16+
localDateTime.getMonthValue(),
17+
localDateTime.getDayOfMonth(),
18+
localDateTime.getHour(),
19+
localDateTime.getMinute(),
20+
localDateTime.getSecond(),
21+
localDateTime.get( ChronoField.MILLI_OF_SECOND ),
22+
<@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED );
23+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<#--
2+
3+
Copyright MapStruct Authors.
4+
5+
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
-->
8+
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.SupportingMappingMethod" -->
9+
private static <@includeModel object=findType("java.time.LocalDateTime")/> ${name}( <@includeModel object=findType("XMLGregorianCalendar")/> xcal ) {
10+
if ( xcal == null ) {
11+
return null;
12+
}
13+
14+
if ( xcal.getYear() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED
15+
&& xcal.getMonth() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED
16+
&& xcal.getDay() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED
17+
&& xcal.getHour() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED
18+
&& xcal.getMinute() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED
19+
) {
20+
if ( xcal.getSecond() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED
21+
&& xcal.getMillisecond() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED ) {
22+
return <@includeModel object=findType("java.time.LocalDateTime")/>.of(
23+
xcal.getYear(),
24+
xcal.getMonth(),
25+
xcal.getDay(),
26+
xcal.getHour(),
27+
xcal.getMinute(),
28+
xcal.getSecond(),
29+
Duration.ofMillis( xcal.getMillisecond() ).getNano()
30+
);
31+
}
32+
else if ( xcal.getSecond() != <@includeModel object=findType("DatatypeConstants")/>.FIELD_UNDEFINED ) {
33+
return <@includeModel object=findType("java.time.LocalDateTime")/>.of(
34+
xcal.getYear(),
35+
xcal.getMonth(),
36+
xcal.getDay(),
37+
xcal.getHour(),
38+
xcal.getMinute(),
39+
xcal.getSecond()
40+
);
41+
}
42+
else {
43+
return <@includeModel object=findType("java.time.LocalDateTime")/>.of(
44+
xcal.getYear(),
45+
xcal.getMonth(),
46+
xcal.getDay(),
47+
xcal.getHour(),
48+
xcal.getMinute()
49+
);
50+
}
51+
}
52+
return null;
53+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.conversion.java8time;
7+
8+
import java.time.LocalDateTime;
9+
import java.util.Calendar;
10+
11+
import javax.xml.datatype.DatatypeConfigurationException;
12+
import javax.xml.datatype.DatatypeConstants;
13+
import javax.xml.datatype.DatatypeFactory;
14+
import javax.xml.datatype.XMLGregorianCalendar;
15+
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
import org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion.SourceTargetMapper;
19+
import org.mapstruct.ap.testutil.WithClasses;
20+
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
21+
import org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion.Source;
22+
import org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion.Target;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* @author Andrei Arlou
28+
*/
29+
@WithClasses({ Source.class, Target.class, SourceTargetMapper.class })
30+
@RunWith(AnnotationProcessorTestRunner.class)
31+
public class LocalDateTimeToXMLGregorianCalendarConversionTest {
32+
33+
@Test
34+
public void shouldNullCheckOnConversionToTarget() {
35+
Target target = SourceTargetMapper.INSTANCE.toTarget( new Source() );
36+
37+
assertThat( target ).isNotNull();
38+
assertThat( target.getLocalDateTime() ).isNull();
39+
}
40+
41+
@Test
42+
public void shouldNullCheckOnConversionToSource() {
43+
Source source = SourceTargetMapper.INSTANCE.toSource( new Target() );
44+
45+
assertThat( source ).isNotNull();
46+
assertThat( source.getXmlGregorianCalendar() ).isNull();
47+
}
48+
49+
@Test
50+
public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithNanoseconds()
51+
throws DatatypeConfigurationException {
52+
LocalDateTime localDateTime = LocalDateTime.of( 1994, Calendar.MARCH, 5, 11, 30, 50, 9000000 );
53+
Target target = new Target();
54+
target.setLocalDateTime( localDateTime );
55+
56+
Source source = SourceTargetMapper.INSTANCE.toSource( target );
57+
58+
XMLGregorianCalendar expectedCalendar = DatatypeFactory.newInstance()
59+
.newXMLGregorianCalendar( 1994, Calendar.MARCH, 5, 11, 30, 50, 9,
60+
DatatypeConstants.FIELD_UNDEFINED
61+
);
62+
63+
assertThat( source ).isNotNull();
64+
assertThat( source.getXmlGregorianCalendar() ).isEqualTo( expectedCalendar );
65+
}
66+
67+
@Test
68+
public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithSeconds()
69+
throws DatatypeConfigurationException {
70+
LocalDateTime localDateTime = LocalDateTime.of( 1994, Calendar.MARCH, 5, 11, 30, 50 );
71+
Target target = new Target();
72+
target.setLocalDateTime( localDateTime );
73+
74+
Source source = SourceTargetMapper.INSTANCE.toSource( target );
75+
76+
XMLGregorianCalendar expectedCalendar = DatatypeFactory.newInstance()
77+
.newXMLGregorianCalendar( 1994, Calendar.MARCH, 5, 11, 30, 50, 0,
78+
DatatypeConstants.FIELD_UNDEFINED
79+
);
80+
81+
assertThat( source ).isNotNull();
82+
assertThat( source.getXmlGregorianCalendar() ).isEqualTo( expectedCalendar );
83+
}
84+
85+
@Test
86+
public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithMinutes()
87+
throws DatatypeConfigurationException {
88+
LocalDateTime localDateTime = LocalDateTime.of( 1994, Calendar.MARCH, 5, 11, 30 );
89+
Target target = new Target();
90+
target.setLocalDateTime( localDateTime );
91+
92+
Source source = SourceTargetMapper.INSTANCE.toSource( target );
93+
94+
XMLGregorianCalendar expectedCalendar = DatatypeFactory.newInstance()
95+
.newXMLGregorianCalendar( 1994, Calendar.MARCH, 5, 11, 30, 0, 0,
96+
DatatypeConstants.FIELD_UNDEFINED
97+
);
98+
99+
assertThat( source ).isNotNull();
100+
assertThat( source.getXmlGregorianCalendar() ).isEqualTo( expectedCalendar );
101+
}
102+
103+
@Test
104+
public void shouldMapXmlGregorianCalendarToLocalDateTimeCorrectly() throws DatatypeConfigurationException {
105+
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
106+
.newXMLGregorianCalendar( 1994, Calendar.MARCH, 5, 11, 30, 50, 500,
107+
DatatypeConstants.FIELD_UNDEFINED
108+
);
109+
110+
Source source = new Source();
111+
source.setXmlGregorianCalendar( xmlGregorianCalendar );
112+
113+
Target target = SourceTargetMapper.INSTANCE.toTarget( source );
114+
115+
LocalDateTime expectedLocalDateTime = LocalDateTime.of( 1994, Calendar.MARCH, 5, 11, 30, 50, 500000000 );
116+
117+
assertThat( target ).isNotNull();
118+
assertThat( target.getLocalDateTime() ).isEqualTo( expectedLocalDateTime );
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion;
7+
8+
import javax.xml.datatype.XMLGregorianCalendar;
9+
10+
/**
11+
* @author Andrei Arlou
12+
*/
13+
public class Source {
14+
private XMLGregorianCalendar xmlGregorianCalendar;
15+
16+
public XMLGregorianCalendar getXmlGregorianCalendar() {
17+
return xmlGregorianCalendar;
18+
}
19+
20+
public void setXmlGregorianCalendar(XMLGregorianCalendar xmlGregorianCalendar) {
21+
this.xmlGregorianCalendar = xmlGregorianCalendar;
22+
}
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion;
7+
8+
import org.mapstruct.Mapper;
9+
import org.mapstruct.Mapping;
10+
import org.mapstruct.factory.Mappers;
11+
12+
/**
13+
* @author Andrei Arlou
14+
*/
15+
@Mapper
16+
public interface SourceTargetMapper {
17+
18+
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
19+
20+
@Mapping(source = "xmlGregorianCalendar", target = "localDateTime")
21+
Target toTarget(Source source);
22+
23+
@Mapping(source = "localDateTime", target = "xmlGregorianCalendar")
24+
Source toSource(Target target);
25+
}

0 commit comments

Comments
 (0)