From ebbeb7c5491ce88beead5078ae9549c76b73f221 Mon Sep 17 00:00:00 2001 From: teckhard <7und4zig3lf> Date: Mon, 22 Dec 2014 12:55:32 +0100 Subject: [PATCH] =?UTF-8?q?#224=20extended=20DefaultConversionContext=20fo?= =?UTF-8?q?r=20verifying=20the=20dateFormat=20if=20defined.=20Based=20on?= =?UTF-8?q?=20the=20source/target=20type=20it=C2=B4s=20checked=20against?= =?UTF-8?q?=20SimpleDateFormat,=20Joda=20Time=20or=20Java=208=20Time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractJodaTypeToStringConversion.java | 1 + .../mapstruct/ap/conversion/Conversions.java | 1 + .../JodaDateTimeToStringConversion.java | 2 + .../JodaLocalDateTimeToStringConversion.java | 2 + .../common/DateFormatValidationResult.java | 49 +++++ .../ap/model/common/DateFormatValidator.java | 36 ++++ .../common/DateFormatValidatorFactory.java | 192 ++++++++++++++++++ .../common/DefaultConversionContext.java | 37 +++- .../creation/MappingResolverImpl.java | 6 +- .../JodaTimeConstants.java | 2 +- .../DateFormatValidatorFactoryTest.java | 184 +++++++++++++++++ .../common/DefaultConversionContextTest.java | 147 ++++++++++++++ 12 files changed, 652 insertions(+), 7 deletions(-) mode change 100644 => 100755 processor/src/main/java/org/mapstruct/ap/conversion/AbstractJodaTypeToStringConversion.java mode change 100644 => 100755 processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java mode change 100644 => 100755 processor/src/main/java/org/mapstruct/ap/conversion/JodaDateTimeToStringConversion.java mode change 100644 => 100755 processor/src/main/java/org/mapstruct/ap/conversion/JodaLocalDateTimeToStringConversion.java create mode 100755 processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidationResult.java create mode 100755 processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidator.java create mode 100755 processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidatorFactory.java mode change 100644 => 100755 processor/src/main/java/org/mapstruct/ap/model/common/DefaultConversionContext.java mode change 100644 => 100755 processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java rename processor/src/main/java/org/mapstruct/ap/{conversion => util}/JodaTimeConstants.java (97%) mode change 100644 => 100755 create mode 100755 processor/src/test/java/org/mapstruct/ap/model/common/DateFormatValidatorFactoryTest.java create mode 100755 processor/src/test/java/org/mapstruct/ap/model/common/DefaultConversionContextTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/AbstractJodaTypeToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/conversion/AbstractJodaTypeToStringConversion.java old mode 100644 new mode 100755 index bd966bbf7..cff6ef505 --- a/processor/src/main/java/org/mapstruct/ap/conversion/AbstractJodaTypeToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/conversion/AbstractJodaTypeToStringConversion.java @@ -23,6 +23,7 @@ import java.util.Set; import org.mapstruct.ap.model.common.ConversionContext; import org.mapstruct.ap.model.common.Type; +import org.mapstruct.ap.util.JodaTimeConstants; import org.mapstruct.ap.util.Strings; import static org.mapstruct.ap.util.Collections.asSet; diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java old mode 100644 new mode 100755 index 0e20d0f2c..b470d69cd --- a/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/conversion/Conversions.java @@ -29,6 +29,7 @@ import java.util.Map; import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.util.JavaTimeConstants; +import org.mapstruct.ap.util.JodaTimeConstants; import org.mapstruct.ap.util.NativeTypes; import static org.mapstruct.ap.conversion.ReverseConversion.reverse; diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/JodaDateTimeToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/conversion/JodaDateTimeToStringConversion.java old mode 100644 new mode 100755 index e3dc17e58..8aedaa269 --- a/processor/src/main/java/org/mapstruct/ap/conversion/JodaDateTimeToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/conversion/JodaDateTimeToStringConversion.java @@ -18,6 +18,8 @@ */ package org.mapstruct.ap.conversion; +import org.mapstruct.ap.util.JodaTimeConstants; + /** * Conversion between {@code DateTime} and {@code String}. * diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/JodaLocalDateTimeToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/conversion/JodaLocalDateTimeToStringConversion.java old mode 100644 new mode 100755 index 5f7591fbc..beb890dd3 --- a/processor/src/main/java/org/mapstruct/ap/conversion/JodaLocalDateTimeToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/conversion/JodaLocalDateTimeToStringConversion.java @@ -18,6 +18,8 @@ */ package org.mapstruct.ap.conversion; +import org.mapstruct.ap.util.JodaTimeConstants; + /** * Conversion between {@code LocalDateTime} and {@code String}. * diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidationResult.java b/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidationResult.java new file mode 100755 index 000000000..c7f65f930 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidationResult.java @@ -0,0 +1,49 @@ +/** + * 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.model.common; + +/** + * Reflects the result of a date format validation + */ +final class DateFormatValidationResult { + + private final boolean isValid; + private final String validationInformation; + + /** + * Create a new instance. + * + * @param isValid determines of the validation was successful. + * @param validationInformation a string representing the validation result + */ + DateFormatValidationResult(boolean isValid, String validationInformation) { + + this.isValid = isValid; + this.validationInformation = validationInformation; + } + + public boolean isValid() { + return isValid; + } + + public String validationInformation() { + return validationInformation; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidator.java b/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidator.java new file mode 100755 index 000000000..835348286 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidator.java @@ -0,0 +1,36 @@ +/** + * 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.model.common; + +/** + * An abstraction for validating {@link ConversionContext#getDateFormat()}. There are implementers for different date + * types such as Joda Time, Java8 Time and java.util.Date. + */ +interface DateFormatValidator { + + /** + * Validate the given dateFormat. + * + * @param dateFormat string supposed to be used for date formatting and parsing. + * + * @return {@link DateFormatValidationResult} representing the validation result. + */ + DateFormatValidationResult validate(String dateFormat); + +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidatorFactory.java b/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidatorFactory.java new file mode 100755 index 000000000..62916610e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/common/DateFormatValidatorFactory.java @@ -0,0 +1,192 @@ +/** + * 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.model.common; + +import org.mapstruct.ap.util.JavaTimeConstants; +import org.mapstruct.ap.util.JodaTimeConstants; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; + +/** + * Factory for {@link DateFormatValidator}.

