/**
* 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.common;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import org.mapstruct.ap.internal.util.JavaTimeConstants;
import org.mapstruct.ap.internal.util.JodaTimeConstants;
import org.mapstruct.ap.internal.util.Message;
/**
* Factory for {@link DateFormatValidator}. <p> Based on the types of source / target type a specific {@link
* DateFormatValidator} will be instantiated. <br /> <ul> <li>Joda Time</li> <li>Java 8 Time and</li>
* <li>java.util.Date</li> </ul> are supported. </p>
*/
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, Message.GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK,
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, Message.GENERAL_VALID_DATE, dateFormat );
}
private static DateFormatValidationResult invalidDateFormat(String dateFormat, Throwable e) {
return new DateFormatValidationResult( false, Message.GENERAL_INVALID_DATE, dateFormat, e.getMessage() );
}
}