001/*
002 * Copyright 2002-2020 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.support;
018
019import org.springframework.core.convert.support.DefaultConversionService;
020import org.springframework.format.FormatterRegistry;
021import org.springframework.format.datetime.DateFormatterRegistrar;
022import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
023import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
024import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
025import org.springframework.format.number.money.CurrencyUnitFormatter;
026import org.springframework.format.number.money.Jsr354NumberFormatAnnotationFormatterFactory;
027import org.springframework.format.number.money.MonetaryAmountFormatter;
028import org.springframework.lang.Nullable;
029import org.springframework.util.ClassUtils;
030import org.springframework.util.StringValueResolver;
031
032/**
033 * A specialization of {@link FormattingConversionService} configured by default with
034 * converters and formatters appropriate for most applications.
035 *
036 * <p>Designed for direct instantiation but also exposes the static {@link #addDefaultFormatters}
037 * utility method for ad hoc use against any {@code FormatterRegistry} instance, just
038 * as {@code DefaultConversionService} exposes its own
039 * {@link DefaultConversionService#addDefaultConverters addDefaultConverters} method.
040 *
041 * <p>Automatically registers formatters for JSR-354 Money & Currency, JSR-310 Date-Time
042 * and/or Joda-Time 2.x, depending on the presence of the corresponding API on the classpath.
043 *
044 * @author Chris Beams
045 * @author Juergen Hoeller
046 * @since 3.1
047 */
048public class DefaultFormattingConversionService extends FormattingConversionService {
049
050        private static final boolean jsr354Present;
051
052        private static final boolean jodaTimePresent;
053
054        static {
055                ClassLoader classLoader = DefaultFormattingConversionService.class.getClassLoader();
056                jsr354Present = ClassUtils.isPresent("javax.money.MonetaryAmount", classLoader);
057                jodaTimePresent = ClassUtils.isPresent("org.joda.time.YearMonth", classLoader);
058        }
059
060
061        /**
062         * Create a new {@code DefaultFormattingConversionService} with the set of
063         * {@linkplain DefaultConversionService#addDefaultConverters default converters} and
064         * {@linkplain #addDefaultFormatters default formatters}.
065         */
066        public DefaultFormattingConversionService() {
067                this(null, true);
068        }
069
070        /**
071         * Create a new {@code DefaultFormattingConversionService} with the set of
072         * {@linkplain DefaultConversionService#addDefaultConverters default converters} and,
073         * based on the value of {@code registerDefaultFormatters}, the set of
074         * {@linkplain #addDefaultFormatters default formatters}.
075         * @param registerDefaultFormatters whether to register default formatters
076         */
077        public DefaultFormattingConversionService(boolean registerDefaultFormatters) {
078                this(null, registerDefaultFormatters);
079        }
080
081        /**
082         * Create a new {@code DefaultFormattingConversionService} with the set of
083         * {@linkplain DefaultConversionService#addDefaultConverters default converters} and,
084         * based on the value of {@code registerDefaultFormatters}, the set of
085         * {@linkplain #addDefaultFormatters default formatters}.
086         * @param embeddedValueResolver delegated to {@link #setEmbeddedValueResolver(StringValueResolver)}
087         * prior to calling {@link #addDefaultFormatters}.
088         * @param registerDefaultFormatters whether to register default formatters
089         */
090        public DefaultFormattingConversionService(
091                        @Nullable StringValueResolver embeddedValueResolver, boolean registerDefaultFormatters) {
092
093                if (embeddedValueResolver != null) {
094                        setEmbeddedValueResolver(embeddedValueResolver);
095                }
096                DefaultConversionService.addDefaultConverters(this);
097                if (registerDefaultFormatters) {
098                        addDefaultFormatters(this);
099                }
100        }
101
102
103        /**
104         * Add formatters appropriate for most environments: including number formatters,
105         * JSR-354 Money & Currency formatters, JSR-310 Date-Time and/or Joda-Time formatters,
106         * depending on the presence of the corresponding API on the classpath.
107         * @param formatterRegistry the service to register default formatters with
108         */
109        public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
110                // Default handling of number values
111                formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
112
113                // Default handling of monetary values
114                if (jsr354Present) {
115                        formatterRegistry.addFormatter(new CurrencyUnitFormatter());
116                        formatterRegistry.addFormatter(new MonetaryAmountFormatter());
117                        formatterRegistry.addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
118                }
119
120                // Default handling of date-time values
121
122                // just handling JSR-310 specific date and time types
123                new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);
124
125                if (jodaTimePresent) {
126                        // handles Joda-specific types as well as Date, Calendar, Long
127                        new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
128                }
129                else {
130                        // regular DateFormat-based Date, Calendar, Long converters
131                        new DateFormatterRegistrar().registerFormatters(formatterRegistry);
132                }
133        }
134
135}