001/*
002 * Copyright 2002-2017 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.util.StringUtils;
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 */
046public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport
047                implements AnnotationFormatterFactory<DateTimeFormat> {
048
049        private static final Set<Class<?>> FIELD_TYPES;
050
051        static {
052                // Create the set of field types that may be annotated with @DateTimeFormat.
053                Set<Class<?>> fieldTypes = new HashSet<>(8);
054                fieldTypes.add(LocalDate.class);
055                fieldTypes.add(LocalTime.class);
056                fieldTypes.add(LocalDateTime.class);
057                fieldTypes.add(ZonedDateTime.class);
058                fieldTypes.add(OffsetDateTime.class);
059                fieldTypes.add(OffsetTime.class);
060                FIELD_TYPES = Collections.unmodifiableSet(fieldTypes);
061        }
062
063
064        @Override
065        public final Set<Class<?>> getFieldTypes() {
066                return FIELD_TYPES;
067        }
068
069        @Override
070        public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
071                DateTimeFormatter formatter = getFormatter(annotation, fieldType);
072
073                // Efficient ISO_LOCAL_* variants for printing since they are twice as fast...
074                if (formatter == DateTimeFormatter.ISO_DATE) {
075                        if (isLocal(fieldType)) {
076                                formatter = DateTimeFormatter.ISO_LOCAL_DATE;
077                        }
078                }
079                else if (formatter == DateTimeFormatter.ISO_TIME) {
080                        if (isLocal(fieldType)) {
081                                formatter = DateTimeFormatter.ISO_LOCAL_TIME;
082                        }
083                }
084                else if (formatter == DateTimeFormatter.ISO_DATE_TIME) {
085                        if (isLocal(fieldType)) {
086                                formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
087                        }
088                }
089
090                return new TemporalAccessorPrinter(formatter);
091        }
092
093        @Override
094        @SuppressWarnings("unchecked")
095        public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
096                DateTimeFormatter formatter = getFormatter(annotation, fieldType);
097                return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType, formatter);
098        }
099
100        /**
101         * Factory method used to create a {@link DateTimeFormatter}.
102         * @param annotation the format annotation for the field
103         * @param fieldType the declared type of the field
104         * @return a {@link DateTimeFormatter} instance
105         */
106        protected DateTimeFormatter getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
107                DateTimeFormatterFactory factory = new DateTimeFormatterFactory();
108                String style = resolveEmbeddedValue(annotation.style());
109                if (StringUtils.hasLength(style)) {
110                        factory.setStylePattern(style);
111                }
112                factory.setIso(annotation.iso());
113                String pattern = resolveEmbeddedValue(annotation.pattern());
114                if (StringUtils.hasLength(pattern)) {
115                        factory.setPattern(pattern);
116                }
117                return factory.createDateTimeFormatter();
118        }
119
120        private boolean isLocal(Class<?> fieldType) {
121                return fieldType.getSimpleName().startsWith("Local");
122        }
123
124}