Based on the types of source / target type a specific {@link + * DateFormatValidator} will be instantiated.

are supported.

+ */ +final class DateFormatValidatorFactory { + + private static final String JAVA_UTIL_DATE = "java.util.Date"; + + private static final String JAVA_TIME_FORMAT_DATE_TIME_FORMATTER = "java.time.format.DateTimeFormatter"; + + private static final String OF_PATTERN = "ofPattern"; + + private static final String ORG_JODA_TIME_FORMAT_DATE_TIME_FORMAT = "org.joda.time.format.DateTimeFormat"; + + private static final String FOR_PATTERN = "forPattern"; + private static final String JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR = "javax.xml.datatype.XMLGregorianCalendar"; + + private DateFormatValidatorFactory() { + } + + /** + * Create a new {@link DateFormatValidator} based on source/target type of a type conversion. Theirs typenames will + * be compared against the supported types to determine a validator. + * + * @param sourceType The source type + * @param targetType The target type + * @return a new {@link DateFormatValidator} + */ + public static DateFormatValidator forTypes(final Type sourceType, final Type targetType) { + DateFormatValidator dateFormatValidator; + + if ( isJavaUtilDateSupposed( sourceType, targetType ) ) { + dateFormatValidator = new SimpleDateFormatValidator(); + } + else if ( isXmlGregorianCalendarSupposedToBeMapped( sourceType, targetType ) ) { + dateFormatValidator = new SimpleDateFormatValidator(); + } + else if ( isJava8DateTimeSupposed( sourceType, targetType ) ) { + dateFormatValidator = new JavaDateTimeDateFormatValidator(); + } + else if ( isJodaDateTimeSupposed( sourceType, targetType ) ) { + dateFormatValidator = new JodaTimeDateFormatValidator(); + } + else { + dateFormatValidator = new DateFormatValidator() { + @Override + public DateFormatValidationResult validate(String dateFormat) { + return new DateFormatValidationResult( + true, String.format( + "No dateFormat cheeck is supported for types %s, %s", sourceType, targetType ) ); + } + }; + } + return dateFormatValidator; + + } + + private static boolean isXmlGregorianCalendarSupposedToBeMapped(Type sourceType, Type targetType) { + return typesEqualsOneOf( + sourceType, targetType, JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR ); + } + + private static boolean isJodaDateTimeSupposed(Type sourceType, Type targetType) { + return typesEqualsOneOf( + sourceType, + targetType, + JodaTimeConstants.LOCAL_DATE_FQN, + JodaTimeConstants.LOCAL_TIME_FQN, + JodaTimeConstants.LOCAL_DATE_TIME_FQN, + JodaTimeConstants.DATE_TIME_FQN ); + } + + private static boolean isJava8DateTimeSupposed(Type sourceType, Type targetType) { + return typesEqualsOneOf( + sourceType, + targetType, + JavaTimeConstants.LOCAL_DATE_FQN, + JavaTimeConstants.LOCAL_TIME_FQN, + JavaTimeConstants.LOCAL_DATE_TIME_FQN, + JavaTimeConstants.ZONED_DATE_TIME_FQN ); + } + + private static boolean isJavaUtilDateSupposed(Type sourceType, Type targetType) { + return JAVA_UTIL_DATE.equals( sourceType.getFullyQualifiedName() ) || JAVA_UTIL_DATE.equals( + targetType.getFullyQualifiedName() ); + } + + private static boolean typesEqualsOneOf(Type sourceType, Type targetType, String... typeNames) { + for ( String typeName : typeNames ) { + if ( typeName.equals( sourceType.getFullyQualifiedName() ) + || typeName.equals( targetType.getFullyQualifiedName() ) ) { + return true; + } + } + return false; + } + + private static class JavaDateTimeDateFormatValidator implements DateFormatValidator { + @Override + public DateFormatValidationResult validate(String dateFormat) { + try { + Class aClass = Class.forName( JAVA_TIME_FORMAT_DATE_TIME_FORMATTER ); + Method ofPatternMethod = aClass.getMethod( OF_PATTERN, String.class ); + ofPatternMethod.invoke( aClass, dateFormat ); + return validDateFormat( dateFormat ); + } + catch ( InvocationTargetException e ) { + return invalidDateFormat( dateFormat, e.getCause() ); + } + catch ( Exception e ) { + return invalidDateFormat( dateFormat, e ); + } + } + } + + private static class JodaTimeDateFormatValidator implements DateFormatValidator { + + @Override + public DateFormatValidationResult validate(String dateFormat) { + try { + Class aClass = Class.forName( ORG_JODA_TIME_FORMAT_DATE_TIME_FORMAT ); + Method forPatternMethod = aClass.getMethod( FOR_PATTERN, String.class ); + forPatternMethod.invoke( aClass, dateFormat ); + return validDateFormat( dateFormat ); + } + catch ( InvocationTargetException e ) { + return invalidDateFormat( dateFormat, e.getCause() ); + } + catch ( Exception e ) { + return invalidDateFormat( dateFormat, e ); + } + } + } + + private static class SimpleDateFormatValidator implements DateFormatValidator { + + @Override + public DateFormatValidationResult validate(String dateFormat) { + try { + Class aClass = Class.forName( SimpleDateFormat.class.getCanonicalName() ); + aClass.getConstructor( String.class ).newInstance( dateFormat ); + return validDateFormat( dateFormat ); + } + catch ( InvocationTargetException e ) { + return invalidDateFormat( dateFormat, e.getCause() ); + } + catch ( Exception e ) { + return invalidDateFormat( dateFormat, e ); + } + } + } + + private static DateFormatValidationResult validDateFormat(String dateFormat) { + return new DateFormatValidationResult( + true, String.format( + "given date format \"%s\" is valid.", dateFormat ) ); + } + + private static DateFormatValidationResult invalidDateFormat(String dateFormat, Throwable e) { + return new DateFormatValidationResult( + false, String.format( + "given date format \"%s\" is invalid. Message: \"%s\"", dateFormat, e.getMessage() ) ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/DefaultConversionContext.java b/processor/src/main/java/org/mapstruct/ap/model/common/DefaultConversionContext.java old mode 100644 new mode 100755 index c47a38bf4..da036746e --- a/processor/src/main/java/org/mapstruct/ap/model/common/DefaultConversionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/model/common/DefaultConversionContext.java @@ -18,6 +18,11 @@ */ package org.mapstruct.ap.model.common; +import org.mapstruct.ap.util.Strings; + +import javax.annotation.processing.Messager; +import javax.tools.Diagnostic; + /** * Default implementation of the {@link ConversionContext} passed to conversion providers. * @@ -25,14 +30,34 @@ package org.mapstruct.ap.model.common; */ public class DefaultConversionContext implements ConversionContext { + private final Messager messager; + private final Type sourceType; private final Type targetType; - private final String format; + private final String dateFormat; private final TypeFactory typeFactory; - public DefaultConversionContext(TypeFactory typeFactory, Type targetType, String format) { + public DefaultConversionContext(TypeFactory typeFactory, Messager messager, Type sourceType, Type targetType, + String dateFormat) { this.typeFactory = typeFactory; + this.messager = messager; + this.sourceType = sourceType; this.targetType = targetType; - this.format = format; + this.dateFormat = dateFormat; + validateDateFormat(); + } + + /** + * Validate the dateFormat if it is not null + */ + private void validateDateFormat() { + if ( !Strings.isEmpty( dateFormat ) ) { + DateFormatValidator dateFormatValidator = DateFormatValidatorFactory.forTypes( sourceType, targetType ); + DateFormatValidationResult validationResult = dateFormatValidator.validate( dateFormat ); + + if ( !validationResult.isValid() ) { + messager.printMessage( Diagnostic.Kind.ERROR, validationResult.validationInformation() ); + } + } } @Override @@ -42,11 +67,15 @@ public class DefaultConversionContext implements ConversionContext { @Override public String getDateFormat() { - return format; + return dateFormat; } @Override public TypeFactory getTypeFactory() { return typeFactory; } + + protected Messager getMessager() { + return messager; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java old mode 100644 new mode 100755 index 706f39bc3..b1a20eaf8 --- a/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java @@ -246,7 +246,7 @@ public class MappingResolverImpl implements MappingResolver { } ConversionContext ctx = - new DefaultConversionContext( typeFactory, targetType, dateFormat ); + new DefaultConversionContext( typeFactory, messager, sourceType, targetType, dateFormat ); return conversionProvider.to( ctx ); } @@ -276,7 +276,9 @@ public class MappingResolverImpl implements MappingResolver { if ( matchingBuiltInMethod != null ) { virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod ) ); - ConversionContext ctx = new DefaultConversionContext( typeFactory, targetType, dateFormat ); + ConversionContext ctx = new DefaultConversionContext( typeFactory, messager, + sourceType, + targetType, dateFormat ); Assignment methodReference = AssignmentFactory.createMethodReference( matchingBuiltInMethod, ctx ); methodReference.setAssignment( AssignmentFactory.createDirect( sourceReference ) ); return methodReference; diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/JodaTimeConstants.java b/processor/src/main/java/org/mapstruct/ap/util/JodaTimeConstants.java old mode 100644 new mode 100755 similarity index 97% rename from processor/src/main/java/org/mapstruct/ap/conversion/JodaTimeConstants.java rename to processor/src/main/java/org/mapstruct/ap/util/JodaTimeConstants.java index f12eca96e..dbb3b254d --- a/processor/src/main/java/org/mapstruct/ap/conversion/JodaTimeConstants.java +++ b/processor/src/main/java/org/mapstruct/ap/util/JodaTimeConstants.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.conversion; +package org.mapstruct.ap.util; /** * Helper holding constants for working with Joda-Time. diff --git a/processor/src/test/java/org/mapstruct/ap/model/common/DateFormatValidatorFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/model/common/DateFormatValidatorFactoryTest.java new file mode 100755 index 000000000..540904ad6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/model/common/DateFormatValidatorFactoryTest.java @@ -0,0 +1,184 @@ +/** + * 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.model.common; + +import org.junit.Test; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.util.JavaTimeConstants; +import org.mapstruct.ap.util.JodaTimeConstants; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVisitor; +import java.lang.annotation.Annotation; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; + +/** + * Tests for {@link org.mapstruct.ap.model.common.DateFormatValidatorFactory}. + * + * @author Timo Eckhardt + */ +@IssueKey( "224" ) +public class DateFormatValidatorFactoryTest { + + private static final String JAVA_LANG_STRING = "java.lang.String"; + + private TypeMirror voidTypeMirror = new TypeMirror() { + + @Override + public List getAnnotationMirrors() { + return null; + } + + @Override + public A getAnnotation(Class annotationType) { + return null; + } + + @Override + public A[] getAnnotationsByType(Class annotationType) { + return null; + } + + @Override + public TypeKind getKind() { + return TypeKind.VOID; + } + + @Override + public R accept(TypeVisitor v, P p) { + return null; + } + }; + + @Test + public void testUnsupportedTypes() { + Type sourceType = typeWithFQN( JAVA_LANG_STRING ); + Type targetType = typeWithFQN( JAVA_LANG_STRING ); + DateFormatValidator dateFormatValidator = DateFormatValidatorFactory.forTypes( sourceType, targetType ); + assertThat( dateFormatValidator.validate( "XXXX" ).isValid() ).isTrue(); + } + + @Test + public void testJavaUtilDateValidator() { + + Type sourceType = typeWithFQN( "java.util.Date" ); + Type targetType = typeWithFQN( JAVA_LANG_STRING ); + + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + } + + @Test + public void testJodaTimeValidator() { + + Type targetType = typeWithFQN( JAVA_LANG_STRING ); + + Type sourceType = typeWithFQN( JodaTimeConstants.DATE_TIME_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + + sourceType = typeWithFQN( JodaTimeConstants.LOCAL_DATE_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + + sourceType = typeWithFQN( JodaTimeConstants.LOCAL_DATE_TIME_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + + sourceType = typeWithFQN( JodaTimeConstants.LOCAL_TIME_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + } + + @Test + public void testJavaTimeValidator() { + + Type targetType = typeWithFQN( JAVA_LANG_STRING ); + + Type sourceType = typeWithFQN( JavaTimeConstants.ZONED_DATE_TIME_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + + sourceType = typeWithFQN( JavaTimeConstants.LOCAL_DATE_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + + sourceType = typeWithFQN( JavaTimeConstants.LOCAL_DATE_TIME_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + + sourceType = typeWithFQN( JavaTimeConstants.LOCAL_TIME_FQN ); + assertInvalidDateFormat( sourceType, targetType ); + assertInvalidDateFormat( targetType, sourceType ); + assertValidDateFormat( sourceType, targetType ); + assertValidDateFormat( targetType, sourceType ); + } + + private void assertInvalidDateFormat(Type sourceType, Type targetType) { + DateFormatValidator dateFormatValidator = DateFormatValidatorFactory.forTypes( sourceType, targetType ); + DateFormatValidationResult result = dateFormatValidator.validate( "qwertz" ); + assertThat( result.isValid() ).isFalse(); + } + + private void assertValidDateFormat(Type sourceType, Type targetType) { + DateFormatValidator dateFormatValidator = DateFormatValidatorFactory.forTypes( sourceType, targetType ); + DateFormatValidationResult result = dateFormatValidator.validate( "YYYY" ); + assertThat( result.isValid() ).isTrue(); + } + + private Type typeWithFQN(String fullQualifiedName) { + return new Type( + null, + null, + voidTypeMirror, + null, + null, + null, + null, + null, + fullQualifiedName, + false, + false, + false, + false, + false, + false ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/model/common/DefaultConversionContextTest.java b/processor/src/test/java/org/mapstruct/ap/model/common/DefaultConversionContextTest.java new file mode 100755 index 000000000..2bef26c73 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/model/common/DefaultConversionContextTest.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.model.common; + +import org.junit.Test; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.util.JavaTimeConstants; + +import javax.annotation.processing.Messager; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVisitor; +import javax.tools.Diagnostic; +import java.lang.annotation.Annotation; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; + +/** + * Testing DefaultConversionContext for dateFormat + * + * @author Timo Eckhardt + */ +@IssueKey( "224" ) +public class DefaultConversionContextTest { + + private TypeMirror voidTypeMirror = new TypeMirror() { + + @Override + public List getAnnotationMirrors() { + return null; + } + + @Override + public A getAnnotation(Class annotationType) { + return null; + } + + @Override + public A[] getAnnotationsByType(Class annotationType) { + return null; + } + + @Override + public TypeKind getKind() { + return TypeKind.VOID; + } + + @Override + public R accept(TypeVisitor v, P p) { + return null; + } + }; + + @Test + public void testInvalidDateFormatValidation() { + Type type = typeWithFQN( JavaTimeConstants.ZONED_DATE_TIME_FQN ); + StatefulMessagerMock statefulMessagerMock = new StatefulMessagerMock(); + new DefaultConversionContext( + null, statefulMessagerMock, type, type, "qwertz" ); + assertThat( statefulMessagerMock.getLastKindPrinted() ).isEqualTo( Diagnostic.Kind.ERROR ); + } + + @Test + public void testNullDateFormatValidation() { + Type type = typeWithFQN( JavaTimeConstants.ZONED_DATE_TIME_FQN ); + StatefulMessagerMock statefulMessagerMock = new StatefulMessagerMock(); + new DefaultConversionContext( + null, statefulMessagerMock, type, type, null ); + assertThat( statefulMessagerMock.getLastKindPrinted() ).isNull(); + } + + @Test + public void testUnsupportedType() { + Type type = typeWithFQN( "java.lang.String" ); + StatefulMessagerMock statefulMessagerMock = new StatefulMessagerMock(); + new DefaultConversionContext( + null, statefulMessagerMock, type, type, "qwertz" ); + assertThat( statefulMessagerMock.getLastKindPrinted() ).isNull(); + } + + private Type typeWithFQN(String fullQualifiedName) { + return new Type( + null, + null, + voidTypeMirror, + null, + null, + null, + null, + null, + fullQualifiedName, + false, + false, + false, + false, + false, + false ); + } + + private static class StatefulMessagerMock implements Messager { + + private Diagnostic.Kind lastKindPrinted; + + @Override + public void printMessage(Diagnostic.Kind kind, CharSequence msg) { + lastKindPrinted = kind; + } + + @Override + public void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e) { + } + + @Override + public void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a) { + } + + @Override + public void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a, + AnnotationValue v) { + } + + public Diagnostic.Kind getLastKindPrinted() { + return lastKindPrinted; + } + + } +}