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 9675bf386..58bd17003 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 @@ -55,6 +55,7 @@ public class BuiltInMappingMethods { if ( isJava8TimeAvailable( typeFactory ) ) { builtInMethods.add( new ZonedDateTimeToCalendar( typeFactory ) ); + builtInMethods.add( new ZonedDateTimeToXmlGregorianCalendar( typeFactory ) ); builtInMethods.add( new CalendarToZonedDateTime( typeFactory ) ); if ( isXmlGregorianCalendarPresent ) { builtInMethods.add( new XmlGregorianCalendarToLocalDate( typeFactory ) ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.java new file mode 100644 index 000000000..a06e78da7 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.java @@ -0,0 +1,70 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.internal.model.source.builtin; + +import java.time.ZonedDateTime; +import java.util.GregorianCalendar; +import java.util.Set; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +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 static org.mapstruct.ap.internal.util.Collections.asSet; + +/** + * @author Christian Bandowski + */ +public class ZonedDateTimeToXmlGregorianCalendar extends BuiltInMethod { + + private final Parameter parameter; + private final Type returnType; + private final Set importTypes; + + public ZonedDateTimeToXmlGregorianCalendar(TypeFactory typeFactory) { + this.parameter = new Parameter( "zdt ", typeFactory.getType( ZonedDateTime.class ) ); + this.returnType = typeFactory.getType( XMLGregorianCalendar.class ); + + this.importTypes = asSet( + returnType, + parameter.getType(), + typeFactory.getType( DatatypeFactory.class ), + typeFactory.getType( GregorianCalendar.class ), + typeFactory.getType( DatatypeConfigurationException.class ) + ); + } + + @Override + public Set getImportTypes() { + return importTypes; + } + + @Override + public Parameter getParameter() { + return parameter; + } + + @Override + public Type getReturnType() { + return returnType; + } +} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.ftl index 2e74ffa07..4220268ea 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.ftl @@ -25,7 +25,7 @@ private <@includeModel object=findType("XMLGregorianCalendar")/> ${name}( <@incl } try { - <@includeModel object=findType("GregorianCalendar")/> gcal = new <@includeModel object=findType("GregorianCalendar")/>(); + <@includeModel object=findType("GregorianCalendar")/> gcal = new <@includeModel object=findType("GregorianCalendar")/>( cal.getTimeZone() ); gcal.setTimeInMillis( cal.getTimeInMillis() ); return <@includeModel object=findType("DatatypeFactory")/>.newInstance().newXMLGregorianCalendar( gcal ); } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.ftl new file mode 100644 index 000000000..116d5d352 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.ftl @@ -0,0 +1,33 @@ +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.VirtualMappingMethod" --> +<#-- + + Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + and/or other contributors as indicated by the @authors tag. See the + copyright.txt file in the distribution for a full listing of all + contributors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> +private <@includeModel object=findType("XMLGregorianCalendar")/> ${name}( <@includeModel object=findType("ZonedDateTime")/> zdt ) { + if ( zdt == null ) { + return null; + } + + try { + return <@includeModel object=findType("DatatypeFactory")/>.newInstance().newXMLGregorianCalendar( <@includeModel object=findType("GregorianCalendar")/>.from( zdt ) ); + } + catch ( <@includeModel object=findType("DatatypeConfigurationException")/> ex ) { + throw new RuntimeException( ex ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Mapper.java new file mode 100644 index 000000000..eb54dc6c0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Mapper.java @@ -0,0 +1,34 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._1523.java8; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Bandowski + */ +@Mapper +public abstract class Issue1523Mapper { + + public static final Issue1523Mapper INSTANCE = Mappers.getMapper( Issue1523Mapper.class ); + + public abstract Target map(Source source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java new file mode 100644 index 000000000..0ebde15c2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java @@ -0,0 +1,86 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._1523.java8; + +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.TimeZone; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * This test will evaluate if the conversion from {@code Calendar} to {@code XMLGregorianCalendar} works in case the + * default timezone was not used. Additionally a direct conversion between {@code ZonedDateTime} to + * {@code XMLGregorianCalendar} was added for this issue to improve readability / performance. This will be tested as + * well. + * + * @author Christian Bandowski + */ +@WithClasses({ + Issue1523Mapper.class, + Source.class, + Target.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +@IssueKey("1523") +public class Issue1523Test { + + private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); + + @BeforeClass + public static void before() { + // we want to test that the timezone will correctly be used in mapped XMLGregorianCalendar and not the + // default one, so we must ensure that we use a different timezone than the default one -> set the default + // one explicitly to UTC + TimeZone.setDefault( TimeZone.getTimeZone( "UTC" ) ); + } + + @AfterClass + public static void after() { + // revert the changed default TZ + TimeZone.setDefault( DEFAULT_TIMEZONE ); + } + + @Test + public void testThatCorrectTimeZoneWillBeUsedInTarget() { + Source source = new Source(); + // default one was explicitly set to UTC, thus +01:00 is a different one + source.setValue( ZonedDateTime.parse( "2018-06-15T00:00:00+01:00" ) ); + Calendar cal = Calendar.getInstance( TimeZone.getTimeZone( "GMT+01:00" ) ); + cal.set( 2018, 02, 15, 00, 00, 00 ); + source.setValue2( cal ); + + Target target = Issue1523Mapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNotNull(); + assertThat( target.getValue2() ).isNotNull(); + // +01:00 -> offset is 60 min + assertThat( target.getValue().getTimezone() ).isEqualTo( 60 ); + assertThat( target.getValue2().getTimezone() ).isEqualTo( 60 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Source.java new file mode 100644 index 000000000..5ccb45d9c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Source.java @@ -0,0 +1,43 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._1523.java8; + +import java.time.ZonedDateTime; +import java.util.Calendar; + +public class Source { + private ZonedDateTime value; + private Calendar value2; + + public ZonedDateTime getValue() { + return value; + } + + public void setValue(ZonedDateTime value) { + this.value = value; + } + + public Calendar getValue2() { + return value2; + } + + public void setValue2(Calendar value2) { + this.value2 = value2; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Target.java new file mode 100644 index 000000000..009c4ca62 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Target.java @@ -0,0 +1,42 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.test.bugs._1523.java8; + +import javax.xml.datatype.XMLGregorianCalendar; + +public class Target { + private XMLGregorianCalendar value; + private XMLGregorianCalendar value2; + + public XMLGregorianCalendar getValue() { + return value; + } + + public void setValue(XMLGregorianCalendar value) { + this.value = value; + } + + public XMLGregorianCalendar getValue2() { + return value2; + } + + public void setValue2(XMLGregorianCalendar value2) { + this.value2 = value2; + } +}