From 09943b2041dbbd23a57af38e996704772a252aec Mon Sep 17 00:00:00 2001 From: Timo E aus E Date: Thu, 10 Apr 2014 20:04:31 +0200 Subject: [PATCH] #75 integrated string to joda-time conversion --- parent/pom.xml | 7 + processor/pom.xml | 6 +- .../mapstruct/ap/conversion/Conversions.java | 10 ++ .../JodaTypeToStringConversion.java | 147 ++++++++++++++++++ .../jodatime/JodaConversionTest.java | 106 +++++++++++++ .../ap/test/conversion/jodatime/Source.java | 70 +++++++++ .../jodatime/SourceTargetMapper.java | 65 ++++++++ .../ap/test/conversion/jodatime/Target.java | 65 ++++++++ .../testutil/runner/CompilingStatement.java | 7 +- 9 files changed, 481 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/conversion/JodaTypeToStringConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/SourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Target.java diff --git a/parent/pom.xml b/parent/pom.xml index f00b641db..eb6533f69 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -177,6 +177,13 @@ ${org.springframework.version} + + + joda-time + joda-time + 2.3 + + ${project.groupId} diff --git a/processor/pom.xml b/processor/pom.xml index df04136bd..87e8ea0df 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -57,7 +57,11 @@ mapstruct provided - + + + joda-time + joda-time + junit diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java index 15e977fa8..4fdd365cc 100644 --- a/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java @@ -25,6 +25,10 @@ import java.util.HashMap; import java.util.Map; import javax.lang.model.util.Elements; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.TypeFactory; @@ -177,6 +181,12 @@ public class Conversions { register( BigInteger.class, String.class, new BigIntegerToStringConversion() ); register( BigDecimal.class, String.class, new BigDecimalToStringConversion() ); + // joda + register( DateTime.class, String.class, new JodaTypeToStringConversion( DateTime.class ) ); + register( LocalDate.class, String.class, new JodaTypeToStringConversion( LocalDate.class ) ); + register( LocalTime.class, String.class, new JodaTypeToStringConversion( LocalTime.class ) ); + register( LocalDateTime.class, String.class, new JodaTypeToStringConversion( LocalDateTime.class ) ); + //misc. register( Enum.class, String.class, new EnumStringConversion() ); register( Date.class, String.class, new DateToStringConversion() ); diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/JodaTypeToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/conversion/JodaTypeToStringConversion.java new file mode 100644 index 000000000..907bfd2bd --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/conversion/JodaTypeToStringConversion.java @@ -0,0 +1,147 @@ +/** + * Copyright 2012-2014 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.conversion; + +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; +import org.joda.time.format.DateTimeFormat; +import org.mapstruct.ap.model.TypeConversion; +import org.mapstruct.ap.model.common.ConversionContext; +import org.mapstruct.ap.model.common.Type; + +import java.util.Collections; +import java.util.Locale; + +import static org.mapstruct.ap.util.Collections.asSet; + +/** + * + */ +public class JodaTypeToStringConversion implements ConversionProvider { + + private final Class sourceType; + + public JodaTypeToStringConversion(Class sourceType) { + + this.sourceType = sourceType; + } + + public TypeConversion to(String sourceReference, ConversionContext conversionContext) { + return new TypeConversion( + asSet( + conversionContext.getTypeFactory().getType( DateTimeFormat.class ), + conversionContext.getTypeFactory().getType( DateTime.class ) ), + Collections.emptyList(), + conversionString( sourceReference, conversionContext, "print" ) + ); + } + + public TypeConversion from(String targetReference, ConversionContext conversionContext) { + return new TypeConversion( + asSet( + conversionContext.getTypeFactory().getType( DateTimeFormat.class ), + conversionContext.getTypeFactory().getType( DateTime.class ) ), + Collections.emptyList(), + conversionString( + targetReference, + conversionContext, + parseMethodForTargetType( conversionContext ) ) + ); + } + + private String parseMethodForTargetType(ConversionContext conversionContext) { + if ( DateTime.class.getName().equals( conversionContext.getTargetType().getFullyQualifiedName() ) ) { + return "parseDateTime"; + } + else if ( LocalDateTime.class.getName().equals( conversionContext.getTargetType().getFullyQualifiedName() ) ) { + return "parseLocalDateTime"; + } + else if ( LocalDate.class.getName().equals( conversionContext.getTargetType().getFullyQualifiedName() ) ) { + return "parseLocalDate"; + } + else if ( LocalTime.class.getName().equals( conversionContext.getTargetType().getFullyQualifiedName() ) ) { + return "parseLocalTime"; + } + else { + throw new RuntimeException( + String.format( + "Joda type %s not supported (yet)", + conversionContext.getTargetType() ) + ); + } + } + + private String conversionString(String reference, ConversionContext conversionContext, String method) { + StringBuilder conversionString = new StringBuilder( "DateTimeFormat" ); + conversionString.append( dateFormatPattern( conversionContext ) ); + conversionString.append( "." ); + conversionString.append( method ); + conversionString.append( "( " ); + conversionString.append( reference ); + conversionString.append( " )" ); + return conversionString.toString(); + } + + private String dateFormatPattern(ConversionContext conversionContext) { + String dateFormat = conversionContext.getDateFormat(); + if ( isBlank( dateFormat ) ) { + dateFormat = defaultDateFormatPattern( sourceType ); + } + StringBuilder conversionString = new StringBuilder(); + conversionString.append( ".forPattern( \"" ); + conversionString.append( dateFormat ); + conversionString.append( "\" )" ); + return conversionString.toString(); + } + + public static String defaultDateFormatPattern(Class sourceType) { + return DateTimeFormat.patternForStyle( formatStyle( sourceType ), Locale.getDefault() ); + } + + private static String formatStyle(Class sourceType) { + if ( DateTime.class == sourceType ) { + return "LL"; + } + else if ( LocalDateTime.class == sourceType ) { + return "LL"; + } + else if ( LocalDate.class == sourceType ) { + return "L-"; + } + else if ( LocalTime.class == sourceType ) { + return "-L"; + } + else { + throw new RuntimeException( + String.format( + "Joda type %s not supported (yet)", + sourceType ) + ); + } + } + + // ugly. would be nice to have something like commons-lang or + // guava on classpath. Or something homebrewn for + // these kind of checks, assertions, etc. + private boolean isBlank(String str) { + return str == null || ( str != null && str.trim().equals( "" ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java new file mode 100644 index 000000000..67db3032e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java @@ -0,0 +1,106 @@ +/** + * Copyright 2012-2014 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.conversion.jodatime; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; +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.fest.assertions.Assertions.assertThat; + +/** + * + */ +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses( { Source.class, Target.class, SourceTargetMapper.class } ) +@IssueKey( "75" ) +public class JodaConversionTest { + + @Test + public void testDateTimeToString() { + Source src = new Source(); + src.setDateTime( new DateTime( 2014, 1, 1, 0, 0, 0, DateTimeZone.UTC ) ); + Target target = SourceTargetMapper.INSTANCE.sourceToTargetDateTimeMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); + } + + @Test + public void testLocalDateTimeToString() { + Source src = new Source(); + src.setLocalDateTime( new LocalDateTime( 2014, 1, 1, 0, 0 ) ); + Target target = SourceTargetMapper.INSTANCE.sourceToTargetLocalDateTimeMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); + } + + @Test + public void testLocalDateToString() { + Source src = new Source(); + src.setLocalDate( new LocalDate( 2014, 1, 1 ) ); + Target target = SourceTargetMapper.INSTANCE.sourceToTargetLocalDateMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); + } + + @Test + public void testLocalTimeToString() { + Source src = new Source(); + src.setLocalTime( new LocalTime( 0, 0 ) ); + Target target = SourceTargetMapper.INSTANCE.sourceToTargetLocalTimeMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); + } + + @Test + public void testSourceToTargetMapping() { + Source src = new Source(); + src.setLocalTime( new LocalTime( 0, 0 ) ); + src.setLocalDate( new LocalDate( 2014, 1, 1 ) ); + src.setLocalDateTime( new LocalDateTime( 2014, 1, 1, 0, 0 ) ); + src.setDateTime( new DateTime( 2014, 1, 1, 0, 0, 0, DateTimeZone.UTC ) ); + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( src ); + assertThat( target ).isNotNull(); + assertThat( target.getDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); + assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); + assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); + assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); + } + + @Test + public void testStringToDateTime() { + String dateTimeAsString = "01.01.2014 00:00 UTC"; + Target target = new Target(); + target.setDateTime( dateTimeAsString ); + DateTime sourceDateTime = + new DateTime( 2014, 1, 1, 0, 0, 0, DateTimeZone.UTC ); + + Source src = SourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( src ).isNotNull(); + assertThat( src.getDateTime() ).isEqualTo( sourceDateTime ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Source.java new file mode 100644 index 000000000..35638027f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Source.java @@ -0,0 +1,70 @@ +/** + * Copyright 2012-2014 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.conversion.jodatime; + +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; + +/** + * + */ +public class Source { + + private DateTime dateTime; + + private LocalDateTime localDateTime; + + private LocalDate localDate; + + private LocalTime localTime; + + public DateTime getDateTime() { + return dateTime; + } + + public void setDateTime(DateTime dateTime) { + this.dateTime = dateTime; + } + + public LocalDateTime getLocalDateTime() { + return localDateTime; + } + + public void setLocalDateTime(LocalDateTime localDateTime) { + this.localDateTime = localDateTime; + } + + public LocalDate getLocalDate() { + return localDate; + } + + public void setLocalDate(LocalDate localDate) { + this.localDate = localDate; + } + + public LocalTime getLocalTime() { + return localTime; + } + + public void setLocalTime(LocalTime localTime) { + this.localTime = localTime; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/SourceTargetMapper.java new file mode 100644 index 000000000..cd41934b2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/SourceTargetMapper.java @@ -0,0 +1,65 @@ +/** + * Copyright 2012-2014 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.conversion.jodatime; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +/** + * + */ +@Mapper +public interface SourceTargetMapper { + + String DATE_TIME_FORMAT = "dd.MM.yyyy HH:mm z"; + + String LOCAL_DATE_TIME_FORMAT = "dd.MM.yyyy HH:mm"; + + String LOCAL_DATE_FORMAT = "dd.MM.yyyy"; + + String LOCAL_TIME_FORMAT = "HH:mm"; + + SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + @Mappings({ @Mapping( source = "dateTime", dateFormat = DATE_TIME_FORMAT ), + @Mapping( source = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT ), + @Mapping( source = "localDate", dateFormat = LOCAL_DATE_FORMAT ), + @Mapping( source = "localTime", dateFormat = LOCAL_TIME_FORMAT ) }) + Target sourceToTarget(Source source); + + @Mapping( source = "dateTime", dateFormat = DATE_TIME_FORMAT ) + Target sourceToTargetDateTimeMapped(Source source); + + @Mapping( source = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT ) + Target sourceToTargetLocalDateTimeMapped(Source source); + + @Mapping( source = "localDate", dateFormat = LOCAL_DATE_FORMAT ) + Target sourceToTargetLocalDateMapped(Source source); + + @Mapping( source = "localTime", dateFormat = LOCAL_TIME_FORMAT ) + Target sourceToTargetLocalTimeMapped(Source source); + + + @Mappings({ @Mapping( source = "dateTime", dateFormat = DATE_TIME_FORMAT ), + @Mapping( source = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT ), + @Mapping( source = "localDate", dateFormat = LOCAL_DATE_FORMAT ), + @Mapping( source = "localTime", dateFormat = LOCAL_TIME_FORMAT ) }) + Source targetToSource(Target target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Target.java new file mode 100644 index 000000000..6d227009b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/Target.java @@ -0,0 +1,65 @@ +/** + * Copyright 2012-2014 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.conversion.jodatime; + +/** + * + */ +public class Target { + + private String dateTime; + + private String localDateTime; + + private String localDate; + + private String localTime; + + public String getDateTime() { + return dateTime; + } + + public void setDateTime(String dateTime) { + this.dateTime = dateTime; + } + + public String getLocalDateTime() { + return localDateTime; + } + + public void setLocalDateTime(String localDateTime) { + this.localDateTime = localDateTime; + } + + public String getLocalDate() { + return localDate; + } + + public void setLocalDate(String localDate) { + this.localDate = localDate; + } + + public String getLocalTime() { + return localTime; + } + + public void setLocalTime(String localTime) { + this.localTime = localTime; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java index f3c1bb26d..c70fcd0a1 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java @@ -87,7 +87,12 @@ class CompilingStatement extends Statement { } }; - private static final List LIBRARIES = Arrays.asList( "mapstruct.jar", "guava.jar", "javax.inject.jar" ); + private static final List LIBRARIES = Arrays.asList( + "mapstruct.jar", + "guava.jar", + "javax.inject.jar", + "joda-time.jar" + ); private final Statement next; private final FrameworkMethod method;