1 package com.fasterxml.jackson.databind.ser.std;
2
3 import java.io.IOException;
4 import java.lang.reflect.Type;
5 import java.text.DateFormat;
6 import java.text.SimpleDateFormat;
7 import java.util.Date;
8 import java.util.Locale;
9 import java.util.TimeZone;
10 import java.util.concurrent.atomic.AtomicReference;
11
12 import com.fasterxml.jackson.annotation.JsonFormat;
13
14 import com.fasterxml.jackson.core.JsonGenerator;
15 import com.fasterxml.jackson.core.JsonParser;
16
17 import com.fasterxml.jackson.databind.*;
18 import com.fasterxml.jackson.databind.jsonFormatVisitors.*;
19 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
20 import com.fasterxml.jackson.databind.util.StdDateFormat;
21
22 @SuppressWarnings("serial")
23 public abstract class DateTimeSerializerBase<T>
24 extends StdScalarSerializer<T>
25 implements ContextualSerializer
26 {
27
31 protected final Boolean _useTimestamp;
32
33
38 protected final DateFormat _customFormat;
39
40
48 protected final AtomicReference<DateFormat> _reusedCustomFormat;
49
50 protected DateTimeSerializerBase(Class<T> type,
51 Boolean useTimestamp, DateFormat customFormat)
52 {
53 super(type);
54 _useTimestamp = useTimestamp;
55 _customFormat = customFormat;
56 _reusedCustomFormat = (customFormat == null) ? null : new AtomicReference<DateFormat>();
57 }
58
59 public abstract DateTimeSerializerBase<T> withFormat(Boolean timestamp, DateFormat customFormat);
60
61 @Override
62 public JsonSerializer<?> createContextual(SerializerProvider serializers,
63 BeanProperty property) throws JsonMappingException
64 {
65
66
67 JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
68 if (format == null) {
69 return this;
70 }
71
72 JsonFormat.Shape shape = format.getShape();
73 if (shape.isNumeric()) {
74 return withFormat(Boolean.TRUE, null);
75 }
76
77
78
79 if (format.hasPattern()) {
80 final Locale loc = format.hasLocale()
81 ? format.getLocale()
82 : serializers.getLocale();
83 SimpleDateFormat df = new SimpleDateFormat(format.getPattern(), loc);
84 TimeZone tz = format.hasTimeZone() ? format.getTimeZone()
85 : serializers.getTimeZone();
86 df.setTimeZone(tz);
87 return withFormat(Boolean.FALSE, df);
88 }
89
90
91 final boolean hasLocale = format.hasLocale();
92 final boolean hasTZ = format.hasTimeZone();
93 final boolean asString = (shape == JsonFormat.Shape.STRING);
94
95 if (!hasLocale && !hasTZ && !asString) {
96 return this;
97 }
98
99 DateFormat df0 = serializers.getConfig().getDateFormat();
100
101 if (df0 instanceof StdDateFormat) {
102 StdDateFormat std = (StdDateFormat) df0;
103 if (format.hasLocale()) {
104 std = std.withLocale(format.getLocale());
105 }
106 if (format.hasTimeZone()) {
107 std = std.withTimeZone(format.getTimeZone());
108 }
109 return withFormat(Boolean.FALSE, std);
110 }
111
112
113
114
115 if (!(df0 instanceof SimpleDateFormat)) {
116 serializers.reportBadDefinition(handledType(), String.format(
117 "Configured `DateFormat` (%s) not a `SimpleDateFormat`; cannot configure `Locale` or `TimeZone`",
118 df0.getClass().getName()));
119 }
120 SimpleDateFormat df = (SimpleDateFormat) df0;
121 if (hasLocale) {
122
123 df = new SimpleDateFormat(df.toPattern(), format.getLocale());
124 } else {
125 df = (SimpleDateFormat) df.clone();
126 }
127 TimeZone newTz = format.getTimeZone();
128 boolean changeTZ = (newTz != null) && !newTz.equals(df.getTimeZone());
129 if (changeTZ) {
130 df.setTimeZone(newTz);
131 }
132 return withFormat(Boolean.FALSE, df);
133 }
134
135
140
141 @Override
142 public boolean isEmpty(SerializerProvider serializers, T value) {
143
144
145
146 return false;
147 }
148
149 protected abstract long _timestamp(T value);
150
151 @Override
152 public JsonNode getSchema(SerializerProvider serializers, Type typeHint) {
153
154 return createSchemaNode(_asTimestamp(serializers) ? "number" : "string", true);
155 }
156
157 @Override
158 public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
159 {
160 _acceptJsonFormatVisitor(visitor, typeHint, _asTimestamp(visitor.getProvider()));
161 }
162
163
168
169 @Override
170 public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers)
171 throws IOException;
172
173
178
179 protected boolean _asTimestamp(SerializerProvider serializers)
180 {
181 if (_useTimestamp != null) {
182 return _useTimestamp.booleanValue();
183 }
184 if (_customFormat == null) {
185 if (serializers != null) {
186 return serializers.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
187 }
188
189 throw new IllegalArgumentException("Null SerializerProvider passed for "+handledType().getName());
190 }
191 return false;
192 }
193
194 protected void _acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint,
195 boolean asNumber) throws JsonMappingException
196 {
197 if (asNumber) {
198 visitIntFormat(visitor, typeHint,
199 JsonParser.NumberType.LONG, JsonValueFormat.UTC_MILLISEC);
200 } else {
201 visitStringFormat(visitor, typeHint, JsonValueFormat.DATE_TIME);
202 }
203 }
204
205
208 protected void _serializeAsString(Date value, JsonGenerator g, SerializerProvider provider) throws IOException
209 {
210 if (_customFormat == null) {
211 provider.defaultSerializeDateValue(value, g);
212 return;
213 }
214
215
216
217
218
219
220
221
222 DateFormat f = _reusedCustomFormat.getAndSet(null);
223 if (f == null) {
224 f = (DateFormat) _customFormat.clone();
225 }
226 g.writeString(f.format(value));
227 _reusedCustomFormat.compareAndSet(null, f);
228 }
229 }
230