001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.format.datetime.standard;
018
019import java.time.LocalDate;
020import java.time.LocalDateTime;
021import java.time.LocalTime;
022import java.time.OffsetDateTime;
023import java.time.OffsetTime;
024import java.time.ZonedDateTime;
025import java.time.format.DateTimeFormatter;
026import java.time.temporal.TemporalAccessor;
027import java.util.Collections;
028import java.util.HashSet;
029import java.util.Set;
030
031import org.springframework.context.support.EmbeddedValueResolutionSupport;
032import org.springframework.format.AnnotationFormatterFactory;
033import org.springframework.format.Parser;
034import org.springframework.format.Printer;
035import org.springframework.format.annotation.DateTimeFormat;
036import org.springframework.lang.UsesJava8;
037
038/**
039 * Formats fields annotated with the {@link DateTimeFormat} annotation using the
040 * JSR-310 <code>java.time</code> package in JDK 8.
041 *
042 * @author Juergen Hoeller
043 * @since 4.0
044 * @see org.springframework.format.annotation.DateTimeFormat
045 */
046@UsesJava8
047public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport
048                implements AnnotationFormatterFactory<DateTimeFormat> {
049
050        private static final Set<Class<?>> FIELD_TYPES;
051
052        static {
053                // Create the set of field types that may be annotated with @DateTimeFormat.
054                Set<Class<?>> fieldTypes = new HashSet<Class<?>>(8);
055                fieldTypes.add(LocalDate.class);
056                fieldTypes.add(LocalTime.class);
057                fieldTypes.add(LocalDateTime.class);
058                fieldTypes.add(ZonedDateTime.class);
059                fieldTypes.add(OffsetDateTime.class);
060                fieldTypes.add(OffsetTime.class);
061                FIELD_TYPES = Collections.unmodifiableSet(fieldTypes);
062        }
063
064
065        @Override
066        public final Set<Class<?>> getFieldTypes() {
067                return FIELD_TYPES;
068        }
069
070        @Override
071        public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
072                DateTimeFormatter formatter = getFormatter(annotation, fieldType);
073
074                // Efficient ISO_LOCAL_* variants for printing since they are twice as fast...
075                if (formatter == DateTimeFormatter.ISO_DATE) {
076                        if (isLocal(fieldType)) {
077                                formatter = DateTimeFormatter.ISO_LOCAL_DATE;
078                        }
079                }
080                else if (formatter == DateTimeFormatter.ISO_TIME) {
081                        if (isLocal(fieldType)) {
082                                formatter = DateTimeFormatter.ISO_LOCAL_TIME;
083                        }
084                }
085                else if (formatter == DateTimeFormatter.ISO_DATE_TIME) {
086                        if (isLocal(fieldType)) {
087                                formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
088                        }
089                }
090
091                return new TemporalAccessorPrinter(formatter);
092        }
093
094        @Override
095        @SuppressWarnings("unchecked")
096        public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
097                DateTimeFormatter formatter = getFormatter(annotation, fieldType);
098                return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType, formatter);
099        }
100
101        /**
102         * Factory method used to create a {@link DateTimeFormatter}.
103         * @param annotation the format annotation for the field
104         * @param fieldType the declared type of the field
105         * @return a {@link DateTimeFormatter} instance
106         */
107        protected DateTimeFormatter getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
108                DateTimeFormatterFactory factory = new DateTimeFormatterFactory();
109                factory.setStylePattern(resolveEmbeddedValue(annotation.style()));
110                factory.setIso(annotation.iso());
111                factory.setPattern(resolveEmbeddedValue(annotation.pattern()));
112                return factory.createDateTimeFormatter();
113        }
114
115        private boolean isLocal(Class<?> fieldType) {
116                return fieldType.getSimpleName().startsWith("Local");
117        }
118
119}