1 /*
2  * Copyright 2002-2011 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.format.support;
18 
19 import java.util.Calendar;
20 import java.util.Collections;
21 import java.util.Date;
22 import java.util.HashSet;
23 import java.util.Set;
24 
25 import org.springframework.core.convert.support.DefaultConversionService;
26 import org.springframework.format.AnnotationFormatterFactory;
27 import org.springframework.format.FormatterRegistry;
28 import org.springframework.format.Parser;
29 import org.springframework.format.Printer;
30 import org.springframework.format.annotation.DateTimeFormat;
31 import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
32 import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
33 import org.springframework.util.ClassUtils;
34 import org.springframework.util.StringValueResolver;
35 
36 /**
37  * A specialization of {@link FormattingConversionService} configured by default with
38  * converters and formatters appropriate for most applications.
39  *
40  * <p>Designed for direct instantiation but also exposes the static {@link #addDefaultFormatters}
41  * utility method for ad hoc use against any {@code FormatterRegistry} instance, just
42  * as {@code DefaultConversionService} exposes its own
43  * {@link DefaultConversionService#addDefaultConverters addDefaultConverters} method.
44  *
45  * @author Chris Beams
46  * @since 3.1
47  */
48 public class DefaultFormattingConversionService extends FormattingConversionService {
49 
50 	private static final boolean jodaTimePresent = ClassUtils.isPresent(
51 			"org.joda.time.LocalDate", DefaultFormattingConversionService.class.getClassLoader());
52 
53 	/**
54 	 * Create a new {@code DefaultFormattingConversionService} with the set of
55 	 * {@linkplain DefaultConversionService#addDefaultConverters default converters} and
56 	 * {@linkplain #addDefaultFormatters default formatters}.
57 	 */
DefaultFormattingConversionService()58 	public DefaultFormattingConversionService() {
59 		this(null, true);
60 	}
61 
62 	/**
63 	 * Create a new {@code DefaultFormattingConversionService} with the set of
64 	 * {@linkplain DefaultConversionService#addDefaultConverters default converters} and,
65 	 * based on the value of {@code registerDefaultFormatters}, the set of
66 	 * {@linkplain #addDefaultFormatters default formatters}.
67 	 * @param registerDefaultFormatters whether to register default formatters
68 	 */
DefaultFormattingConversionService(boolean registerDefaultFormatters)69 	public DefaultFormattingConversionService(boolean registerDefaultFormatters) {
70 		this(null, registerDefaultFormatters);
71 	}
72 
73 	/**
74 	 * Create a new {@code DefaultFormattingConversionService} with the set of
75 	 * {@linkplain DefaultConversionService#addDefaultConverters default converters} and,
76 	 * based on the value of {@code registerDefaultFormatters}, the set of
77 	 * {@linkplain #addDefaultFormatters default formatters}
78 	 * @param embeddedValueResolver delegated to {@link #setEmbeddedValueResolver(StringValueResolver)}
79 	 * prior to calling {@link #addDefaultFormatters}.
80 	 * @param registerDefaultFormatters whether to register default formatters
81 	 */
DefaultFormattingConversionService(StringValueResolver embeddedValueResolver, boolean registerDefaultFormatters)82 	public DefaultFormattingConversionService(StringValueResolver embeddedValueResolver, boolean registerDefaultFormatters) {
83 		this.setEmbeddedValueResolver(embeddedValueResolver);
84 		DefaultConversionService.addDefaultConverters(this);
85 		if (registerDefaultFormatters) {
86 			addDefaultFormatters(this);
87 		}
88 	}
89 
90 	/**
91 	 * Add formatters appropriate for most environments, including number formatters and a Joda-Time
92 	 * date formatter if Joda-Time is present on the classpath.
93 	 * @param formatterRegistry the service to register default formatters against
94 	 */
addDefaultFormatters(FormatterRegistry formatterRegistry)95 	public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
96 		formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
97 		if (jodaTimePresent) {
98 			new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
99 		} else {
100 			formatterRegistry.addFormatterForFieldAnnotation(new NoJodaDateTimeFormatAnnotationFormatterFactory());
101 		}
102 	}
103 
104 
105 	/**
106 	 * Dummy AnnotationFormatterFactory that simply fails if @DateTimeFormat is being used
107 	 * without the JodaTime library being present.
108 	 */
109 	private static final class NoJodaDateTimeFormatAnnotationFormatterFactory
110 			implements AnnotationFormatterFactory<DateTimeFormat> {
111 
112 		private final Set<Class<?>> fieldTypes;
113 
NoJodaDateTimeFormatAnnotationFormatterFactory()114 		public NoJodaDateTimeFormatAnnotationFormatterFactory() {
115 			Set<Class<?>> rawFieldTypes = new HashSet<Class<?>>(4);
116 			rawFieldTypes.add(Date.class);
117 			rawFieldTypes.add(Calendar.class);
118 			rawFieldTypes.add(Long.class);
119 			this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes);
120 		}
121 
getFieldTypes()122 		public Set<Class<?>> getFieldTypes() {
123 			return this.fieldTypes;
124 		}
125 
getPrinter(DateTimeFormat annotation, Class<?> fieldType)126 		public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
127 			throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
128 		}
129 
getParser(DateTimeFormat annotation, Class<?> fieldType)130 		public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
131 			throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
132 		}
133 	}
134 
135 }
136